[pypy-svn] r36721 - in pypy/dist/pypy: config module/pypymagic objspace/std objspace/std/test

cfbolz at codespeak.net cfbolz at codespeak.net
Sun Jan 14 12:10:38 CET 2007


Author: cfbolz
Date: Sun Jan 14 12:10:36 2007
New Revision: 36721

Modified:
   pypy/dist/pypy/config/pypyoption.py
   pypy/dist/pypy/module/pypymagic/__init__.py
   pypy/dist/pypy/module/pypymagic/interp_magic.py
   pypy/dist/pypy/objspace/std/objspace.py
   pypy/dist/pypy/objspace/std/test/test_shadowtracking.py
   pypy/dist/pypy/objspace/std/typeobject.py
Log:
(pedronis, cfbolz): try to implement method lookup caching. seems to work as
intended, but gives very little speedup so far (about 4% in richards). more
work needed.


Modified: pypy/dist/pypy/config/pypyoption.py
==============================================================================
--- pypy/dist/pypy/config/pypyoption.py	(original)
+++ pypy/dist/pypy/config/pypyoption.py	Sun Jan 14 12:10:36 2007
@@ -155,8 +155,18 @@
                    default=False,
                    requires=[("objspace.std.withmultidict", True),
                              ("objspace.std.withtypeversion", True)]),
-
-
+        BoolOption("withmethodcache",
+                   "try to cache methods",
+                   default=False,
+                   requires=[("objspace.std.withshadowtracking", True)]),
+        BoolOption("withmethodcachecounter",
+                   "try to cache methods and provide a counter in pypymagic. "
+                   "for testing purposes only.",
+                   default=False,
+                   requires=[("objspace.std.withmethodcache", True)]),
+        IntOption("methodcachesize",
+                  "size of the method cache (should be a power of 2)",
+                  default=2048),
         BoolOption("optimized_int_add",
                    "special case the addition of two integers in BINARY_ADD",
                    default=False),

Modified: pypy/dist/pypy/module/pypymagic/__init__.py
==============================================================================
--- pypy/dist/pypy/module/pypymagic/__init__.py	(original)
+++ pypy/dist/pypy/module/pypymagic/__init__.py	Sun Jan 14 12:10:36 2007
@@ -10,4 +10,6 @@
         'pypy_repr'             : 'interp_magic.pypy_repr',
         'isfake'                : 'interp_magic.isfake',
         'interp_pdb'            : 'interp_magic.interp_pdb',
+        'method_cache_counter'  : 'interp_magic.method_cache_counter',
+        'reset_method_cache_counter'  : 'interp_magic.reset_method_cache_counter',
     }

Modified: pypy/dist/pypy/module/pypymagic/interp_magic.py
==============================================================================
--- pypy/dist/pypy/module/pypymagic/interp_magic.py	(original)
+++ pypy/dist/pypy/module/pypymagic/interp_magic.py	Sun Jan 14 12:10:36 2007
@@ -1,4 +1,5 @@
 from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import ObjSpace
 from pypy.rlib.objectmodel import we_are_translated
 
 def pypy_repr(space, w_object):
@@ -19,3 +20,21 @@
     else:
         import pdb
         pdb.set_trace()
+
+def method_cache_counter(space, name):
+    if not space.config.objspace.std.withmethodcachecounter:
+        raise OperationError(space.w_NotImplementedError,
+                             space.wrap("not implemented"))
+    ec = space.getexecutioncontext()
+    return space.newtuple([space.newint(ec.method_cache_hits.get(name, 0)),
+                           space.newint(ec.method_cache_misses.get(name, 0)),])
+method_cache_counter.unwrap_spec = [ObjSpace, str]
+
+def reset_method_cache_counter(space):
+    if not space.config.objspace.std.withmethodcachecounter:
+        raise OperationError(space.w_NotImplementedError,
+                             space.wrap("not implemented"))
+    ec = space.getexecutioncontext()
+    ec.method_cache_misses = {}
+    ec.method_cache_hits = {}
+

Modified: pypy/dist/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/std/objspace.py	(original)
+++ pypy/dist/pypy/objspace/std/objspace.py	Sun Jan 14 12:10:36 2007
@@ -275,6 +275,14 @@
         # add space specific fields to execution context
         ec = ObjSpace.createexecutioncontext(self)
         ec._py_repr = self.newdict()
+        if self.config.objspace.std.withmethodcache:
+            SIZE = self.config.objspace.std.methodcachesize
+            ec.method_cache_versions = [None] * SIZE
+            ec.method_cache_names = [None] * SIZE
+            ec.method_cache_lookup_where = [(None, None)] * SIZE
+            if self.config.objspace.std.withmethodcachecounter:
+                ec.method_cache_hits = {}
+                ec.method_cache_misses = {}
         return ec
 
     def createframe(self, code, w_globals, closure=None):

Modified: pypy/dist/pypy/objspace/std/test/test_shadowtracking.py
==============================================================================
--- pypy/dist/pypy/objspace/std/test/test_shadowtracking.py	(original)
+++ pypy/dist/pypy/objspace/std/test/test_shadowtracking.py	Sun Jan 14 12:10:36 2007
@@ -87,3 +87,86 @@
         assert a.f() == 42
         a.f = lambda : 43
         assert a.f() == 43
+
+class AppTestMethodCaching(AppTestShadowTracking):
+    def setup_class(cls):
+        cls.space = gettestobjspace(
+            **{"objspace.std.withmethodcachecounter": True})
+
+    def test_mix_classes(self):
+        import pypymagic
+        class A(object):
+            def f(self):
+                return 42
+        class B(object):
+            def f(self):
+                return 43
+        class C(object):
+            def f(self):
+                return 44
+        l = [A(), B(), C()] * 10
+        pypymagic.reset_method_cache_counter()
+        for i, a in enumerate(l):
+            assert a.f() == 42 + i % 3
+        cache_counter = pypymagic.method_cache_counter("f")
+        print cache_counter
+        assert cache_counter[1] >= 3 # should be (27, 3)
+        assert sum(cache_counter) == 30
+
+    def test_class_that_cannot_be_cached(self):
+        import pypymagic
+        class metatype(type):
+            pass
+        class A(object):
+            __metaclass__ = metatype
+            def f(self):
+                return 42
+
+        class B(object):
+            def f(self):
+                return 43
+        class C(object):
+            def f(self):
+                return 44
+        l = [A(), B(), C()] * 10
+        pypymagic.reset_method_cache_counter()
+        for i, a in enumerate(l):
+            assert a.f() == 42 + i % 3
+        cache_counter = pypymagic.method_cache_counter("f")
+        print cache_counter
+        assert cache_counter[1] >= 2 # should be (18, 2)
+        assert sum(cache_counter) == 20
+ 
+    def test_change_methods(self):
+        import pypymagic
+        class A(object):
+            def f(self):
+                return 42
+        l = [A()] * 10
+        pypymagic.reset_method_cache_counter()
+        for i, a in enumerate(l):
+            assert a.f() == 42 + i
+            A.f = eval("lambda self: %s" % (42 + i + 1, ))
+        cache_counter = pypymagic.method_cache_counter("f")
+        print cache_counter
+        assert cache_counter == (0, 10)
+
+    def test_subclasses(self):
+        import pypymagic
+        class A(object):
+            def f(self):
+                return 42
+        class B(object):
+            def f(self):
+                return 43
+        class C(A):
+            pass
+        l = [A(), B(), C()] * 10
+        pypymagic.reset_method_cache_counter()
+        for i, a in enumerate(l):
+            assert a.f() == 42 + (i % 3 == 1)
+        cache_counter = pypymagic.method_cache_counter("f")
+        print cache_counter
+        assert cache_counter[1] >= 3 # should be (27, 3)
+        assert sum(cache_counter) == 30
+  

Modified: pypy/dist/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/dist/pypy/objspace/std/typeobject.py	(original)
+++ pypy/dist/pypy/objspace/std/typeobject.py	Sun Jan 14 12:10:36 2007
@@ -276,16 +276,30 @@
                     return w_value
         return w_value
 
-    def lookup(w_self, key):
+    def lookup(w_self, name):
         # note that this doesn't call __get__ on the result at all
         space = w_self.space
+        if space.config.objspace.std.withmethodcache:
+            return w_self.lookup_where_with_method_cache(name)[1]
+
+        return w_self._lookup(name)
+
+    def lookup_where(w_self, name):
+        space = w_self.space
+        if space.config.objspace.std.withmethodcache:
+            return w_self.lookup_where_with_method_cache(name)
+
+        return w_self._lookup_where(name)
+
+    def _lookup(w_self, key):
+        space = w_self.space
         for w_class in w_self.mro_w:
             w_value = w_class.getdictvalue_w(space, key)
             if w_value is not None:
                 return w_value
         return None
 
-    def lookup_where(w_self, key):
+    def _lookup_where(w_self, key):
         # like lookup() but also returns the parent class in which the
         # attribute was found
         space = w_self.space
@@ -295,6 +309,41 @@
                 return w_class, w_value
         return None, None
 
+    def lookup_where_with_method_cache(w_self, name):
+        space = w_self.space
+        assert space.config.objspace.std.withmethodcache
+        ec = space.getexecutioncontext()
+        try:
+            frame = ec.framestack.top()
+            position_hash = frame.last_instr ^ id(frame.pycode)
+        except IndexError:
+            position_hash = 0
+        version_tag = w_self.version_tag
+        if version_tag is None:
+            tup = w_self._lookup_where(name)
+            return tup
+        SIZE = space.config.objspace.std.methodcachesize
+        method_hash = (id(version_tag) ^ position_hash ^ hash(name)) % SIZE
+        cached_version_tag = ec.method_cache_versions[method_hash]
+        if cached_version_tag is version_tag:
+            cached_name = ec.method_cache_names[method_hash]
+            if cached_name == name:
+                tup = ec.method_cache_lookup_where[method_hash]
+                if space.config.objspace.std.withmethodcachecounter:
+                    ec.method_cache_hits[name] = \
+                            ec.method_cache_hits.get(name, 0) + 1
+#                print "hit", w_self, name
+                return tup
+        tup = w_self._lookup_where(name)
+        ec.method_cache_versions[method_hash] = version_tag
+        ec.method_cache_names[method_hash] = name
+        ec.method_cache_lookup_where[method_hash] = tup
+        if space.config.objspace.std.withmethodcachecounter:
+            ec.method_cache_misses[name] = \
+                    ec.method_cache_misses.get(name, 0) + 1
+#        print "miss", w_self, name
+        return tup
+
     def check_user_subclass(w_self, w_subtype):
         space = w_self.space
         if not isinstance(w_subtype, W_TypeObject):



More information about the Pypy-commit mailing list