[pypy-commit] pypy reorder-map-attributes: a tweak to the algorithm to solve the problem of infinite reorderings more thoroughly

cfbolz pypy.commits at gmail.com
Mon Feb 15 05:11:14 EST 2016


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: reorder-map-attributes
Changeset: r82255:3387e677ff14
Date: 2016-02-15 00:46 +0100
http://bitbucket.org/pypy/pypy/changeset/3387e677ff14/

Log:	a tweak to the algorithm to solve the problem of infinite
	reorderings more thoroughly

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
@@ -1,4 +1,4 @@
-import weakref
+import weakref, sys
 
 from rpython.rlib import jit, objectmodel, debug, rerased
 from rpython.rlib.rarithmetic import intmask, r_uint
@@ -159,7 +159,7 @@
         if self.cache_attrs is not None:
             return self.cache_attrs.get(key, None)
         return None
-    
+
     def add_attr(self, obj, name, index, w_value):
         self._reorder_and_add(obj, name, index, w_value)
         if not jit.we_are_jitted():
@@ -193,53 +193,71 @@
             jit.isconstant(name) and
             jit.isconstant(index))
     def _reorder_and_add(self, obj, name, index, w_value):
+        # the idea is as follows: the subtrees of any map are ordered by insertion.
+        # the invariant is that subtrees that are inserted later must not contain
+        # the name of the attribute of any earlier inserted attribute anywhere
+        #                              m______
+        #         inserted first      / \ ... \   further attributes
+        #           attrname a      0/  1\    n\
+        #                           m  a must not appear here anywhere
+        #
+        # when inserting a new attribute in an object we check whether any
+        # parent of lower order has seen that attribute yet. if yes, we follow
+        # that branch. if not, we normally append that attribute. When we
+        # follow a prior branch, we necessarily remove some attributes to be
+        # able to do that. They need to be re-added, which has to follow the
+        # reordering procedure recusively.
+
+        # we store the to-be-readded attribute in stack_maps and stack_values
+        # those are lazily initialized to two lists large enough to store all
+        # current attributes
         stack_maps = None
         stack_values = None
         stack_index = 0
         while True:
             current = self
-            localstack_index = stack_index
+            number_to_readd = 0
+            current_order = sys.maxint
+            # walk up the map chain to find an ancestor with lower order that
+            # already has the current name as a child inserted
             while True:
                 attr = current._get_cache_attr(name, index)
-                if attr is None:
-                    # if not found in all ancestors
+                if attr is None or attr.order > current_order:
+                    # we reached the top, so we didn't find it anywhere,
+                    # just add it
                     if not isinstance(current, PlainAttribute):
                         self._add_attr_without_reordering(obj, name, index, w_value)
                         break
 
                     # if not found try parent
                     else:
-                        w_self_value = obj._mapdict_read_storage(current.storageindex)
+                        number_to_readd += 1
+                        current_order = current.order
+                        current = current.back
+                else:
+                    # we found the attributes further up, need to save the
+                    # previous values of the attributes we passed
+                    if number_to_readd:
                         if stack_maps is None:
                             stack_maps = [None] * self.length()
                             stack_values = [None] * self.length()
-                        stack_maps[localstack_index] = current
-                        stack_values[localstack_index] = w_self_value
-                        localstack_index += 1
-                        current = current.back
-                else:
+                        current = self
+                        for i in range(number_to_readd):
+                            assert isinstance(current, PlainAttribute)
+                            w_self_value = obj._mapdict_read_storage(
+                                    current.storageindex)
+                            stack_maps[stack_index] = current
+                            stack_values[stack_index] = w_self_value
+                            stack_index += 1
+                            current = current.back
                     attr._switch_map_and_write_storage(obj, w_value)
-                    if not localstack_index:
-                        return
-
-                    if not stack_index:
-                        # add the first attribute of the stack without reordering
-                        # to prevent an endless loop
-                        localstack_index += -1
-                        next_map = stack_maps[localstack_index]
-                        w_value = stack_values[localstack_index]
-                        obj._get_mapdict_map()._add_attr_without_reordering(
-                            obj, next_map.name, next_map.index, w_value)
-
-                    stack_index = localstack_index    
                     break
 
             if not stack_index:
                 return
 
-            # readd all other values from the stack (with reordering)
-            # the last element of the stack will be the new current
-            stack_index += -1
+            # readd the current top of the stack
+            stack_index -= 1
             next_map = stack_maps[stack_index]
             w_value = stack_values[stack_index]
             name = next_map.name
@@ -350,7 +368,7 @@
         return Terminator.set_terminator(self, obj, terminator)
 
 class PlainAttribute(AbstractAttribute):
-    _immutable_fields_ = ['name', 'index', 'storageindex', 'back', 'ever_mutated?']
+    _immutable_fields_ = ['name', 'index', 'storageindex', 'back', 'ever_mutated?', 'order']
 
     def __init__(self, name, index, back):
         AbstractAttribute.__init__(self, back.space, back.terminator)
@@ -360,6 +378,7 @@
         self.back = back
         self._size_estimate = self.length() * NUM_DIGITS_POW2
         self.ever_mutated = False
+        self.order = len(back.cache_attrs) if back.cache_attrs else 0
 
     def _copy_attr(self, obj, new_obj):
         w_value = self.read(obj, self.name, self.index)


More information about the pypy-commit mailing list