[Checkins] SVN: z3c.pt/trunk/ Repeatedly calling flatten was horrifically expensive according to

Chris McDonough chrism at plope.com
Thu Aug 21 03:01:09 EDT 2008


Log message for revision 90049:
  Repeatedly calling flatten was horrifically expensive according to
  profiler output.
  
  

Changed:
  U   z3c.pt/trunk/CHANGES.txt
  U   z3c.pt/trunk/src/z3c/pt/codegen.py

-=-
Modified: z3c.pt/trunk/CHANGES.txt
===================================================================
--- z3c.pt/trunk/CHANGES.txt	2008-08-21 02:15:15 UTC (rev 90048)
+++ z3c.pt/trunk/CHANGES.txt	2008-08-21 07:01:08 UTC (rev 90049)
@@ -4,6 +4,9 @@
 Version 1.0dev
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+- Improve performance of codegen by not repeatedly calling
+  an expensive "flatten" function. [chrism]
+
 - Remove ``safe_render`` implementation detail.  It hid information
   in tracebacks. [chrism]
 

Modified: z3c.pt/trunk/src/z3c/pt/codegen.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/codegen.py	2008-08-21 02:15:15 UTC (rev 90048)
+++ z3c.pt/trunk/src/z3c/pt/codegen.py	2008-08-21 07:01:08 UTC (rev 90049)
@@ -8,16 +8,18 @@
 CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis'])
 UNDEFINED = object()
 
-def flatten(items):
-    """Flattens a potentially nested sequence into a flat list."""
-
-    retval = []
-    for item in items:
-        if isinstance(item, (frozenset, list, set, tuple)):
-            retval += flatten(item)
+def flatten(list):
+    """Flattens a potentially nested sequence into a flat list.
+    """
+    l = []
+    for elt in list:
+        t = type(elt)
+        if t is set or t is tuple or t is list or t is frozenset:
+            for elt2 in flatten(elt):
+                l.append(elt2)
         else:
-            retval.append(item)
-    return retval
+            l.append(elt)
+    return l
 
 class Lookup(object):
     """Abstract base class for variable lookup implementations."""
@@ -52,13 +54,21 @@
 class TemplateASTTransformer(ASTTransformer):
     """Concrete AST transformer that implements the AST transformations needed
     for code embedded in templates.
+
     """
 
     def __init__(self, globals):
         self.locals = [CONSTANTS]
+        builtin = dir(__builtin__)
         self.locals.append(set(globals))
-        self.locals.append(set(dir(__builtin__)))
-        
+        self.locals.append(set(builtin))
+        # self.names is an optimization for visitName (so we don't
+        # need to flatten the locals every time it's called)
+        self.names = set()
+        self.names.update(CONSTANTS)
+        self.names.update(builtin)
+        self.names.update(globals)
+
     def visitConst(self, node):
         if isinstance(node.value, unicode):
             return ast.Const(node.value.encode('utf-8'))
@@ -68,13 +78,16 @@
         if len(self.locals) > 1:
             if node.flags == 'OP_ASSIGN':
                 self.locals[-1].add(node.name)
+                self.names.add(node.name)
             else:
                 self.locals[-1].remove(node.name)
+                self.names.remove(node.name)
         return node
 
     def visitClass(self, node):
         if len(self.locals) > 1:
             self.locals[-1].add(node.name)
+            self.names.add(node.name)
         self.locals.append(set())
         try:
             return ASTTransformer.visitClass(self, node)
@@ -91,7 +104,9 @@
     def visitFunction(self, node):
         if len(self.locals) > 1:
             self.locals[-1].add(node.name)
+            self.names.add(node.name)
         self.locals.append(set(node.argnames))
+        self.names.update(node.argnames)
         try:
             return ASTTransformer.visitFunction(self, node)
         finally:
@@ -105,7 +120,9 @@
             self.locals.pop()
 
     def visitLambda(self, node):
-        self.locals.append(set(flatten(node.argnames)))
+        argnames = flatten(node.argnames)
+        self.names.update(argnames)
+        self.locals.append(set(argnames))
         try:
             return ASTTransformer.visitLambda(self, node)
         finally:
@@ -121,7 +138,7 @@
     def visitName(self, node):
         # If the name refers to a local inside a lambda, list comprehension, or
         # generator expression, leave it alone
-        if node.name not in flatten(self.locals):
+        if not node.name in self.names:
             # Otherwise, translate the name ref into a context lookup
             func_args = [ast.Name('_scope'), ast.Const(node.name)]
             node = ast.CallFunc(ast.Name('_lookup_name'), func_args)



More information about the Checkins mailing list