[pypy-commit] pypy default: Document the JitCounter and BaseJitCell classes.
arigo
noreply at buildbot.pypy.org
Sun Aug 31 08:59:35 CEST 2014
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r73216:cd1ea9c54b1b
Date: 2014-08-31 08:59 +0200
http://bitbucket.org/pypy/pypy/changeset/cd1ea9c54b1b/
Log: Document the JitCounter and BaseJitCell classes.
These two classes have some usage patterns and invariants that are
worth writing down precisely.
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
@@ -7,13 +7,78 @@
assert r_uint32.BITS == 32
UINT32MAX = 2 ** 32 - 1
-# keep in sync with the C code in pypy__decay_jit_counters
+# keep in sync with the C code in pypy__decay_jit_counters below
ENTRY = lltype.Struct('timetable_entry',
('times', lltype.FixedSizeArray(rffi.FLOAT, 5)),
('subhashes', lltype.FixedSizeArray(rffi.USHORT, 5)))
class JitCounter:
+ """A process translated with the JIT contains one prebuilt instance
+ of this class. It is used for three things:
+
+ * It maps greenkey hashes to counters, to know when we have seen this
+ greenkey enough to reach the 'threshold' or 'function_threshold'
+ parameters. This is done in a lossy way by a fixed-size 'timetable'.
+
+ * It handles the counters on the failing guards, for 'trace_eagerness'.
+ This is done in the same 'timetable'.
+
+ * It records the JitCell objects that are created when we compile
+ a loop, in a non-lossy dictionary-like strurcture. This is done
+ in the 'celltable'.
+
+ The 'timetable' is a table of DEFAULT_SIZE entries, each of which
+ containing 5 entries. From a hash value, we use the index number
+ '_get_index(hash)', and then we look in all five entries for a
+ matching '_get_subhash(hash)'. The five entries are roughly kept
+ sorted by decreasing recorded time. The hash value itself should be
+ computed accordingly: we only use bits 21:32 for _get_index and
+ bits 0:16 for _get_subhash. (This organization is "probably good"
+ to get not-too-random behavior; another motivation for it was for
+ the STM branch, to avoid pointless conflicts between threads.)
+
+ The time value stored in the timetable is a (short-precision)
+ floating-point number. The idea is that a value of 0.0 means
+ absent, and values go up to the maximum of 1.0.
+
+ 'compute_threshold(threshold)' returns basically the fraction
+ 1.0/threshold, corresponding to the 'increment' value for the
+ following APIs.
+
+ 'tick(hash, increment)' adds 'increment' to the time value stored
+ with the 'hash'. Remember that only bits 0:16,21:32 of the hash
+ are used; in case of collision between two hashes, they will grow
+ twice as fast, because each tick() call will contribute to the
+ colliding time value.
+
+ 'fetch_next_hash()' returns a "random" hash value suitable for
+ using in tick() later. Used when compiling guards; when the
+ guard actually fails, we'll tick() the guard's stored random hash.
+
+ 'reset(hash)', 'change_current_fraction(hash, new_time_value)'
+ change the time value associated with a hash. The former resets
+ it to zero, and the latter changes it to the given value (which
+ should be a value close to 1.0).
+
+ 'set_decay(decay)', 'decay_all_counters()' is used to globally
+ reduce all the stored time values. They all get multiplied by
+ a fraction close to (but smaller than) 1.0, computed from the
+ 'decay' parameter.
+
+ 'install_new_cell(hash, newcell)' adds the new JitCell to the
+ celltable, at the index given by 'hash' (bits 21:32). Unlike
+ the timetable, the celltable stores a linked list of JitCells
+ for every entry, and so it is not lossy.
+
+ 'lookup_chain(hash)' returns the first JitCell at 'hash'. You can
+ then walk the chain by following the '.next' attributes until you
+ reach None.
+
+ 'cleanup_chain(hash)' resets the timetable's 'hash' entry and
+ cleans up the celltable at 'hash'. It removes those JitCells
+ for which 'cell.should_remove_jitcell()' returns True.
+ """
DEFAULT_SIZE = 2048
def __init__(self, size=DEFAULT_SIZE, translator=None):
diff --git a/rpython/jit/metainterp/warmstate.py b/rpython/jit/metainterp/warmstate.py
--- a/rpython/jit/metainterp/warmstate.py
+++ b/rpython/jit/metainterp/warmstate.py
@@ -129,6 +129,49 @@
JC_TEMPORARY = 0x04
class BaseJitCell(object):
+ """Subclasses of BaseJitCell are used in tandem with the single
+ JitCounter instance to record places in the JIT-tracked user program
+ where something particular occurs with the JIT. For some
+ 'greenkeys' (e.g. Python bytecode position), we create one instance
+ of JitCell and attach it to that greenkey. This is implemented
+ with jitcounter.install_new_cell(), but conceptually you can think
+ about JitCode instances as attached to some locations of the
+ app-level Python code.
+
+ We create subclasses of BaseJitCell --one per jitdriver-- so that
+ they can store greenkeys of different types.
+
+ Note that we don't create a JitCell the first time we see a given
+ greenkey position in the interpreter. At first, we only hash the
+ greenkey and use that in the JitCounter to record the number of
+ times we have seen it. We only create a JitCell when the
+ JitCounter's total time value reaches 1.0 and we are starting to
+ JIT-compile.
+
+ A JitCell has a 'wref_procedure_token' that is non-None when we
+ actually have a compiled procedure for that greenkey. (It is a
+ weakref, so that it could later be freed; in this case the JitCell
+ will likely be reclaimed a bit later by 'should_remove_jitcell()'.)
+
+ There are other less-common cases where we also create a JitCell: to
+ record some long-term flags about the greenkey. In general, a
+ JitCell can have any combination of the following flags set:
+
+ JC_TRACING: we are now tracing the loop from this greenkey.
+ We'll likely end up with a wref_procedure_token, soonish.
+
+ JC_TEMPORARY: a "temporary" wref_procedure_token.
+ It's the procedure_token of a dummy loop that simply calls
+ back the interpreter. Used for a CALL_ASSEMBLER where the
+ target was not compiled yet. In this situation we are still
+ ticking the JitCounter for the same hash, until we reach the
+ threshold and start tracing the loop in earnest.
+
+ JC_DONT_TRACE_HERE: when tracing, don't inline calls to
+ this particular function. (We only set this flag when aborting
+ due to a trace too long, so we use the same flag as a hint to
+ also mean "please trace from here as soon as possible".)
+ """
flags = 0 # JC_xxx flags
wref_procedure_token = None
next = None
More information about the pypy-commit
mailing list