[pypy-svn] r79018 - in pypy/branch/mapdict-without-jit/pypy/objspace/std: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Thu Nov 11 23:52:10 CET 2010


Author: cfbolz
Date: Thu Nov 11 23:52:08 2010
New Revision: 79018

Modified:
   pypy/branch/mapdict-without-jit/pypy/objspace/std/callmethod.py
   pypy/branch/mapdict-without-jit/pypy/objspace/std/mapdict.py
   pypy/branch/mapdict-without-jit/pypy/objspace/std/test/test_mapdict.py
Log:
Also use the local mapdict cache in the CALLMETHOD bytecode. I think it all can
be generalized a bit more, but I first want to see if this helps.


Modified: pypy/branch/mapdict-without-jit/pypy/objspace/std/callmethod.py
==============================================================================
--- pypy/branch/mapdict-without-jit/pypy/objspace/std/callmethod.py	(original)
+++ pypy/branch/mapdict-without-jit/pypy/objspace/std/callmethod.py	Thu Nov 11 23:52:08 2010
@@ -13,6 +13,9 @@
 from pypy.interpreter import function
 from pypy.objspace.descroperation import object_getattribute
 from pypy.rlib import jit, rstack # for resume points
+from pypy.objspace.std.mapdict import LOOKUP_METHOD_mapdict, \
+    LOOKUP_METHOD_mapdict_fill_cache_method
+
 
 # This module exports two extra methods for StdObjSpaceFrame implementing
 # the LOOKUP_METHOD and CALL_METHOD opcodes in an efficient way, as well
@@ -30,6 +33,13 @@
     #
     space = f.space
     w_obj = f.popvalue()
+
+    if space.config.objspace.std.withmapdict:
+        # mapdict has an extra-fast version of this function
+        from pypy.objspace.std.mapdict import LOOKUP_METHOD_mapdict
+        if LOOKUP_METHOD_mapdict(f, nameindex, w_obj):
+            return
+
     w_name = f.getname_w(nameindex)
     w_value = None
 
@@ -50,6 +60,10 @@
                     # nothing in the instance
                     f.pushvalue(w_descr)
                     f.pushvalue(w_obj)
+                    if space.config.objspace.std.withmapdict:
+                        # let mapdict cache stuff
+                        LOOKUP_METHOD_mapdict_fill_cache_method(
+                            f.getcode(), nameindex, w_obj, w_type, w_descr)
                     return
     if w_value is None:
         w_value = space.getattr(w_obj, w_name)

Modified: pypy/branch/mapdict-without-jit/pypy/objspace/std/mapdict.py
==============================================================================
--- pypy/branch/mapdict-without-jit/pypy/objspace/std/mapdict.py	(original)
+++ pypy/branch/mapdict-without-jit/pypy/objspace/std/mapdict.py	Thu Nov 11 23:52:08 2010
@@ -659,30 +659,54 @@
     map = None
     version_tag = None
     index = 0
+    w_method = None # for callmethod
     success_counter = 0
     failure_counter = 0
 
+    def is_valid_for_obj(self, w_obj):
+        map = w_obj._get_mapdict_map()
+        return self.is_valid_for_map(map)
+
+    def is_valid_for_map(self, map):
+        if map is self.map:
+            version_tag = map.terminator.w_cls.version_tag()
+            if version_tag is self.version_tag:
+                # everything matches, it's incredibly fast
+                if map.space.config.objspace.std.withmethodcachecounter:
+                    self.success_counter += 1
+                return True
+        return False
+
 INVALID_CACHE_ENTRY = CacheEntry()
 INVALID_CACHE_ENTRY.map = objectmodel.instantiate(AbstractAttribute)
                              # different from any real map ^^^
 INVALID_CACHE_ENTRY.map.terminator = None
 
+
 def init_mapdict_cache(pycode):
     num_entries = len(pycode.co_names_w)
     pycode._mapdict_caches = [INVALID_CACHE_ENTRY] * num_entries
 
+def _fill_cache(pycode, nameindex, map, version_tag, index, w_method=None):
+    entry = pycode._mapdict_caches[nameindex]
+    if entry is INVALID_CACHE_ENTRY:
+        entry = CacheEntry()
+        pycode._mapdict_caches[nameindex] = entry
+    entry.map = map
+    entry.version_tag = version_tag
+    entry.index = index
+    entry.w_method = w_method
+    if pycode.space.config.objspace.std.withmethodcachecounter:
+        entry.failure_counter += 1
+
 def LOAD_ATTR_caching(pycode, w_obj, nameindex):
     # this whole mess is to make the interpreter quite a bit faster; it's not
     # used if we_are_jitted().
     entry = pycode._mapdict_caches[nameindex]
     map = w_obj._get_mapdict_map()
-    if map is entry.map:
-        version_tag = map.terminator.w_cls.version_tag()
-        if version_tag is entry.version_tag:
-            # everything matches, it's incredibly fast
-            if pycode.space.config.objspace.std.withmethodcachecounter:
-                entry.success_counter += 1
-            return w_obj._mapdict_read_storage(entry.index)
+    if entry.is_valid_for_map(map):
+        # everything matches, it's incredibly fast
+        return w_obj._mapdict_read_storage(entry.index)
     return LOAD_ATTR_slowpath(pycode, w_obj, nameindex, map)
 LOAD_ATTR_caching._always_inline_ = True
 
@@ -710,17 +734,30 @@
             if selector[1] != INVALID:
                 index = map.index(selector)
                 if index >= 0:
-                    entry = pycode._mapdict_caches[nameindex]
-                    if entry is INVALID_CACHE_ENTRY:
-                        entry = CacheEntry()
-                        pycode._mapdict_caches[nameindex] = entry
-                    entry.map = map
-                    entry.version_tag = version_tag
-                    entry.index = index
-                    if space.config.objspace.std.withmethodcachecounter:
-                        entry.failure_counter += 1
+                    _fill_cache(pycode, nameindex, map, version_tag, index)
                     return w_obj._mapdict_read_storage(index)
     if space.config.objspace.std.withmethodcachecounter:
         INVALID_CACHE_ENTRY.failure_counter += 1
     return space.getattr(w_obj, w_name)
 LOAD_ATTR_slowpath._dont_inline_ = True
+
+def LOOKUP_METHOD_mapdict(f, nameindex, w_obj):
+    space = f.space
+    pycode = f.getcode()
+    entry = pycode._mapdict_caches[nameindex]
+    if entry.is_valid_for_obj(w_obj):
+        w_method = entry.w_method
+        if w_method is not None:
+            f.pushvalue(w_method)
+            f.pushvalue(w_obj)
+            return True
+    return False
+
+def LOOKUP_METHOD_mapdict_fill_cache_method(pycode, nameindex, w_obj, w_type, w_method):
+    version_tag = w_type.version_tag()
+    if version_tag is None:
+        return
+    map = w_obj._get_mapdict_map()
+    if map is None:
+        return
+    _fill_cache(pycode, nameindex, map, version_tag, -1, w_method)

Modified: pypy/branch/mapdict-without-jit/pypy/objspace/std/test/test_mapdict.py
==============================================================================
--- pypy/branch/mapdict-without-jit/pypy/objspace/std/test/test_mapdict.py	(original)
+++ pypy/branch/mapdict-without-jit/pypy/objspace/std/test/test_mapdict.py	Thu Nov 11 23:52:08 2010
@@ -597,7 +597,8 @@
         from pypy.interpreter import gateway
         cls.space = gettestobjspace(
             **{"objspace.std.withmapdict": True,
-               "objspace.std.withmethodcachecounter": True})
+               "objspace.std.withmethodcachecounter": True,
+               "objspace.opcodes.CALL_METHOD": True})
         #
         def check(space, w_func, name):
             w_code = space.getattr(w_func, space.wrap('func_code'))
@@ -778,6 +779,58 @@
         res = self.check(f, 'x')
         assert res == (0, 0, 1)
 
+    def test_call_method_uses_cache(self):
+        # bit sucky
+        global C
+
+        class C(object):
+            def m(*args):
+                return args
+        C.sm = staticmethod(C.m.im_func)
+        C.cm = classmethod(C.m.im_func)
+
+        exec """if 1:
+
+            def f():
+                c = C()
+                res = c.m(1)
+                assert res == (c, 1)
+                return 42
+
+            def g():
+                c = C()
+                res = c.sm(1)
+                assert res == (1, )
+                return 42
+
+            def h():
+                c = C()
+                res = c.cm(1)
+                assert res == (C, 1)
+                return 42
+        """
+        res = self.check(f, 'm')
+        assert res == (1, 0, 0)
+        res = self.check(f, 'm')
+        assert res == (0, 1, 0)
+        res = self.check(f, 'm')
+        assert res == (0, 1, 0)
+        res = self.check(f, 'm')
+        assert res == (0, 1, 0)
+
+        # static methods are not cached
+        res = self.check(g, 'sm')
+        assert res == (0, 0, 0)
+        res = self.check(g, 'sm')
+        assert res == (0, 0, 0)
+
+        # neither are class methods
+        res = self.check(h, 'cm')
+        assert res == (0, 0, 0)
+        res = self.check(h, 'cm')
+        assert res == (0, 0, 0)
+
+
 class AppTestGlobalCaching(AppTestWithMapDict):
     def setup_class(cls):
         cls.space = gettestobjspace(



More information about the Pypy-commit mailing list