Author: Carl Friedrich Bolz <[email protected]>
Branch: 
Changeset: r81857:170318ed8b6b
Date: 2016-01-19 14:56 +0100
http://bitbucket.org/pypy/pypy/changeset/170318ed8b6b/

Log:    don't create 2-tuples all the time in mapdict. pass the content
        along instead

        this changes nothing in the warmed up case, but it means the JIT
        doesn't have to virtualize tuples all the time just because. also,
        the interpreter is a bit faster.

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
@@ -30,10 +30,10 @@
         assert isinstance(terminator, Terminator)
         self.terminator = terminator
 
-    def read(self, obj, selector):
-        attr = self.find_map_attr(selector)
+    def read(self, obj, name, index):
+        attr = self.find_map_attr(name, index)
         if attr is None:
-            return self.terminator._read_terminator(obj, selector)
+            return self.terminator._read_terminator(obj, name, index)
         if (
             jit.isconstant(attr.storageindex) and
             jit.isconstant(obj) and
@@ -47,39 +47,39 @@
     def _pure_mapdict_read_storage(self, obj, storageindex):
         return obj._mapdict_read_storage(storageindex)
 
-    def write(self, obj, selector, w_value):
-        attr = self.find_map_attr(selector)
+    def write(self, obj, name, index, w_value):
+        attr = self.find_map_attr(name, index)
         if attr is None:
-            return self.terminator._write_terminator(obj, selector, w_value)
+            return self.terminator._write_terminator(obj, name, index, w_value)
         if not attr.ever_mutated:
             attr.ever_mutated = True
         obj._mapdict_write_storage(attr.storageindex, w_value)
         return True
 
-    def delete(self, obj, selector):
+    def delete(self, obj, name, index):
         pass
 
-    def find_map_attr(self, selector):
+    def find_map_attr(self, name, index):
         if jit.we_are_jitted():
             # hack for the jit:
             # the _find_map_attr method is pure too, but its argument is never
             # constant, because it is always a new tuple
-            return self._find_map_attr_jit_pure(selector[0], selector[1])
+            return self._find_map_attr_jit_pure(name, index)
         else:
-            return self._find_map_attr_indirection(selector)
+            return self._find_map_attr_indirection(name, index)
 
     @jit.elidable
     def _find_map_attr_jit_pure(self, name, index):
-        return self._find_map_attr_indirection((name, index))
+        return self._find_map_attr_indirection(name, index)
 
     @jit.dont_look_inside
-    def _find_map_attr_indirection(self, selector):
+    def _find_map_attr_indirection(self, name, index):
         if (self.space.config.objspace.std.withmethodcache):
-            return self._find_map_attr_cache(selector)
-        return self._find_map_attr(selector)
+            return self._find_map_attr_cache(name, index)
+        return self._find_map_attr(name, index)
 
     @jit.dont_look_inside
-    def _find_map_attr_cache(self, selector):
+    def _find_map_attr_cache(self, name, index):
         space = self.space
         cache = space.fromcache(MapAttrCache)
         SHIFT2 = r_uint.BITS - space.config.objspace.std.methodcachesizeexp
@@ -87,31 +87,36 @@
         attrs_as_int = objectmodel.current_object_addr_as_int(self)
         # ^^^Note: see comment in typeobject.py for
         # _pure_lookup_where_with_method_cache()
-        hash_selector = objectmodel.compute_hash(selector)
+
+        # unrolled hash computation for 2-tuple
+        c1 = 0x345678
+        c2 = 1000003
+        hash_name = objectmodel.compute_hash(name)
+        hash_selector = intmask((c2 * ((c2 * c1) ^ hash_name)) ^ index)
         product = intmask(attrs_as_int * hash_selector)
         attr_hash = (r_uint(product) ^ (r_uint(product) << SHIFT1)) >> SHIFT2
         # ^^^Note2: same comment too
         cached_attr = cache.attrs[attr_hash]
         if cached_attr is self:
-            cached_selector = cache.selectors[attr_hash]
-            if cached_selector == selector:
+            cached_name = cache.names[attr_hash]
+            cached_index = cache.indexes[attr_hash]
+            if cached_name == name and cached_index == index:
                 attr = cache.cached_attrs[attr_hash]
                 if space.config.objspace.std.withmethodcachecounter:
-                    name = selector[0]
                     cache.hits[name] = cache.hits.get(name, 0) + 1
                 return attr
-        attr = self._find_map_attr(selector)
+        attr = self._find_map_attr(name, index)
         cache.attrs[attr_hash] = self
-        cache.selectors[attr_hash] = selector
+        cache.names[attr_hash] = name
+        cache.indexes[attr_hash] = index
         cache.cached_attrs[attr_hash] = attr
         if space.config.objspace.std.withmethodcachecounter:
-            name = selector[0]
             cache.misses[name] = cache.misses.get(name, 0) + 1
         return attr
 
-    def _find_map_attr(self, selector):
+    def _find_map_attr(self, name, index):
         while isinstance(self, PlainAttribute):
-            if selector == self.selector:
+            if name == self.name and index == self.index:
                 return self
             self = self.back
         return None
@@ -137,23 +142,22 @@
 
     @jit.elidable
     def _get_new_attr(self, name, index):
-        selector = name, index
         cache = self.cache_attrs
         if cache is None:
             cache = self.cache_attrs = {}
-        attr = cache.get(selector, None)
+        attr = cache.get((name, index), None)
         if attr is None:
-            attr = PlainAttribute(selector, self)
-            cache[selector] = attr
+            attr = PlainAttribute(name, index, self)
+            cache[name, index] = attr
         return attr
 
-    @jit.look_inside_iff(lambda self, obj, selector, w_value:
+    @jit.look_inside_iff(lambda self, obj, name, index, w_value:
             jit.isconstant(self) and
-            jit.isconstant(selector[0]) and
-            jit.isconstant(selector[1]))
-    def add_attr(self, obj, selector, w_value):
+            jit.isconstant(name) and
+            jit.isconstant(index))
+    def add_attr(self, obj, name, index, w_value):
         # grumble, jit needs this
-        attr = self._get_new_attr(selector[0], selector[1])
+        attr = self._get_new_attr(name, index)
         oldattr = obj._get_mapdict_map()
         if not jit.we_are_jitted():
             size_est = (oldattr._size_estimate + attr.size_estimate()
@@ -189,11 +193,11 @@
         AbstractAttribute.__init__(self, space, self)
         self.w_cls = w_cls
 
-    def _read_terminator(self, obj, selector):
+    def _read_terminator(self, obj, name, index):
         return None
 
-    def _write_terminator(self, obj, selector, w_value):
-        obj._get_mapdict_map().add_attr(obj, selector, w_value)
+    def _write_terminator(self, obj, name, index, w_value):
+        obj._get_mapdict_map().add_attr(obj, name, index, w_value)
         return True
 
     def copy(self, obj):
@@ -231,40 +235,40 @@
 
 
 class NoDictTerminator(Terminator):
-    def _write_terminator(self, obj, selector, w_value):
-        if selector[1] == DICT:
+    def _write_terminator(self, obj, name, index, w_value):
+        if index == DICT:
             return False
-        return Terminator._write_terminator(self, obj, selector, w_value)
+        return Terminator._write_terminator(self, obj, name, index, w_value)
 
 
 class DevolvedDictTerminator(Terminator):
-    def _read_terminator(self, obj, selector):
-        if selector[1] == DICT:
+    def _read_terminator(self, obj, name, index):
+        if index == DICT:
             space = self.space
             w_dict = obj.getdict(space)
-            return space.finditem_str(w_dict, selector[0])
-        return Terminator._read_terminator(self, obj, selector)
+            return space.finditem_str(w_dict, name)
+        return Terminator._read_terminator(self, obj, name, index)
 
-    def _write_terminator(self, obj, selector, w_value):
-        if selector[1] == DICT:
+    def _write_terminator(self, obj, name, index, w_value):
+        if index == DICT:
             space = self.space
             w_dict = obj.getdict(space)
-            space.setitem_str(w_dict, selector[0], w_value)
+            space.setitem_str(w_dict, name, w_value)
             return True
-        return Terminator._write_terminator(self, obj, selector, w_value)
+        return Terminator._write_terminator(self, obj, name, index, w_value)
 
-    def delete(self, obj, selector):
+    def delete(self, obj, name, index):
         from pypy.interpreter.error import OperationError
-        if selector[1] == DICT:
+        if index == DICT:
             space = self.space
             w_dict = obj.getdict(space)
             try:
-                space.delitem(w_dict, space.wrap(selector[0]))
+                space.delitem(w_dict, space.wrap(name))
             except OperationError, ex:
                 if not ex.match(space, space.w_KeyError):
                     raise
             return Terminator.copy(self, obj)
-        return Terminator.delete(self, obj, selector)
+        return Terminator.delete(self, obj, name, index)
 
     def remove_dict_entries(self, obj):
         assert 0, "should be unreachable"
@@ -276,27 +280,28 @@
         return Terminator.set_terminator(self, obj, terminator)
 
 class PlainAttribute(AbstractAttribute):
-    _immutable_fields_ = ['selector', 'storageindex', 'back', 'ever_mutated?']
+    _immutable_fields_ = ['name', 'index', 'storageindex', 'back', 
'ever_mutated?']
 
-    def __init__(self, selector, back):
+    def __init__(self, name, index, back):
         AbstractAttribute.__init__(self, back.space, back.terminator)
-        self.selector = selector
+        self.name = name
+        self.index = index
         self.storageindex = back.length()
         self.back = back
         self._size_estimate = self.length() * NUM_DIGITS_POW2
         self.ever_mutated = False
 
     def _copy_attr(self, obj, new_obj):
-        w_value = self.read(obj, self.selector)
-        new_obj._get_mapdict_map().add_attr(new_obj, self.selector, w_value)
+        w_value = self.read(obj, self.name, self.index)
+        new_obj._get_mapdict_map().add_attr(new_obj, self.name, self.index, 
w_value)
 
-    def delete(self, obj, selector):
-        if selector == self.selector:
+    def delete(self, obj, name, index):
+        if name == self.name and index == self.index:
             # ok, attribute is deleted
             if not self.ever_mutated:
                 self.ever_mutated = True
             return self.back.copy(obj)
-        new_obj = self.back.delete(obj, selector)
+        new_obj = self.back.delete(obj, name, index)
         if new_obj is not None:
             self._copy_attr(obj, new_obj)
         return new_obj
@@ -315,14 +320,14 @@
         return new_obj
 
     def search(self, attrtype):
-        if self.selector[1] == attrtype:
+        if self.index == attrtype:
             return self
         return self.back.search(attrtype)
 
     def materialize_r_dict(self, space, obj, dict_w):
         new_obj = self.back.materialize_r_dict(space, obj, dict_w)
-        if self.selector[1] == DICT:
-            w_attr = space.wrap(self.selector[0])
+        if self.index == DICT:
+            w_attr = space.wrap(self.name)
             dict_w[w_attr] = obj._mapdict_read_storage(self.storageindex)
         else:
             self._copy_attr(obj, new_obj)
@@ -330,12 +335,12 @@
 
     def remove_dict_entries(self, obj):
         new_obj = self.back.remove_dict_entries(obj)
-        if self.selector[1] != DICT:
+        if self.index != DICT:
             self._copy_attr(obj, new_obj)
         return new_obj
 
     def __repr__(self):
-        return "<PlainAttribute %s %s %r>" % (self.selector, 
self.storageindex, self.back)
+        return "<PlainAttribute %s %s %s %r>" % (self.name, self.index, 
self.storageindex, self.back)
 
 def _become(w_obj, new_obj):
     # this is like the _become method, really, but we cannot use that due to
@@ -347,8 +352,8 @@
         assert space.config.objspace.std.withmethodcache
         SIZE = 1 << space.config.objspace.std.methodcachesizeexp
         self.attrs = [None] * SIZE
-        self._empty_selector = (None, INVALID)
-        self.selectors = [self._empty_selector] * SIZE
+        self.names = [None] * SIZE
+        self.indexes = [INVALID] * SIZE
         self.cached_attrs = [None] * SIZE
         if space.config.objspace.std.withmethodcachecounter:
             self.hits = {}
@@ -357,8 +362,9 @@
     def clear(self):
         for i in range(len(self.attrs)):
             self.attrs[i] = None
-        for i in range(len(self.selectors)):
-            self.selectors[i] = self._empty_selector
+        for i in range(len(self.names)):
+            self.names[i] = None
+            self.indexes[i] = INVALID
         for i in range(len(self.cached_attrs)):
             self.cached_attrs[i] = None
 
@@ -388,20 +394,20 @@
     # objspace interface
 
     def getdictvalue(self, space, attrname):
-        return self._get_mapdict_map().read(self, (attrname, DICT))
+        return self._get_mapdict_map().read(self, attrname, DICT)
 
     def setdictvalue(self, space, attrname, w_value):
-        return self._get_mapdict_map().write(self, (attrname, DICT), w_value)
+        return self._get_mapdict_map().write(self, attrname, DICT, w_value)
 
     def deldictvalue(self, space, attrname):
-        new_obj = self._get_mapdict_map().delete(self, (attrname, DICT))
+        new_obj = self._get_mapdict_map().delete(self, attrname, DICT)
         if new_obj is None:
             return False
         self._become(new_obj)
         return True
 
     def getdict(self, space):
-        w_dict = self._get_mapdict_map().read(self, ("dict", SPECIAL))
+        w_dict = self._get_mapdict_map().read(self, "dict", SPECIAL)
         if w_dict is not None:
             assert isinstance(w_dict, W_DictMultiObject)
             return w_dict
@@ -409,7 +415,7 @@
         strategy = space.fromcache(MapDictStrategy)
         storage = strategy.erase(self)
         w_dict = W_DictObject(space, strategy, storage)
-        flag = self._get_mapdict_map().write(self, ("dict", SPECIAL), w_dict)
+        flag = self._get_mapdict_map().write(self, "dict", SPECIAL, w_dict)
         assert flag
         return w_dict
 
@@ -425,7 +431,7 @@
         # shell that continues to delegate to 'self'.
         if type(w_olddict.get_strategy()) is MapDictStrategy:
             w_olddict.get_strategy().switch_to_object_strategy(w_olddict)
-        flag = self._get_mapdict_map().write(self, ("dict", SPECIAL), w_dict)
+        flag = self._get_mapdict_map().write(self, "dict", SPECIAL, w_dict)
         assert flag
 
     def getclass(self, space):
@@ -443,16 +449,16 @@
         self._init_empty(w_subtype.terminator)
 
     def getslotvalue(self, slotindex):
-        key = ("slot", SLOTS_STARTING_FROM + slotindex)
-        return self._get_mapdict_map().read(self, key)
+        index = SLOTS_STARTING_FROM + slotindex
+        return self._get_mapdict_map().read(self, "slot", index)
 
     def setslotvalue(self, slotindex, w_value):
-        key = ("slot", SLOTS_STARTING_FROM + slotindex)
-        self._get_mapdict_map().write(self, key, w_value)
+        index = SLOTS_STARTING_FROM + slotindex
+        self._get_mapdict_map().write(self, "slot", index, w_value)
 
     def delslotvalue(self, slotindex):
-        key = ("slot", SLOTS_STARTING_FROM + slotindex)
-        new_obj = self._get_mapdict_map().delete(self, key)
+        index = SLOTS_STARTING_FROM + slotindex
+        new_obj = self._get_mapdict_map().delete(self, "slot", index)
         if new_obj is None:
             return False
         self._become(new_obj)
@@ -462,7 +468,7 @@
 
     def getweakref(self):
         from pypy.module._weakref.interp__weakref import WeakrefLifeline
-        lifeline = self._get_mapdict_map().read(self, ("weakref", SPECIAL))
+        lifeline = self._get_mapdict_map().read(self, "weakref", SPECIAL)
         if lifeline is None:
             return None
         assert isinstance(lifeline, WeakrefLifeline)
@@ -472,11 +478,11 @@
     def setweakref(self, space, weakreflifeline):
         from pypy.module._weakref.interp__weakref import WeakrefLifeline
         assert isinstance(weakreflifeline, WeakrefLifeline)
-        self._get_mapdict_map().write(self, ("weakref", SPECIAL), 
weakreflifeline)
+        self._get_mapdict_map().write(self, "weakref", SPECIAL, 
weakreflifeline)
     setweakref._cannot_really_call_random_things_ = True
 
     def delweakref(self):
-        self._get_mapdict_map().write(self, ("weakref", SPECIAL), None)
+        self._get_mapdict_map().write(self, "weakref", SPECIAL, None)
     delweakref._cannot_really_call_random_things_ = True
 
 class ObjectMixin(object):
@@ -721,7 +727,7 @@
         curr = self.unerase(w_dict.dstorage)._get_mapdict_map().search(DICT)
         if curr is None:
             raise KeyError
-        key = curr.selector[0]
+        key = curr.name
         w_value = self.getitem_str(w_dict, key)
         w_key = self.space.wrap(key)
         self.delitem(w_dict, w_key)
@@ -758,7 +764,7 @@
             curr_map = self.curr_map.search(DICT)
             if curr_map:
                 self.curr_map = curr_map.back
-                attr = curr_map.selector[0]
+                attr = curr_map.name
                 w_attr = self.space.wrap(attr)
                 return w_attr
         return None
@@ -780,7 +786,7 @@
             curr_map = self.curr_map.search(DICT)
             if curr_map:
                 self.curr_map = curr_map.back
-                attr = curr_map.selector[0]
+                attr = curr_map.name
                 return self.w_obj.getdictvalue(self.space, attr)
         return None
 
@@ -801,7 +807,7 @@
             curr_map = self.curr_map.search(DICT)
             if curr_map:
                 self.curr_map = curr_map.back
-                attr = curr_map.selector[0]
+                attr = curr_map.name
                 w_attr = self.space.wrap(attr)
                 return w_attr, self.w_obj.getdictvalue(self.space, attr)
         return None, None
@@ -884,9 +890,9 @@
             _, w_descr = w_type._pure_lookup_where_possibly_with_method_cache(
                 name, version_tag)
             #
-            selector = ("", INVALID)
+            attrname, index = ("", INVALID)
             if w_descr is None:
-                selector = (name, DICT) # common case: no such attr in the 
class
+                attrname, index = (name, DICT) # common case: no such attr in 
the class
             elif isinstance(w_descr, MutableCell):
                 pass              # we have a MutableCell in the class: give up
             elif space.is_data_descr(w_descr):
@@ -894,20 +900,21 @@
                 # (if any) has no relevance.
                 from pypy.interpreter.typedef import Member
                 if isinstance(w_descr, Member):    # it is a slot -- easy case
-                    selector = ("slot", SLOTS_STARTING_FROM + w_descr.index)
+                    attrname, index = ("slot", SLOTS_STARTING_FROM + 
w_descr.index)
             else:
                 # There is a non-data descriptor in the class.  If there is
                 # also a dict attribute, use the latter, caching its 
storageindex.
                 # If not, we loose.  We could do better in this case too,
                 # but we don't care too much; the common case of a method
                 # invocation is handled by LOOKUP_METHOD_xxx below.
-                selector = (name, DICT)
+                attrname = name
+                index = DICT
             #
-            if selector[1] != INVALID:
-                attr = map.find_map_attr(selector)
+            if index != INVALID:
+                attr = map.find_map_attr(attrname, index)
                 if attr is not None:
                     # Note that if map.terminator is a DevolvedDictTerminator,
-                    # map.find_map_attr will always return None if 
selector[1]==DICT.
+                    # map.find_map_attr will always return None if index==DICT.
                     _fill_cache(pycode, nameindex, map, version_tag, 
attr.storageindex)
                     return w_obj._mapdict_read_storage(attr.storageindex)
     if space.config.objspace.std.withmethodcachecounter:
diff --git a/pypy/objspace/std/test/test_mapdict.py 
b/pypy/objspace/std/test/test_mapdict.py
--- a/pypy/objspace/std/test/test_mapdict.py
+++ b/pypy/objspace/std/test/test_mapdict.py
@@ -34,8 +34,8 @@
 
 def test_plain_attribute():
     w_cls = "class"
-    aa = PlainAttribute(("b", DICT),
-                        PlainAttribute(("a", DICT),
+    aa = PlainAttribute("b", DICT,
+                        PlainAttribute("a", DICT,
                                        Terminator(space, w_cls)))
     assert aa.space is space
     assert aa.terminator.w_cls is w_cls
@@ -63,16 +63,16 @@
 def test_huge_chain():
     current = Terminator(space, "cls")
     for i in range(20000):
-        current = PlainAttribute((str(i), DICT), current)
-    assert current.find_map_attr(("0", DICT)).storageindex == 0
+        current = PlainAttribute(str(i), DICT, current)
+    assert current.find_map_attr("0", DICT).storageindex == 0
 
 
 def test_search():
-    aa = PlainAttribute(("b", DICT), PlainAttribute(("a", DICT), 
Terminator(None, None)))
+    aa = PlainAttribute("b", DICT, PlainAttribute("a", DICT, Terminator(None, 
None)))
     assert aa.search(DICT) is aa
     assert aa.search(SLOTS_STARTING_FROM) is None
     assert aa.search(SPECIAL) is None
-    bb = PlainAttribute(("C", SPECIAL), PlainAttribute(("A", 
SLOTS_STARTING_FROM), aa))
+    bb = PlainAttribute("C", SPECIAL, PlainAttribute("A", SLOTS_STARTING_FROM, 
aa))
     assert bb.search(DICT) is aa
     assert bb.search(SLOTS_STARTING_FROM) is bb.back
     assert bb.search(SPECIAL) is bb
@@ -320,7 +320,7 @@
 
     d = {}
     w_d = FakeDict(d)
-    flag = obj.map.write(obj, ("dict", SPECIAL), w_d)
+    flag = obj.map.write(obj, "dict", SPECIAL, w_d)
     assert flag
     materialize_r_dict(space, obj, d)
     assert d == {"a": 5, "b": 6, "c": 7}
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to