Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit