[pypy-svn] r54285 - in pypy/branch/faster-binop/pypy: config doc/config objspace objspace/std objspace/std/test

arigo at codespeak.net arigo at codespeak.net
Wed Apr 30 19:03:33 CEST 2008


Author: arigo
Date: Wed Apr 30 19:03:32 2008
New Revision: 54285

Added:
   pypy/branch/faster-binop/pypy/doc/config/objspace.std.getattributeshortcut.txt   (contents, props changed)
Modified:
   pypy/branch/faster-binop/pypy/config/pypyoption.py
   pypy/branch/faster-binop/pypy/objspace/descroperation.py
   pypy/branch/faster-binop/pypy/objspace/std/callmethod.py
   pypy/branch/faster-binop/pypy/objspace/std/objspace.py
   pypy/branch/faster-binop/pypy/objspace/std/test/test_callmethod.py
   pypy/branch/faster-binop/pypy/objspace/std/test/test_userobject.py
   pypy/branch/faster-binop/pypy/objspace/std/typeobject.py
Log:
Playing around with a possible getattr optimization.
Not too happy about the code duplication.


Modified: pypy/branch/faster-binop/pypy/config/pypyoption.py
==============================================================================
--- pypy/branch/faster-binop/pypy/config/pypyoption.py	(original)
+++ pypy/branch/faster-binop/pypy/config/pypyoption.py	Wed Apr 30 19:03:32 2008
@@ -284,6 +284,9 @@
         BoolOption("builtinshortcut",
                    "a shortcut for operations between built-in types",
                    default=False),
+        BoolOption("getattributeshortcut",
+                   "track types that override __getattribute__",
+                   default=False),
 
         BoolOption("oldstyle",
                    "specify whether the default metaclass should be classobj",

Added: pypy/branch/faster-binop/pypy/doc/config/objspace.std.getattributeshortcut.txt
==============================================================================
--- (empty file)
+++ pypy/branch/faster-binop/pypy/doc/config/objspace.std.getattributeshortcut.txt	Wed Apr 30 19:03:32 2008
@@ -0,0 +1 @@
+Performance only: track types that override __getattribute__.

Modified: pypy/branch/faster-binop/pypy/objspace/descroperation.py
==============================================================================
--- pypy/branch/faster-binop/pypy/objspace/descroperation.py	(original)
+++ pypy/branch/faster-binop/pypy/objspace/descroperation.py	Wed Apr 30 19:03:32 2008
@@ -129,7 +129,11 @@
         return space.get_and_call_function(w_delete, w_descr, w_obj)
 
     def getattr(space, w_obj, w_name):
+        # may be overridden in StdObjSpace
         w_descr = space.lookup(w_obj, '__getattribute__')
+        return space._handle_getattribute(w_descr, w_obj, w_name)
+
+    def _handle_getattribute(space, w_descr, w_obj, w_name):
         try:
             if w_descr is None:   # obscure case
                 raise OperationError(space.w_AttributeError, space.w_None)

Modified: pypy/branch/faster-binop/pypy/objspace/std/callmethod.py
==============================================================================
--- pypy/branch/faster-binop/pypy/objspace/std/callmethod.py	(original)
+++ pypy/branch/faster-binop/pypy/objspace/std/callmethod.py	Wed Apr 30 19:03:32 2008
@@ -32,10 +32,25 @@
     w_obj = f.popvalue()
     w_name = f.getname_w(nameindex)
     w_value = None
-    w_getattribute = space.lookup(w_obj, '__getattribute__')
-    if w_getattribute is object_getattribute(space):
+
+    if space.config.objspace.std.getattributeshortcut:
+        w_type = space.type(w_obj)
+        fastpath = w_type.uses_object_getattribute
+        # conservatively, 'uses_object_getattribute' can be False
+        # even if __getattribute__ was not overridden.  In this
+        # case, the code below calls space.getattr(), which will
+        # set 'uses_object_getattribute' to True for the next time.
+    else:
+        w_getattribute = space.lookup(w_obj, '__getattribute__')
+        if w_getattribute is object_getattribute(space):
+            w_type = space.type(w_obj)
+            fastpath = True
+        else:
+            fastpath = False
+
+    if fastpath:
         name = space.str_w(w_name)
-        w_descr = space.lookup(w_obj, name)
+        w_descr = w_type.lookup(name)
         if w_descr is None:
             # this handles directly the common case
             #   module.function(args..)

Modified: pypy/branch/faster-binop/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/branch/faster-binop/pypy/objspace/std/objspace.py	(original)
+++ pypy/branch/faster-binop/pypy/objspace/std/objspace.py	Wed Apr 30 19:03:32 2008
@@ -683,6 +683,51 @@
             return w_obj.boolval
         return DescrOperation.is_true(self, w_obj)
 
+    def getattr(self, w_obj, w_name):
+        if not self.config.objspace.std.getattributeshortcut:
+            return DescrOperation.getattr(self, w_obj, w_name)
+
+        # an optional shortcut for performance
+        from pypy.objspace.descroperation import raiseattrerror
+        from pypy.objspace.descroperation import object_getattribute
+        w_type = self.type(w_obj)
+        if not w_type.uses_object_getattribute:
+            # slow path: look for a custom __getattribute__ on the class
+            w_descr = w_type.lookup('__getattribute__')
+            # if it was not actually overriden in the class, we remember this
+            # fact for the next time.
+            if w_descr is object_getattribute(self):
+                w_type.uses_object_getattribute = True
+            return self._handle_getattribute(w_descr, w_obj, w_name)
+
+        # fast path: XXX this is duplicating most of the logic
+        # from the default __getattribute__ and the getattr() method...
+        name = self.str_w(w_name)
+        w_descr = w_type.lookup(name)
+        e = None
+        if w_descr is not None:
+            if not self.is_data_descr(w_descr):
+                w_value = w_obj.getdictvalue_attr_is_in_class(self, w_name)
+                if w_value is not None:
+                    return w_value
+            try:
+                return self.get(w_descr, w_obj)
+            except OperationError, e:
+                if not e.match(self, self.w_AttributeError):
+                    raise
+        else:
+            w_value = w_obj.getdictvalue(self, w_name)
+            if w_value is not None:
+                return w_value
+
+        w_descr = self.lookup(w_obj, '__getattr__')
+        if w_descr is not None:
+            return space.get_and_call_function(w_descr, w_obj, w_name)
+        elif e is not None:
+            raise e
+        else:
+            raiseattrerror(self, w_obj, name)
+
     def finditem(self, w_obj, w_key):
         # performance shortcut to avoid creating the OperationError(KeyError)
         if type(w_obj) is self.DictObjectCls:

Modified: pypy/branch/faster-binop/pypy/objspace/std/test/test_callmethod.py
==============================================================================
--- pypy/branch/faster-binop/pypy/objspace/std/test/test_callmethod.py	(original)
+++ pypy/branch/faster-binop/pypy/objspace/std/test/test_callmethod.py	Wed Apr 30 19:03:32 2008
@@ -6,8 +6,10 @@
     # The exec hacking is needed to have the code snippets compiled
     # by our own compiler, not CPython's
 
+    OPTIONS = {"objspace.opcodes.CALL_METHOD": True}
+
     def setup_class(cls):
-        cls.space = gettestobjspace(**{"objspace.opcodes.CALL_METHOD": True})
+        cls.space = gettestobjspace(**cls.OPTIONS)
 
     def test_call_method(self):
         exec """if 1:
@@ -106,6 +108,11 @@
         """
 
 
+class AppTestCallMethodWithGetattributeShortcut(AppTestCallMethod):
+    OPTIONS = AppTestCallMethod.OPTIONS.copy()
+    OPTIONS["objspace.std.getattributeshortcut"] = True
+
+
 class TestCallMethod:
 
     def setup_class(cls):

Modified: pypy/branch/faster-binop/pypy/objspace/std/test/test_userobject.py
==============================================================================
--- pypy/branch/faster-binop/pypy/objspace/std/test/test_userobject.py	(original)
+++ pypy/branch/faster-binop/pypy/objspace/std/test/test_userobject.py	Wed Apr 30 19:03:32 2008
@@ -216,3 +216,8 @@
     def teardown_class(cls):
         from pypy.objspace.std import multimethod
         multimethod.Installer = cls.prev_installer
+
+
+class AppTestWithGetAttributeShortcut(AppTestUserObject):
+    OPTIONS = {"objspace.std.getattributeshortcut": True}
+

Modified: pypy/branch/faster-binop/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/branch/faster-binop/pypy/objspace/std/typeobject.py	(original)
+++ pypy/branch/faster-binop/pypy/objspace/std/typeobject.py	Wed Apr 30 19:03:32 2008
@@ -50,6 +50,10 @@
 
     lazyloaders = {} # can be overridden by specific instances
 
+    uses_object_getattribute = False
+    # ^^^ for config.objspace.std.getattributeshortcut
+    # (False is a conservative default, fixed during real usage)
+
     def __init__(w_self, space, name, bases_w, dict_w,
                  overridetypedef=None):
         w_self.space = space
@@ -221,6 +225,9 @@
 
     def mutated(w_self):
         space = w_self.space
+        if space.config.objspace.std.getattributeshortcut:
+            w_self.uses_object_getattribute = False
+            # ^^^ conservative default, fixed during real usage
         if not space.config.objspace.std.withtypeversion:
             return
         # Invariant: version_tag is None if and only if



More information about the Pypy-commit mailing list