# HG changeset patch # User Paul Boddie # Date 1206917643 -7200 # Node ID eb3c1ed9dbee841eea24d62f7889f5848be002fa # Parent e704da427db4762876a831288f9e99ee01ac4d5f Introduced support for different levels of optimisations, tidying up the tests and actions for each optimisation. Added some support for default parameter values, with actual frame filling yet to be done. Added a test of default parameter values. Added more to the rationale. diff -r e704da427db4 -r eb3c1ed9dbee docs/rationale.txt --- a/docs/rationale.txt Mon Mar 24 23:53:18 2008 +0100 +++ b/docs/rationale.txt Mon Mar 31 00:54:03 2008 +0200 @@ -25,9 +25,29 @@ * Potentially lots of code * Lots of executed instructions +Improving attribute access performance + + * Achieve faster access using a different representation + * Must define attributes of objects in advance + * Restriction: modules, classes, instances are "closed" + * Evaluate the limitations: are they too disruptive? + +Consequences of revised attribute access + + * Cannot extend the range of attributes on objects + * Further optimisations: + * Restriction: attempt to control modification of attributes + * Result: further optimisation of accesses + +Invocations + + * Target checking + * Number of arguments vs. number of parameters + * Keyword parameter resolution + * Defaults + Other costly operations - * Keyword parameter resolution * Binary operators * Comparisons diff -r e704da427db4 -r eb3c1ed9dbee micropython/__init__.py --- a/micropython/__init__.py Mon Mar 24 23:53:18 2008 +0100 +++ b/micropython/__init__.py Mon Mar 31 00:54:03 2008 +0200 @@ -86,7 +86,7 @@ return self.modules.values() - def get_image(self, with_builtins=0): + def get_image(self, with_builtins=0, optimisations=None): "Return a dictionary mapping modules to structures." @@ -115,7 +115,7 @@ # Position the module in the image and make a translation. module.location = pos - trans = micropython.ast.Translation(module, objtable, paramtable, self.modules.get("__builtins__")) + trans = micropython.ast.Translation(module, objtable, paramtable, self.modules.get("__builtins__"), optimisations) # Add header details. diff -r e704da427db4 -r eb3c1ed9dbee micropython/ast.py --- a/micropython/ast.py Mon Mar 24 23:53:18 2008 +0100 +++ b/micropython/ast.py Mon Mar 31 00:54:03 2008 +0200 @@ -48,7 +48,9 @@ "A translated module." - def __init__(self, module, objtable, paramtable, builtins=None): + supported_optimisations = ["constant_storage", "known_target"] + + def __init__(self, module, objtable, paramtable, builtins=None, optimisations=None): """ Initialise the translation with an inspected 'module' and an attribute @@ -62,6 +64,10 @@ self.paramtable = paramtable self.builtins = builtins + # Desired optimisations. + + self.optimisations = set(optimisations or []) + # The current unit being translated. self.unit = None @@ -205,7 +211,9 @@ # Where the last operation (defining the attribute owner) yields a # constant... - if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: + if self._have_constant_input(0): + + # Optimise away the constant storage if appropriate. if self._optimise_constant_storage(AttrInstruction, 1): return @@ -357,13 +365,14 @@ # Either test for a complete set of arguments. if target is not None: - nargs = len(target.positional_names) - if len(args) < nargs: + nargs_max = len(target.positional_names) + nargs_min = nargs_max - len(target.defaults) + if len(args) < nargs_min: raise TranslateError(self.module.full_name(), node, - "Insufficient arguments for %r: need %d arguments." % (target.name, nargs)) - elif len(args) > nargs and not target.has_star and not target.has_dstar: + "Insufficient arguments for %r: need at least %d arguments." % (target.name, nargs_min)) + elif len(args) > nargs_max and not target.has_star and not target.has_dstar: raise TranslateError(self.module.full_name(), node, - "Too many arguments for %r: need %d arguments." % (target.name, nargs)) + "Too many arguments for %r: need at most %d arguments." % (target.name, nargs)) # Or generate instructions to do this at run-time. @@ -432,7 +441,23 @@ else: raise TranslateError(self.module.full_name(), node, "No __builtins__ module is available for name %r." % name) - # Optimisation methods. + # Optimisation tests. + + def _should_optimise_constant_storage(self): + return "constant_storage" in self.optimisations + + def _should_optimise_known_target(self): + return "known_target" in self.optimisations + + def _have_constant_input(self, n): + last = self.last_ops(n+1) + return len(last) > n and (isinstance(last[n], LoadAttr) and last[n].attr.assignments == 1 or + isinstance(last[n], LoadConst)) + + def _have_known_target(self): + return self._have_constant_input(0) + + # Optimisation methods. See the supported_optimisations class attribute. def _optimise_constant_storage(self, cls, n): @@ -441,12 +466,8 @@ also constant, optimise away both operations. """ - last = self.last_ops(n+1) - - if cls in (StoreAttr, StoreName) and len(last) > n and \ - (isinstance(last[n], LoadAttr) and last[n].attr.assignments == 1 or - isinstance(last[n], LoadConst)): - + if self._should_optimise_constant_storage() and cls in (StoreAttr, StoreName) and \ + self._have_constant_input(n) and self._have_constant_input(n-1): self.remove_ops(n+1) return 1 else: @@ -460,8 +481,8 @@ appropriate, get information about the specific initialiser. """ - last = self.last_op() - if isinstance(last, (LoadName, LoadAttr)) and last.attr.assignments == 1: + if self._should_optimise_known_target() and self._have_known_target(): + last = self.last_op() target = last.attr.value context = last.attr.parent diff -r e704da427db4 -r eb3c1ed9dbee micropython/inspect.py --- a/micropython/inspect.py Mon Mar 24 23:53:18 2008 +0100 +++ b/micropython/inspect.py Mon Mar 31 00:54:03 2008 +0200 @@ -571,11 +571,12 @@ "An inspected function." - def __init__(self, name, parent, argnames, has_star, has_dstar, global_namespace=None, node=None): + def __init__(self, name, parent, argnames, defaults, has_star, has_dstar, global_namespace=None, node=None): NamespaceDict.__init__(self, global_namespace) self.name = name self.parent = parent self.argnames = argnames + self.defaults = defaults self.positional_names = self.argnames[:] if has_dstar: self.dstar_name = self.positional_names[-1] @@ -609,12 +610,12 @@ def __repr__(self): if self.location is not None: - return "Function(%r, %r, %r, %r, %r, location=%r)" % ( - self.name, self.parent, self.argnames, self.has_star, self.has_dstar, self.location + return "Function(%r, %r, %r, %r, %r, %r, location=%r)" % ( + self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar, self.location ) else: - return "Function(%r, %r, %r, %r, %r)" % ( - self.name, self.parent, self.argnames, self.has_star, self.has_dstar + return "Function(%r, %r, %r, %r, %r, %r)" % ( + self.name, self.parent, self.argnames, self.defaults, self.has_star, self.has_dstar ) def make_global(self, name): @@ -679,7 +680,8 @@ "Make a function from a method." - return Function(self.name, self.parent, self.argnames[1:], self.has_star, self.has_dstar, self.global_namespace, self.node) + return Function(self.name, self.parent, self.argnames[1:], self.defaults, + self.has_star, self.has_dstar, self.global_namespace, self.node) class UnresolvedName(NamespaceDict): @@ -1030,6 +1032,7 @@ node.name, self.get_parent(), node.argnames, + node.defaults, (node.flags & 4 != 0), (node.flags & 8 != 0), self, diff -r e704da427db4 -r eb3c1ed9dbee test.py --- a/test.py Mon Mar 24 23:53:18 2008 +0100 +++ b/test.py Mon Mar 31 00:54:03 2008 +0200 @@ -3,27 +3,34 @@ import micropython import sys -def show(importer, with_builtins=0): - for i, x in enumerate(importer.get_image(with_builtins=with_builtins)): +def show(importer, with_builtins=0, optimisations=None): + optimisations = optimisations or requested_optimisations + for i, x in enumerate(importer.get_image(with_builtins, optimisations)): print i, x def attrs(obj): for name, attr in obj.items(): print name, attr -i = micropython.Importer(sys.path, "-v" in sys.argv) -try: - builtins = i.load_from_file("lib/builtins.py", "__builtins__") - if len(sys.argv) < 2: - m = i.load("micropython") - #m = i.load_from_file("micropython/__init__.py") +if __name__ == "__main__": + requested_optimisations = [] + for arg in sys.argv[2:]: + if arg.startswith("-o"): + requested_optimisations.append(arg[2:]) + + i = micropython.Importer(sys.path, "-v" in sys.argv) + try: + builtins = i.load_from_file("lib/builtins.py", "__builtins__") + if len(sys.argv) < 2: + m = i.load("micropython") + #m = i.load_from_file("micropython/__init__.py") + else: + m = i.load_from_file(sys.argv[1]) + except micropython.ProcessingError, exc: + print repr(exc) else: - m = i.load_from_file(sys.argv[1]) -except micropython.ProcessingError, exc: - print repr(exc) -else: - i.vacuum() - ot = i.get_object_table() - pt = i.get_parameter_table() + i.vacuum() + ot = i.get_object_table() + pt = i.get_parameter_table() # vim: tabstop=4 expandtab shiftwidth=4 diff -r e704da427db4 -r eb3c1ed9dbee tests/call_func_default.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/call_func_default.py Mon Mar 31 00:54:03 2008 +0200 @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +def f(a, b, c=4): + pass + +f(1, 2, 3) +f(1, b=2, c=3) +f(c=3, b=2, a=1) +f(1, 2) + +g = f +g(1, c=3, b=2) +g(1, 2) + +def g(a, c, b=5): + pass + +g(1, c=3, b=2) +g(1, 3) + +# vim: tabstop=4 expandtab shiftwidth=4