micropython

docs/optimisations.txt

440:cf329206154e
2011-07-03 Paul Boddie Introduced constant usage tracking.
     1 Optimisations
     2 =============
     3 
     4 Some optimisations around constant objects might be possible; these depend on
     5 the following:
     6 
     7   * Reliable tracking of assignments: where assignment operations occur, the
     8     target of the assignment should be determined if any hope of optimisation
     9     is to be maintained. Where no guarantees can be made about the target of
    10     an assignment, no assignment-related information should be written to
    11     potential targets.
    12 
    13   * Objects acting as "containers" of attributes must be regarded as "safe":
    14     where assignments are recorded as occurring on an attribute, it must be
    15     guaranteed that no other unforeseen ways exist to assign to such
    16     attributes.
    17 
    18 The discussion below presents certain rules which must be imposed to uphold
    19 the above requirements.
    20 
    21 Safe Containers
    22 ---------------
    23 
    24 Where attributes of modules, classes and instances are only set once and are
    25 effectively constant, it should be possible to circumvent the attribute lookup
    26 mechanism and to directly reference the attribute value. This technique may
    27 only be considered applicable for the following "container" objects, subject
    28 to the noted constraints:
    29 
    30  1. For modules, "safety" is enforced by ensuring that assignments to module
    31     attributes are only permitted within the module itself either at the
    32     top-level or via names declared as globals. Thus, the following would not
    33     be permitted:
    34 
    35     another_module.this_module.attr = value
    36 
    37     In the above, this_module is a reference to the current module.
    38 
    39  2. For classes, "safety" is enforced by ensuring that assignments to class
    40     attributes are only permitted within the class definition, outside
    41     methods. This would mean that classes would be "sealed" at definition time
    42     (like functions).
    43 
    44 Unlike the property of function locals that they may only sensibly be accessed
    45 within the function in which they reside, these cases demand additional
    46 controls or assumptions on or about access to the stored data. Meanwhile, it
    47 would be difficult to detect eligible attributes on arbitrary instances due to
    48 the need for some kind of type inference or abstract execution.
    49 
    50 Constant Attributes
    51 -------------------
    52 
    53 When accessed via "safe containers", as described above, any attribute with
    54 only one recorded assignment on it can be considered a constant attribute and
    55 this eligible for optimisation, the consequence of which would be the
    56 replacement of a LoadAttrIndex instruction (which needs to look up an
    57 attribute using the run-time details of the "container" and the compile-time
    58 details of the attribute) with a LoadAttr instruction.
    59 
    60 However, some restrictions exist on assignment operations which may be
    61 regarded to cause only one assignment in the lifetime of a program:
    62 
    63  1. For module attributes, only assignments at the top-level outside loop
    64     statements can be reliably assumed to cause only a single assignment.
    65 
    66  2. For class attributes, only assignments at the top-level within class
    67     definitions and outside loop statements can be reliably assumed to cause
    68     only a single assignment.
    69 
    70 All assignments satisfying the "safe container" requirements, but not the
    71 requirements immediately above, should each be recorded as causing at least
    72 one assignment.
    73 
    74 Additional Controls
    75 -------------------
    76 
    77 For the above cases for "container" objects, the following controls would need
    78 to apply:
    79 
    80  1. Modules would need to be immutable after initialisation. However, during
    81     initialisation, there remains a possibility of another module attempting
    82     to access the original module. For example, if ppp/__init__.py contained
    83     the following...
    84 
    85     x = 1
    86     import ppp.qqq
    87     print x
    88 
    89     ...and if ppp/qqq.py contained the following...
    90 
    91     import ppp
    92     ppp.x = 2
    93 
    94     ...then the value 2 would be printed. Since modules are objects which are
    95     registered globally in a program, it would be possible to set attributes
    96     in the above way.
    97 
    98  2. Classes would need to be immutable after initialisation. However, since
    99     classes are objects, any reference to a class after initialisation could
   100     be used to set attributes on the class.
   101 
   102 Solutions:
   103 
   104  1. Insist on global scope for module attribute assignments.
   105 
   106  2. Insist on local scope within classes.
   107 
   108 Both of the above measures need to be enforced at run-time, since an arbitrary
   109 attribute assignment could be attempted on any kind of object, yet to uphold
   110 the properties of "safe containers", attempts to change attributes of such
   111 objects should be denied. Since foreseen attribute assignment operations have
   112 certain properties detectable at compile-time, it could be appropriate to
   113 generate special instructions (or modified instructions) during the
   114 initialisation of modules and classes for such foreseen assignments, whilst
   115 employing normal attribute assignment operations in all other cases. Indeed,
   116 the StoreAttr instruction, which is used to set attributes in "safe
   117 containers" would be used exclusively for this purpose; the StoreAttrIndex
   118 instruction would be used exclusively for all other attribute assignments.
   119 
   120 To ensure the "sealing" of modules and classes, entries in the attribute
   121 lookup table would encode whether a class or module is being accessed, so
   122 that the StoreAttrIndex instruction could reject such accesses.
   123 
   124 Constant Attribute Values
   125 -------------------------
   126 
   127 Where an attribute value is itself regarded as constant, is a "safe container"
   128 and is used in an operation accessing its own attributes, the value can be
   129 directly inspected for optimisations or employed in the generated code. For
   130 the attribute values themselves, only objects of a constant nature may be
   131 considered suitable for this particular optimisation:
   132 
   133   * Classes
   134   * Modules
   135   * Instances defined as constant literals
   136 
   137 This is because arbitrary objects (such as most instances) have no
   138 well-defined form before run-time and cannot be investigated further at
   139 compile-time or have a representation inserted into the generated code.
   140 
   141 Class Attributes and Access via Instances
   142 -----------------------------------------
   143 
   144 Unlike module attributes, class attributes can be accessed in a number of
   145 different ways:
   146 
   147   * Using the class itself:
   148 
   149     C.x = 123
   150     cls = C; cls.x = 234
   151 
   152   * Using a subclass of the class (for reading attributes):
   153 
   154     class D(C):
   155         pass
   156     D.x # setting D.x would populate D, not C
   157 
   158   * Using instances of the class or a subclass of the class (for reading
   159     attributes):
   160 
   161     c = C()
   162     c.x # setting c.x would populate c, not C
   163 
   164 Since assignments are only achieved using direct references to the class, and
   165 since class attributes should be defined only within the class initialisation
   166 process, the properties of class attributes should be consistent with those
   167 desired.
   168 
   169 Method Access via Instances
   170 ---------------------------
   171 
   172 It is desirable to optimise method access, even though most method calls are
   173 likely to occur via instances. It is possible, given the properties of methods
   174 as class attributes to investigate the kind of instance that the self
   175 parameter/local refers to within each method: it should be an instance either
   176 of the class in which the method is defined or a compatible class, although
   177 situations exist where this might not be the case:
   178 
   179   * Explicit invocation of a method:
   180 
   181     d = D() # D is not related to C
   182     C.f(d) # calling f(self) in C
   183 
   184 If blatant usage of incompatible instances were somehow disallowed, it would
   185 still be necessary to investigate the properties of an instance's class and
   186 its relationship with other classes. Consider the following example:
   187 
   188     class A:
   189         def f(self): ...
   190 
   191     class B:
   192         def f(self): ...
   193         def g(self):
   194             self.f()
   195 
   196     class C(A, B):
   197         pass
   198 
   199 Here, instances of B passed into the method B.g could be assumed to provide
   200 access to B.f when self.f is resolved at compile-time. However, instances of C
   201 passed into B.g would instead provide access to A.f when self.f is resolved at
   202 compile-time (since the method resolution order is C, A, B instead of just B).
   203 
   204 One solution might be to specialise methods for each instance type, but this
   205 could be costly. Another less ambitious solution might only involve the
   206 optimisation of such internal method calls if an unambiguous target can be
   207 resolved.
   208 
   209 Narrowing Type Candidates using Attribute Names
   210 -----------------------------------------------
   211 
   212 Within functions, it might be useful to record attribute accesses on local
   213 names and to collect the names in order to help resolve targets in the
   214 generated code. For example:
   215 
   216     def f(x, y):
   217         x.method(y)
   218         if x.something:
   219             ...
   220         return x.attr
   221 
   222 Here, the object referenced via x should have "method", "something" and "attr"
   223 as attributes. We could impose a test for a type providing these attributes at
   224 the earliest opportunity and then specialise the attribute accesses, perhaps
   225 also invocations, in the generated code. AttributeError occurrences would need
   226 to be considered, however, potentially disqualifying certain attributes from
   227 any optimisations, and control flow would also need to be considered. The
   228 result would resemble the following:
   229 
   230     def f(x, y):
   231         if not isinstance(x, C) and x is not C:
   232             raise TypeError
   233         x.method(y)
   234         if x.something:
   235             ...
   236         return x.attr
   237 
   238 With more specific information about the attributes used with a given name,
   239 combinations of attributes can be provided instead of single attributes when
   240 recording attribute usage. For example, from the above:
   241 
   242     x uses method    -> indicates potential usage of C.method, D1.method, ...
   243     x uses something -> indicates potential usage of C.something,
   244                         D2.something, ...
   245     x uses attr      -> indicates potential usage of C.attr, D3.attr, ...
   246 
   247 These unspecific declarations of attribute usage can be replaced with the
   248 following:
   249 
   250     x uses method, something, attr -> indicates potential usage of C, D4, ...
   251                                       (each providing all of the stated
   252                                       attributes)
   253 
   254 Reducing Object Table Scope
   255 ---------------------------
   256 
   257 Where attributes may be used in a program but never accessed via the object
   258 table-dependent instructions, such attributes could be omitted from the object
   259 table.
   260 
   261 Implemented Optimisation Types
   262 ==============================
   263 
   264 Optimisation                    Prerequisites and Effect
   265 ------------                    ------------------------
   266 
   267 constant_storage                value instruction references a constant;
   268 (guidance)                      storage instruction references a constant;
   269                               | indicate whether both instructions satisfy the
   270                               | preconditions and should be removed (although
   271                               | this currently involves just a single merged
   272                               | instruction)
   273 
   274 constant_accessor               value instruction references a constant;
   275 (guidance)                    | target name provided (for use in producing an
   276                               | address access instruction)
   277 
   278 known_target                    value instruction references a constant;
   279 (guidance)                    | target and context are provided (no instructions
   280                               | are removed)
   281 
   282 self_access                     value instruction references "self" in a method;
   283 (guidance)                      specified attribute name always has the same
   284                                 position;
   285                               | indicate whether an appropriate instruction can
   286                               | be generated for the access
   287 
   288 temp_storage                    value instruction is a simple input operation;
   289 (elimination)                   value instruction is the last instruction;
   290 (guidance)                    | remove the value instruction, provide the value
   291                               | instruction in place of a temporary storage
   292                               | reference
   293 
   294 load_operations                 value instruction is a simple input operation;
   295 (merge)                         value instruction is the last instruction;
   296                                 current instruction uses simple input;
   297                               | remove the value instruction, make the value
   298                               | instruction the input to the current instruction
   299 
   300 no_operations                   input to the current instruction loads from the
   301 (guidance)                      destination of the current instruction;
   302                               | indicate that the current instruction should be
   303                               | omitted
   304 
   305 unused_results                  value instruction is a simple input operation;
   306 (elimination)                   value instruction is the final instruction of a
   307                                 discarded expression;
   308                               | remove the value instruction
   309 
   310 unused_objects                  attribute is defined using a name which is not
   311 (inspection)                    used in an active region of the program or is
   312                                 defined within a class which is not used by
   313                                 the program, object is unambiguously
   314                                 referenced by such attributes
   315                               | remove such attributes and objects