[pypy-commit] pypy default: don't add superfluous QUASIIMMUT_FIELD during tracing (I've seen their creation

cfbolz pypy.commits at gmail.com
Thu Jun 30 08:42:32 EDT 2016


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: 
Changeset: r85471:b116f09c4e9d
Date: 2016-06-30 14:41 +0200
http://bitbucket.org/pypy/pypy/changeset/b116f09c4e9d/

Log:	don't add superfluous QUASIIMMUT_FIELD during tracing (I've seen
	their creation take 2% of the full execution time during profiling)

diff --git a/rpython/jit/metainterp/heapcache.py b/rpython/jit/metainterp/heapcache.py
--- a/rpython/jit/metainterp/heapcache.py
+++ b/rpython/jit/metainterp/heapcache.py
@@ -57,10 +57,16 @@
         self.cache_anything = {}
         self.cache_seen_allocation = {}
 
+        # set of boxes that we've seen a quasi-immut for the field on. cleared
+        # on writes to the field.
+        self.quasiimmut_seen = None
+
     def _clear_cache_on_write(self, seen_allocation_of_target):
         if not seen_allocation_of_target:
             self.cache_seen_allocation.clear()
         self.cache_anything.clear()
+        if self.quasiimmut_seen is not None:
+            self.quasiimmut_seen.clear()
 
     def _seen_alloc(self, ref_box):
         if not isinstance(ref_box, RefFrontendOp):
@@ -92,6 +98,8 @@
     def invalidate_unescaped(self):
         self._invalidate_unescaped(self.cache_anything)
         self._invalidate_unescaped(self.cache_seen_allocation)
+        if self.quasiimmut_seen is not None:
+            self.quasiimmut_seen.clear()
 
     def _invalidate_unescaped(self, d):
         for ref_box in d.keys():
@@ -484,3 +492,18 @@
         if isinstance(oldbox, FrontendOp) and isinstance(newbox, Const):
             assert newbox.same_constant(constant_from_op(oldbox))
             oldbox.set_replaced_with_const()
+
+    def is_quasi_immut_known(self, fielddescr, box):
+        cache = self.heap_cache.get(fielddescr, None)
+        if cache is not None and cache.quasiimmut_seen is not None:
+            return box in cache.quasiimmut_seen
+        return False
+
+    def quasi_immut_now_known(self, fielddescr, box):
+        cache = self.heap_cache.get(fielddescr, None)
+        if cache is None:
+            cache = self.heap_cache[fielddescr] = CacheEntry(self)
+        if cache.quasiimmut_seen is not None:
+            cache.quasiimmut_seen[box] = None
+        else:
+            cache.quasiimmut_seen = {box: None}
diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -829,8 +829,11 @@
                                        mutatefielddescr, orgpc):
         from rpython.jit.metainterp.quasiimmut import QuasiImmutDescr
         cpu = self.metainterp.cpu
+        if self.metainterp.heapcache.is_quasi_immut_known(fielddescr, box):
+            return
         descr = QuasiImmutDescr(cpu, box.getref_base(), fielddescr,
                                 mutatefielddescr)
+        self.metainterp.heapcache.quasi_immut_now_known(fielddescr, box)
         self.metainterp.history.record(rop.QUASIIMMUT_FIELD, [box],
                                        None, descr=descr)
         self.metainterp.generate_guard(rop.GUARD_NOT_INVALIDATED,
diff --git a/rpython/jit/metainterp/test/test_heapcache.py b/rpython/jit/metainterp/test/test_heapcache.py
--- a/rpython/jit/metainterp/test/test_heapcache.py
+++ b/rpython/jit/metainterp/test/test_heapcache.py
@@ -797,3 +797,46 @@
         h.class_now_known(box1)     # interaction of the two families of flags
         assert not h.is_unescaped(box1)
         assert h.is_likely_virtual(box1)
+
+    def test_quasiimmut_seen(self):
+        h = HeapCache()
+        box1 = RefFrontendOp(1)
+        box2 = RefFrontendOp(2)
+        box3 = RefFrontendOp(3)
+        box4 = RefFrontendOp(4)
+        assert not h.is_quasi_immut_known(descr1, box1)
+        assert not h.is_quasi_immut_known(descr1, box2)
+        assert not h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
+        h.quasi_immut_now_known(descr1, box1)
+        assert h.is_quasi_immut_known(descr1, box1)
+        assert not h.is_quasi_immut_known(descr1, box2)
+        assert not h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
+        h.quasi_immut_now_known(descr1, box2)
+        assert h.is_quasi_immut_known(descr1, box1)
+        assert h.is_quasi_immut_known(descr1, box2)
+        assert not h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
+        h.quasi_immut_now_known(descr2, box3)
+        assert h.is_quasi_immut_known(descr1, box1)
+        assert h.is_quasi_immut_known(descr1, box2)
+        assert h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
+        h.quasi_immut_now_known(descr2, box4)
+        assert h.is_quasi_immut_known(descr1, box1)
+        assert h.is_quasi_immut_known(descr1, box2)
+        assert h.is_quasi_immut_known(descr2, box3)
+        assert h.is_quasi_immut_known(descr2, box4)
+
+        # invalidate the descr1 cache
+
+        h.setfield(box1, box3, descr1)
+        assert not h.is_quasi_immut_known(descr1, box1)
+        assert not h.is_quasi_immut_known(descr1, box2)
+
+        # a call invalidates everything
+        h.invalidate_caches(
+            rop.CALL_N, FakeCallDescr(FakeEffectinfo.EF_CAN_RAISE), [])
+        assert not h.is_quasi_immut_known(descr2, box3)
+        assert not h.is_quasi_immut_known(descr2, box4)
diff --git a/rpython/jit/metainterp/test/test_tracingopts.py b/rpython/jit/metainterp/test/test_tracingopts.py
--- a/rpython/jit/metainterp/test/test_tracingopts.py
+++ b/rpython/jit/metainterp/test/test_tracingopts.py
@@ -512,6 +512,32 @@
         assert res == 4 * 7
         self.check_operations_history(getfield_gc_i=2, getfield_gc_r=2)
 
+    def test_heap_caching_quasi_immutable(self):
+        class A:
+            _immutable_fields_ = ['x?']
+        a1 = A()
+        a1.x = 5
+        a2 = A()
+        a2.x = 7
+
+        @jit.elidable
+        def get(n):
+            if n > 0:
+                return a1
+            return a2
+
+        def g(a):
+            return a.x
+
+        def fn(n):
+            jit.promote(n)
+            a = get(n)
+            return g(a) + a.x
+        res = self.interp_operations(fn, [7])
+        assert res == 10
+        self.check_operations_history(quasiimmut_field=1)
+
+
     def test_heap_caching_multiple_tuples(self):
         class Gbl(object):
             pass


More information about the pypy-commit mailing list