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