Author: Armin Rigo <ar...@tunes.org>
Branch: jit-counter
Changeset: r67745:22b67a551eb0
Date: 2013-10-30 19:12 +0100
http://bitbucket.org/pypy/pypy/changeset/22b67a551eb0/

Log:    in-progress on the guard counters now

diff --git a/rpython/jit/codewriter/longlong.py 
b/rpython/jit/codewriter/longlong.py
--- a/rpython/jit/codewriter/longlong.py
+++ b/rpython/jit/codewriter/longlong.py
@@ -25,6 +25,7 @@
     getfloatstorage = lambda x: x
     getrealfloat    = lambda x: x
     gethash         = compute_hash
+    gethash_fast    = longlong2float.float2longlong
     is_longlong     = lambda TYPE: False
 
     # -------------------------------------
@@ -40,6 +41,7 @@
     getfloatstorage = longlong2float.float2longlong
     getrealfloat    = longlong2float.longlong2float
     gethash         = lambda xll: rarithmetic.intmask(xll - (xll >> 32))
+    gethash_fast    = gethash
     is_longlong     = lambda TYPE: (TYPE is lltype.SignedLongLong or
                                     TYPE is lltype.UnsignedLongLong)
 
diff --git a/rpython/jit/metainterp/compile.py 
b/rpython/jit/metainterp/compile.py
--- a/rpython/jit/metainterp/compile.py
+++ b/rpython/jit/metainterp/compile.py
@@ -483,11 +483,7 @@
     pass
 
 class ResumeGuardDescr(ResumeDescr):
-    _counter = 0        # on a GUARD_VALUE, there is one counter per value;
-    _counters = None    # they get stored in _counters then.
-
     # this class also gets the following attributes stored by resume.py code
-
     # XXX move all of unused stuff to guard_op, now that we can have
     #     a separate class, so it does not survive that long
     rd_snapshot = None
@@ -498,18 +494,26 @@
     rd_virtuals = None
     rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
 
-    CNT_BASE_MASK  =  0x0FFFFFFF     # the base counter value
-    CNT_BUSY_FLAG  =  0x10000000     # if set, busy tracing from the guard
-    CNT_TYPE_MASK  =  0x60000000     # mask for the type
+    status = 0
 
-    CNT_INT        =  0x20000000
-    CNT_REF        =  0x40000000
-    CNT_FLOAT      =  0x60000000
+    ST_BUSY_FLAG    = 0x01     # if set, busy tracing from the guard
+    ST_TYPE_MASK    = 0x06     # mask for the type (TY_xxx)
+    ST_SHIFT        = 3        # in "status >> ST_SHIFT" is stored:
+                               # - if TY_NONE, the jitcounter index directly
+                               # - otherwise, the guard_value failarg index
+    TY_NONE         = 0x00
+    TY_INT          = 0x02
+    TY_REF          = 0x04
+    TY_FLOAT        = 0x06
 
-    def store_final_boxes(self, guard_op, boxes):
+    def store_final_boxes(self, guard_op, boxes, metainterp_sd):
         guard_op.setfailargs(boxes)
         self.rd_count = len(boxes)
         self.guard_opnum = guard_op.getopnum()
+        #
+        if metainterp_sd.warmrunnerdesc is not None:   # for tests
+            jitcounter = metainterp_sd.warmrunnerdesc.jitcounter
+            self.status = jitcounter.fetch_next_index() << self.ST_SHIFT
 
     def make_a_counter_per_value(self, guard_value_op):
         assert guard_value_op.getopnum() == rop.GUARD_VALUE
@@ -519,18 +523,15 @@
         except ValueError:
             return     # xxx probably very rare
         else:
-            if i > self.CNT_BASE_MASK:
-                return    # probably never, but better safe than sorry
             if box.type == history.INT:
-                cnt = self.CNT_INT
+                ty = self.TY_INT
             elif box.type == history.REF:
-                cnt = self.CNT_REF
+                ty = self.TY_REF
             elif box.type == history.FLOAT:
-                cnt = self.CNT_FLOAT
+                ty = self.TY_FLOAT
             else:
                 assert 0, box.type
-            assert cnt > self.CNT_BASE_MASK
-            self._counter = cnt | i
+            self.status = ty | (i << self.ST_SHIFT)
 
     def handle_fail(self, deadframe, metainterp_sd, jitdriver_sd):
         if self.must_compile(deadframe, metainterp_sd, jitdriver_sd):
@@ -557,65 +558,55 @@
     _trace_and_compile_from_bridge._dont_inline_ = True
 
     def must_compile(self, deadframe, metainterp_sd, jitdriver_sd):
-        trace_eagerness = jitdriver_sd.warmstate.trace_eagerness
+        jitcounter = metainterp_sd.warmrunnerdesc.jitcounter
         #
-        if self._counter <= self.CNT_BASE_MASK:
-            # simple case: just counting from 0 to trace_eagerness
-            self._counter += 1
-            return self._counter >= trace_eagerness
+        if self.status & (self.ST_BUSY_FLAG | self.ST_TYPE_MASK) == 0:
+            # common case: this is not a guard_value, and we are not
+            # already busy tracing.  The rest of self.status stores a
+            # valid per-guard index in the jitcounter.
+            index = self.status >> self.ST_SHIFT
         #
         # do we have the BUSY flag?  If so, we're tracing right now, e.g. in an
         # outer invocation of the same function, so don't trace again for now.
-        elif self._counter & self.CNT_BUSY_FLAG:
+        elif self.status & self.ST_BUSY_FLAG:
             return False
         #
-        else: # we have a GUARD_VALUE that fails.  Make a _counters instance
-            # (only now, when the guard is actually failing at least once),
-            # and use it to record some statistics about the failing values.
-            index = self._counter & self.CNT_BASE_MASK
-            typetag = self._counter & self.CNT_TYPE_MASK
-            counters = self._counters
-            if typetag == self.CNT_INT:
-                intvalue = metainterp_sd.cpu.get_int_value(
-                    deadframe, index)
-                if counters is None:
-                    self._counters = counters = ResumeGuardCountersInt()
-                else:
-                    assert isinstance(counters, ResumeGuardCountersInt)
-                counter = counters.see_int(intvalue)
+        else:    # we have a GUARD_VALUE that fails.
+            from rpython.rlib.objectmodel import current_object_addr_as_int
+
+            index = self.status >> self.ST_SHIFT
+            typetag = self.status & self.ST_TYPE_MASK
+
+            # fetch the actual value of the guard_value, possibly turning
+            # it to an integer
+            if typetag == self.TY_INT:
+                intval = metainterp_sd.cpu.get_int_value(deadframe, index)
             elif typetag == self.CNT_REF:
-                refvalue = metainterp_sd.cpu.get_ref_value(
-                    deadframe, index)
-                if counters is None:
-                    self._counters = counters = ResumeGuardCountersRef()
-                else:
-                    assert isinstance(counters, ResumeGuardCountersRef)
-                counter = counters.see_ref(refvalue)
+                refval = metainterp_sd.cpu.get_ref_value(deadframe, index)
+                intval = current_object_addr_as_int(refval)
             elif typetag == self.CNT_FLOAT:
-                floatvalue = metainterp_sd.cpu.get_float_value(
-                    deadframe, index)
-                if counters is None:
-                    self._counters = counters = ResumeGuardCountersFloat()
-                else:
-                    assert isinstance(counters, ResumeGuardCountersFloat)
-                counter = counters.see_float(floatvalue)
+                floatval = metainterp_sd.cpu.get_float_value(deadframe, index)
+                intval = longlong.gethash_fast(floatval)
             else:
                 assert 0, typetag
-            return counter >= trace_eagerness
+
+            hash = (current_object_addr_as_int(self) * 777767777 +
+                    intval * 1442968193)
+            index = jitcounter.get_index(hash)
+        #
+        increment = jitdriver_sd.warmstate.increment_trace_eagerness
+        return jitcounter.tick(index, increment)
 
     def start_compiling(self):
         # start tracing and compiling from this guard.
-        self._counter |= self.CNT_BUSY_FLAG
+        self.status |= self.ST_BUSY_FLAG
 
     def done_compiling(self):
-        # done tracing and compiling from this guard.  Either the bridge has
-        # been successfully compiled, in which case whatever value we store
-        # in self._counter will not be seen any more, or not, in which case
-        # we should reset the counter to 0, in order to wait a bit until the
-        # next attempt.
-        if self._counter >= 0:
-            self._counter = 0
-        self._counters = None
+        # done tracing and compiling from this guard.  Note that if the
+        # bridge has not been successfully compiled, the jitcounter for
+        # it was reset to 0 already by jitcounter.tick() and not
+        # incremented at all as long as ST_BUSY_FLAG was set.
+        self.status &= ~self.ST_BUSY_FLAG
 
     def compile_and_attach(self, metainterp, new_loop):
         # We managed to create a bridge.  Attach the new operations
@@ -745,69 +736,6 @@
         return res
 
 
-class AbstractResumeGuardCounters(object):
-    # Completely custom algorithm for now: keep 5 pairs (value, counter),
-    # and when we need more, we discard the middle pair (middle in the
-    # current value of the counter).  That way, we tend to keep the
-    # values with a high counter, but also we avoid always throwing away
-    # the most recently added value.  **THIS ALGO MUST GO AWAY AT SOME POINT**
-    pass
-
-def _see(self, newvalue):
-    # find and update an existing counter
-    unused = -1
-    for i in range(5):
-        cnt = self.counters[i]
-        if cnt:
-            if self.values[i] == newvalue:
-                cnt += 1
-                self.counters[i] = cnt
-                return cnt
-        else:
-            unused = i
-    # not found.  Use a previously unused entry, if there is one
-    if unused >= 0:
-        self.counters[unused] = 1
-        self.values[unused] = newvalue
-        return 1
-    # no unused entry.  Overwrite the middle one.  Computed with indices
-    # a, b, c meaning the highest, second highest, and third highest
-    # entries.
-    a = 0
-    b = c = -1
-    for i in range(1, 5):
-        if self.counters[i] > self.counters[a]:
-            c = b
-            b = a
-            a = i
-        elif b < 0 or self.counters[i] > self.counters[b]:
-            c = b
-            b = i
-        elif c < 0 or self.counters[i] > self.counters[c]:
-            c = i
-    self.counters[c] = 1
-    self.values[c] = newvalue
-    return 1
-
-class ResumeGuardCountersInt(AbstractResumeGuardCounters):
-    def __init__(self):
-        self.counters = [0] * 5
-        self.values = [0] * 5
-    see_int = func_with_new_name(_see, 'see_int')
-
-class ResumeGuardCountersRef(AbstractResumeGuardCounters):
-    def __init__(self):
-        self.counters = [0] * 5
-        self.values = [history.ConstPtr.value] * 5
-    see_ref = func_with_new_name(_see, 'see_ref')
-
-class ResumeGuardCountersFloat(AbstractResumeGuardCounters):
-    def __init__(self):
-        self.counters = [0] * 5
-        self.values = [longlong.ZEROF] * 5
-    see_float = func_with_new_name(_see, 'see_float')
-
-
 class ResumeFromInterpDescr(ResumeDescr):
     def __init__(self, original_greenkey):
         self.original_greenkey = original_greenkey
diff --git a/rpython/jit/metainterp/counter.py 
b/rpython/jit/metainterp/counter.py
--- a/rpython/jit/metainterp/counter.py
+++ b/rpython/jit/metainterp/counter.py
@@ -22,6 +22,7 @@
                                        flavor='raw', zero=True,
                                        track_allocation=False)
         self.celltable = [None] * size
+        self._nextindex = 0
 
     def compute_threshold(self, threshold):
         """Return the 'increment' value corresponding to the given number."""
@@ -37,6 +38,11 @@
         return intmask(r_uint32(hash) >> self.shift)
     get_index._always_inline_ = True
 
+    def fetch_next_index(self):
+        result = self._nextindex
+        self._nextindex = (result + 1) & self.get_index(-1)
+        return result
+
     def tick(self, index, increment):
         counter = float(self.timetable[index]) + increment
         if counter < 1.0:
diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py 
b/rpython/jit/metainterp/optimizeopt/optimizer.py
--- a/rpython/jit/metainterp/optimizeopt/optimizer.py
+++ b/rpython/jit/metainterp/optimizeopt/optimizer.py
@@ -584,7 +584,7 @@
                 raise resume.TagOverflow
         except resume.TagOverflow:
             raise compile.giveup()
-        descr.store_final_boxes(op, newboxes)
+        descr.store_final_boxes(op, newboxes, self.metainterp_sd)
         #
         if op.getopnum() == rop.GUARD_VALUE:
             if self.getvalue(op.getarg(0)) in self.bool_boxes:
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py 
b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -312,7 +312,7 @@
     def __init__(self, metainterp_sd=None, original_greenkey=None):
         self.metainterp_sd = metainterp_sd
         self.original_greenkey = original_greenkey
-    def store_final_boxes(self, op, boxes):
+    def store_final_boxes(self, op, boxes, metainterp_sd):
         op.setfailargs(boxes)
     def __eq__(self, other):
         return type(self) is type(other)      # xxx obscure
diff --git a/rpython/jit/metainterp/test/test_counter.py 
b/rpython/jit/metainterp/test/test_counter.py
--- a/rpython/jit/metainterp/test/test_counter.py
+++ b/rpython/jit/metainterp/test/test_counter.py
@@ -8,6 +8,11 @@
         index = jc.get_index(hash)
         assert index == (hash >> (32 - 7))
 
+def test_fetch_next_index():
+    jc = JitCounter(size=4)
+    lst = [jc.fetch_next_index() for i in range(10)]
+    assert lst == [0, 1, 2, 3, 0, 1, 2, 3, 0, 1]
+
 def test_tick():
     jc = JitCounter()
     incr = jc.compute_threshold(4)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to