Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r75895:5eb6bfa9ec8e
Date: 2015-02-15 15:49 +0100
http://bitbucket.org/pypy/pypy/changeset/5eb6bfa9ec8e/

Log:    Simplify the logic in rweaklist.

diff --git a/rpython/rlib/rweaklist.py b/rpython/rlib/rweaklist.py
--- a/rpython/rlib/rweaklist.py
+++ b/rpython/rlib/rweaklist.py
@@ -1,50 +1,40 @@
 import weakref
 from rpython.rlib.rweakref import dead_ref
 
-
-def _reduced_value(s):
-    while True:
-        divide = s & 1
-        s >>= 1
-        if not divide:
-            return s
+INITIAL_SIZE = 4
 
 
 class RWeakListMixin(object):
     _mixin_ = True
 
     def initialize(self):
-        self.handles = []
-        self.look_distance = 0
+        self.handles = [dead_ref] * INITIAL_SIZE
+        self.free_list = range(INITIAL_SIZE)
 
     def get_all_handles(self):
         return self.handles
 
     def reserve_next_handle_index(self):
-        # The reservation ordering done here is tweaked for pypy's
-        # memory allocator.  We look from index 'look_distance'.
-        # Look_distance increases from 0.  But we also look at
-        # "look_distance/2" or "/4" or "/8", etc.  If we find that one
-        # of these secondary locations is free, we assume it's because
-        # there was recently a minor collection; so we reset
-        # look_distance to 0 and start again from the lowest locations.
-        length = len(self.handles)
-        for d in range(self.look_distance, length):
-            if self.handles[d]() is None:
-                self.look_distance = d + 1
-                return d
-            s = _reduced_value(d)
-            if self.handles[s]() is None:
-                break
-        # restart from the beginning
-        for d in range(0, length):
-            if self.handles[d]() is None:
-                self.look_distance = d + 1
-                return d
-        # full! extend, but don't use '+=' here
-        self.handles = self.handles + [dead_ref] * (length // 3 + 5)
-        self.look_distance = length + 1
-        return length
+        # (this algorithm should be amortized constant-time)
+        # get the next 'free_list' entry, if any
+        free_list = self.free_list
+        try:
+            return free_list.pop()
+        except IndexError:
+            pass
+        # slow path: collect all now-free handles in 'free_list'
+        handles = self.handles
+        for i in range(len(handles)):
+            if handles[i]() is None:
+                free_list.append(i)
+        # double the size of the self.handles list, but don't do that
+        # if there are more than 66% of handles free already
+        if len(free_list) * 3 < len(handles) * 2:
+            free_list.extend(range(len(handles), len(handles) * 2))
+            # don't use '+=' on 'self.handles'
+            self.handles = handles = handles + [dead_ref] * len(handles)
+        #
+        return free_list.pop()
 
     def add_handle(self, content):
         index = self.reserve_next_handle_index()
diff --git a/rpython/rlib/test/test_rweaklist.py 
b/rpython/rlib/test/test_rweaklist.py
--- a/rpython/rlib/test/test_rweaklist.py
+++ b/rpython/rlib/test/test_rweaklist.py
@@ -1,20 +1,5 @@
 import gc
-from rpython.rlib.rweaklist import RWeakListMixin, _reduced_value as 
reduced_value
-
-
-def test_reduced_value():
-    assert reduced_value(0) == 0
-    assert reduced_value(1) == 0
-    assert reduced_value(2) == 1
-    assert reduced_value(3) == 0
-    assert reduced_value(4) == 2
-    assert reduced_value(5) == 1
-    assert reduced_value(6) == 3
-    assert reduced_value(7) == 0
-    assert reduced_value(8) == 4
-    assert reduced_value(9) == 2
-    assert reduced_value(10) == 5
-    assert reduced_value(11) == 1
+from rpython.rlib.rweaklist import RWeakListMixin, INITIAL_SIZE
 
 
 class A(object):
@@ -25,29 +10,30 @@
     a1 = A(); a2 = A()
     wlist = RWeakListMixin(); wlist.initialize()
     i = wlist.add_handle(a1)
-    assert i == 0
+    assert i == INITIAL_SIZE - 1
     i = wlist.reserve_next_handle_index()
-    assert i == 1
+    assert i == INITIAL_SIZE - 2
     wlist.store_handle(i, a2)
-    assert wlist.fetch_handle(0) is a1
-    assert wlist.fetch_handle(1) is a2
+    assert wlist.fetch_handle(INITIAL_SIZE - 1) is a1
+    assert wlist.fetch_handle(INITIAL_SIZE - 2) is a2
     #
     del a2
     for i in range(5):
         gc.collect()
-        if wlist.fetch_handle(1) is None:
+        if wlist.fetch_handle(INITIAL_SIZE - 2) is None:
             break
     else:
-        raise AssertionError("handle(1) did not disappear")
-    assert wlist.fetch_handle(0) is a1
+        raise AssertionError("second handle() did not disappear")
+    assert wlist.fetch_handle(INITIAL_SIZE - 1) is a1
 
 def test_reuse():
     alist = [A() for i in range(200)]
     wlist = RWeakListMixin(); wlist.initialize()
+    mapping = []
     for i in range(200):
         j = wlist.reserve_next_handle_index()
-        assert j == i
-        wlist.store_handle(i, alist[i])
+        mapping.append(j)
+        wlist.store_handle(j, alist[i])
     #
     del alist[1::2]
     del alist[1::2]
@@ -57,8 +43,8 @@
     for i in range(5):
         gc.collect()
     #
-    for i in range(200):
-        a = wlist.fetch_handle(i)
+    for i, j in enumerate(mapping):
+        a = wlist.fetch_handle(j)
         if i % 32 == 0:
             assert a is alist[i // 32]
         else:
@@ -67,6 +53,7 @@
     maximum = -1
     for i in range(200):
         j = wlist.reserve_next_handle_index()
+        assert wlist.fetch_handle(j) is None
         maximum = max(maximum, j)
         wlist.store_handle(j, A())
-    assert maximum <= 240
+    assert maximum < 256
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to