[pypy-commit] pypy improve-heap-caching-tracing: cache the length of arrays

cfbolz noreply at buildbot.pypy.org
Wed Sep 7 11:02:02 CEST 2011


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: improve-heap-caching-tracing
Changeset: r47122:e5b582b1252b
Date: 2011-09-06 14:03 +0200
http://bitbucket.org/pypy/pypy/changeset/e5b582b1252b/

Log:	cache the length of arrays

diff --git a/pypy/jit/metainterp/heapcache.py b/pypy/jit/metainterp/heapcache.py
--- a/pypy/jit/metainterp/heapcache.py
+++ b/pypy/jit/metainterp/heapcache.py
@@ -18,6 +18,8 @@
         # heap array cache
         # maps descrs to {index: {from_box: to_box}} dicts
         self.heap_array_cache = {}
+        # cache the length of arrays
+        self.length_cache = {}
 
     def invalidate_caches(self, opnum, descr):
         if opnum == rop.SETFIELD_GC:
@@ -57,6 +59,10 @@
     def new(self, box):
         self.new_boxes[box] = None
 
+    def new_array(self, box, lengthbox):
+        self.new(box)
+        self.arraylen_now_known(box, lengthbox)
+
     def getfield(self, box, descr):
         d = self.heap_cache.get(descr, None)
         if d:
@@ -112,23 +118,26 @@
         indexcache = cache.get(index, None)
         cache[index] = self._do_write_with_aliasing(indexcache, box, valuebox)
 
+    def arraylen(self, box):
+        return self.length_cache.get(box, None)
+
+    def arraylen_now_known(self, box, lengthbox):
+        self.length_cache[box] = lengthbox
+
+    def _replace_box(self, d, oldbox, newbox):
+        new_d = {}
+        for frombox, tobox in d.iteritems():
+            if frombox is oldbox:
+                frombox = newbox
+            if tobox is oldbox:
+                tobox = newbox
+            new_d[frombox] = tobox
+        return new_d
+
     def replace_box(self, oldbox, newbox):
         for descr, d in self.heap_cache.iteritems():
-            new_d = {}
-            for frombox, tobox in d.iteritems():
-                if frombox is oldbox:
-                    frombox = newbox
-                if tobox is oldbox:
-                    tobox = newbox
-                new_d[frombox] = tobox
-            self.heap_cache[descr] = new_d
+            self.heap_cache[descr] = self._replace_box(d, oldbox, newbox)
         for descr, d in self.heap_array_cache.iteritems():
             for index, cache in d.iteritems():
-                new_cache = {}
-                for frombox, tobox in cache.iteritems():
-                    if frombox is oldbox:
-                        frombox = newbox
-                    if tobox is oldbox:
-                        tobox = newbox
-                    new_cache[frombox] = tobox
-                d[index] = new_cache
+                d[index] = self._replace_box(cache, oldbox, newbox)
+        self.length_cache = self._replace_box(self.length_cache, oldbox, newbox)
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -394,9 +394,9 @@
 ##        self.execute(rop.SUBCLASSOF, box1, box2)
 
     @arguments("descr", "box")
-    def opimpl_new_array(self, itemsizedescr, countbox):
-        resbox = self.execute_with_descr(rop.NEW_ARRAY, itemsizedescr, countbox)
-        self.metainterp.heapcache.new(resbox)
+    def opimpl_new_array(self, itemsizedescr, lengthbox):
+        resbox = self.execute_with_descr(rop.NEW_ARRAY, itemsizedescr, lengthbox)
+        self.metainterp.heapcache.new_array(resbox, lengthbox)
         return resbox
 
     @arguments("box", "descr", "box")
@@ -456,7 +456,12 @@
 
     @arguments("box", "descr")
     def opimpl_arraylen_gc(self, arraybox, arraydescr):
-        return self.execute_with_descr(rop.ARRAYLEN_GC, arraydescr, arraybox)
+        lengthbox = self.metainterp.heapcache.arraylen(arraybox)
+        if lengthbox is None:
+            lengthbox = self.execute_with_descr(
+                    rop.ARRAYLEN_GC, arraydescr, arraybox)
+            self.metainterp.heapcache.arraylen_now_known(arraybox, lengthbox)
+        return lengthbox
 
     @arguments("orgpc", "box", "descr", "box")
     def opimpl_check_neg_index(self, orgpc, arraybox, arraydescr, indexbox):
@@ -465,10 +470,9 @@
         negbox = self.implement_guard_value(orgpc, negbox)
         if negbox.getint():
             # the index is < 0; add the array length to it
-            lenbox = self.metainterp.execute_and_record(
-                rop.ARRAYLEN_GC, arraydescr, arraybox)
+            lengthbox = self.opimpl_arraylen_gc(arraybox, arraydescr)
             indexbox = self.metainterp.execute_and_record(
-                rop.INT_ADD, None, indexbox, lenbox)
+                rop.INT_ADD, None, indexbox, lengthbox)
         return indexbox
 
     @arguments("descr", "descr", "descr", "descr", "box")
@@ -721,7 +725,7 @@
     def opimpl_arraylen_vable(self, pc, box, fdescr, adescr):
         if self._nonstandard_virtualizable(pc, box):
             arraybox = self._opimpl_getfield_gc_any(box, fdescr)
-            return self.execute_with_descr(rop.ARRAYLEN_GC, adescr, arraybox)
+            return self.opimpl_arraylen_gc(arraybox, adescr)
         vinfo = self.metainterp.jitdriver_sd.virtualizable_info
         virtualizable_box = self.metainterp.virtualizable_boxes[-1]
         virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
diff --git a/pypy/jit/metainterp/test/test_heapcache.py b/pypy/jit/metainterp/test/test_heapcache.py
--- a/pypy/jit/metainterp/test/test_heapcache.py
+++ b/pypy/jit/metainterp/test/test_heapcache.py
@@ -6,6 +6,8 @@
 box2 = object()
 box3 = object()
 box4 = object()
+lengthbox1 = object()
+lengthbox2 = object()
 descr1 = object()
 descr2 = object()
 descr3 = object()
@@ -206,6 +208,15 @@
         assert h.getarrayitem(box3, descr1, index1) is box4
         assert h.getarrayitem(box1, descr1, index1) is box3 # box1 and box3 cannot alias
 
+    def test_length_cache(self):
+        h = HeapCache()
+        h.new_array(box1, lengthbox1)
+        assert h.arraylen(box1) is lengthbox1
+
+        assert h.arraylen(box2) is None
+        h.arraylen_now_known(box2, lengthbox2)
+        assert h.arraylen(box2) is lengthbox2
+
 
     def test_invalidate_cache(self):
         h = HeapCache()
@@ -252,14 +263,20 @@
         h = HeapCache()
         h.setarrayitem(box1, descr1, index1, box2)
         h.setarrayitem(box1, descr2, index1, box3)
+        h.arraylen_now_known(box1, lengthbox1)
         h.setarrayitem(box2, descr1, index2, box1)
         h.setarrayitem(box3, descr2, index2, box1)
         h.setarrayitem(box2, descr3, index2, box3)
         h.replace_box(box1, box4)
         assert h.getarrayitem(box1, descr1, index1) is None
         assert h.getarrayitem(box1, descr2, index1) is None
+        assert h.arraylen(box1) is None
+        assert h.arraylen(box4) is lengthbox1
         assert h.getarrayitem(box4, descr1, index1) is box2
         assert h.getarrayitem(box4, descr2, index1) is box3
         assert h.getarrayitem(box2, descr1, index2) is box4
         assert h.getarrayitem(box3, descr2, index2) is box4
         assert h.getarrayitem(box2, descr3, index2) is box3
+
+        h.replace_box(lengthbox1, lengthbox2)
+        assert h.arraylen(box4) is lengthbox2
diff --git a/pypy/jit/metainterp/test/test_tracingopts.py b/pypy/jit/metainterp/test/test_tracingopts.py
--- a/pypy/jit/metainterp/test/test_tracingopts.py
+++ b/pypy/jit/metainterp/test/test_tracingopts.py
@@ -469,3 +469,17 @@
         assert res == 2 * -7 + 2 * -8
         self.check_operations_history(getarrayitem_gc=0)
 
+    def test_length_caching(self):
+        class Gbl(object):
+            pass
+        g = Gbl()
+        g.a = [0] * 7
+        def fn(n):
+            a = g.a
+            res = len(a) + len(a)
+            a1 = [0] * n
+            g.a = a1
+            return len(a1) + res
+        res = self.interp_operations(fn, [7])
+        assert res == 7 * 3
+        self.check_operations_history(arraylen_gc=1)


More information about the pypy-commit mailing list