[pypy-commit] pypy remove-objspace-options: problems that the enabling mapdict by default found:

cfbolz pypy.commits at gmail.com
Sat Apr 23 14:18:36 EDT 2016


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: remove-objspace-options
Changeset: r83835:9df69444009e
Date: 2016-04-23 13:37 +0300
http://bitbucket.org/pypy/pypy/changeset/9df69444009e/

Log:	problems that the enabling mapdict by default found:

	- the mapdict cache needed an extra lookup, that is fixed
	- looking up non-method things via the class is bad

diff --git a/pypy/objspace/std/callmethod.py b/pypy/objspace/std/callmethod.py
--- a/pypy/objspace/std/callmethod.py
+++ b/pypy/objspace/std/callmethod.py
@@ -23,6 +23,7 @@
 
 
 def LOOKUP_METHOD(f, nameindex, *ignored):
+    from pypy.objspace.std.typeobject import MutableCell
     #   stack before                 after
     #  --------------    --fast-method----fallback-case------------
     #
@@ -44,7 +45,18 @@
     w_type = space.type(w_obj)
     if w_type.has_object_getattribute():
         name = space.str_w(w_name)
-        w_descr = w_type.lookup(name)
+        # bit of a mess to use these internal functions, but it allows the
+        # mapdict caching below to work without an additional lookup
+        version_tag = w_type.version_tag()
+        if version_tag is None:
+            _, w_descr = w_type._lookup_where(name)
+            w_descr_cell = None
+        else:
+            _, w_descr_cell = w_type._pure_lookup_where_possibly_with_method_cache(
+                name, version_tag)
+            w_descr = w_descr_cell
+            if isinstance(w_descr, MutableCell):
+                w_descr = w_descr.unwrap_cell(space)
         if w_descr is None:
             # this handles directly the common case
             #   module.function(args..)
@@ -62,7 +74,8 @@
                     if not jit.we_are_jitted():
                         # let mapdict cache stuff
                         LOOKUP_METHOD_mapdict_fill_cache_method(
-                            space, f.getcode(), name, nameindex, w_obj, w_type)
+                            space, f.getcode(), name, nameindex, w_obj, w_type,
+                            w_descr_cell)
                     return
     if w_value is None:
         w_value = space.getattr(w_obj, w_name)
diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py
--- a/pypy/objspace/std/mapdict.py
+++ b/pypy/objspace/std/mapdict.py
@@ -1011,22 +1011,15 @@
     return False
 
 def LOOKUP_METHOD_mapdict_fill_cache_method(space, pycode, name, nameindex,
-                                            w_obj, w_type):
+                                            w_obj, w_type, w_method):
+    if w_method is None or isinstance(w_method, MutableCell):
+        # don't cache the MutableCell XXX could be fixed
+        return
     version_tag = w_type.version_tag()
-    if version_tag is None:
-        return
+    assert version_tag is not None
     map = w_obj._get_mapdict_map()
     if map is None or isinstance(map.terminator, DevolvedDictTerminator):
         return
-    # We know here that w_obj.getdictvalue(space, name) just returned None,
-    # so the 'name' is not in the instance.  We repeat the lookup to find it
-    # in the class, this time taking care of the result: it can be either a
-    # quasi-constant class attribute, or actually a MutableCell --- which we
-    # must not cache.  (It should not be None here, but you never know...)
-    _, w_method = w_type._pure_lookup_where_possibly_with_method_cache(
-        name, version_tag)
-    if w_method is None or isinstance(w_method, MutableCell):
-        return
     _fill_cache(pycode, nameindex, map, version_tag, -1, w_method)
 
 # XXX fix me: if a function contains a loop with both LOAD_ATTR and
diff --git a/pypy/objspace/std/test/test_methodcache.py b/pypy/objspace/std/test/test_methodcache.py
--- a/pypy/objspace/std/test/test_methodcache.py
+++ b/pypy/objspace/std/test/test_methodcache.py
@@ -202,7 +202,8 @@
                 l = [type.__getattribute__(A, "__new__")(A)] * 10
                 __pypy__.reset_method_cache_counter()
                 for i, a in enumerate(l):
-                    assert a.f() == 42
+                    # use getattr to circumvent the mapdict cache
+                    assert getattr(a, "f")() == 42
                 cache_counter = __pypy__.method_cache_counter("f")
                 assert sum(cache_counter) == 10
                 if cache_counter == (9, 1):
@@ -225,9 +226,11 @@
                 assert a.x == i + 1
                 A.x += 1
             cache_counter = __pypy__.method_cache_counter("x")
-            assert cache_counter[0] >= 350
+            # XXX this is the bad case for the mapdict cache: looking up
+            # non-method attributes from the class
+            assert cache_counter[0] >= 450
             assert cache_counter[1] >= 1
-            assert sum(cache_counter) == 400
+            assert sum(cache_counter) == 500
 
             __pypy__.reset_method_cache_counter()
             a = A()


More information about the pypy-commit mailing list