Author: Carl Friedrich Bolz <cfb...@gmx.de> Branch: value-profiling Changeset: r78894:9c96634eb883 Date: 2015-08-11 14:44 +0200 http://bitbucket.org/pypy/pypy/changeset/9c96634eb883/
Log: (arigo, cfbolz): use a slightly different approach, both a bit more flexible and safer diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py --- a/pypy/interpreter/pycode.py +++ b/pypy/interpreter/pycode.py @@ -50,14 +50,23 @@ kwargname = varnames[argcount] if code.co_flags & CO_VARKEYWORDS else None return Signature(argnames, varargname, kwargname) +class ValueProf(valueprof.ValueProf): + def is_int(self, w_obj): + from pypy.objspace.std.intobject import W_IntObject + return type(w_obj) is W_IntObject + + def get_int_val(self, w_obj): + from pypy.objspace.std.intobject import W_IntObject + assert isinstance(w_obj, W_IntObject) + return w_obj.intval class PyCode(eval.Code): "CPython-style code objects." _immutable_ = True _immutable_fields_ = ["co_consts_w[*]", "co_names_w[*]", "co_varnames[*]", "co_freevars[*]", "co_cellvars[*]", - "_args_as_cellvars[*]", "vprof"] - + "_args_as_cellvars[*]", "vprofs[*]"] + def __init__(self, space, argcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, freevars, cellvars, @@ -86,8 +95,7 @@ self._signature = cpython_code_signature(self) self._initialize() space.register_code_object(self) - self.vprof = valueprof.ValueProf(self.co_nlocals) - self.printed = [False] * self.co_nlocals + self.vprofs = [ValueProf() for i in range(self.co_nlocals)] def _initialize(self): if self.co_cellvars: diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py --- a/pypy/interpreter/pyframe.py +++ b/pypy/interpreter/pyframe.py @@ -147,22 +147,16 @@ def _getlocal(self, varindex): from pypy.objspace.std.intobject import W_IntObject + if we_are_jitted(): + vprof = self.getcode().vprofs[varindex] + # some careful logic there + if vprof.can_fold_read_int(): + return W_IntObject(vprof.read_constant_int()) + elif vprof.can_fold_read_obj(): + w_res = vprof.try_read_constant_obj() + if w_res is not None: + return w_res w_res = self.locals_cells_stack_w[varindex] - if we_are_jitted(): - vprof = self.getcode().vprof - # some careful logic there - frozen = vprof.freeze() - assert frozen - if vprof.is_variable_constant(varindex): - if vprof.is_variable_int(varindex): - res_prof = vprof.variable_value_int(varindex) - if isinstance(w_res, W_IntObject): - if w_res.intval == res_prof: - return W_IntObject(res_prof) - else: - w_res_prof = vprof.variable_value_object(varindex) - if w_res is w_res_prof: - return w_res_prof return w_res def _setlocal(self, varindex, value): @@ -171,16 +165,8 @@ def _value_profile_local(self, varindex, value): from pypy.objspace.std.intobject import W_IntObject - if we_are_jitted(): - return - vprof = self.pycode.vprof - if vprof.frozen: - return - if isinstance(value, W_IntObject): - times = vprof.see_int(varindex, value.intval) - else: - times = self.pycode.vprof.see_object(varindex, value) - + vprof = self.getcode().vprofs[varindex] + vprof.see_write(value) def mark_as_escaped(self): """ diff --git a/pypy/interpreter/test/test_valueprof.py b/pypy/interpreter/test/test_valueprof.py --- a/pypy/interpreter/test/test_valueprof.py +++ b/pypy/interpreter/test/test_valueprof.py @@ -1,45 +1,85 @@ -from pypy.interpreter.valueprof import ValueProf +from pypy.interpreter.valueprof import * class Value(): pass -def test_simple(): - v = ValueProf(2) - v.see_int(0, 1) - v.see_int(0, 1) - v.see_int(0, 1) - v.see_int(0, 1) - assert v.values_int[0] == 1 - assert v.counters[0] == -4 - v.see_int(0, 5) - v.see_int(0, 5) - v.see_int(0, 5) - v.see_int(0, 5) - v.see_int(0, 5) - assert v.values_int[0] == 5 - assert v.counters[0] == -5 +class ValueInt(Value): + def __init__(self, val): + self.intval = val - val1 = Value() - v.see_object(0, val1) - v.see_object(0, val1) - v.see_object(0, val1) - v.see_object(0, val1) - assert v.values_wref[0]() is val1 - assert v.counters[0] == 4 - v.see_object(0, None) - assert v.counters[0] == 0 +class ValueProf(ValueProf): + def is_int(self, val): + return isinstance(val, ValueInt) -def test_freeze(): - v = ValueProf(2) - v.see_int(0, 1) - v.see_int(0, 1) - v.see_int(0, 1) - v.see_int(0, 1) - v.see_int(0, 1) - v.see_int(0, 1) - v.see_int(0, 1) - v.freeze() - v.see_int(0, 2) - assert v.values_int[0] == 1 + def get_int_val(self, val): + return val.intval + + +def test_int(): + v = ValueProf() + assert v.status == SEEN_NOTHING + v.see_write(ValueInt(1)) + v.see_write(ValueInt(1)) + v.see_write(ValueInt(1)) + v.see_write(ValueInt(1)) + assert v.read_constant_int() == 1 + assert v.status == SEEN_INT + v.see_int(2) + assert v.status == SEEN_TOO_MUCH + v.see_int(1) + assert v.status == SEEN_TOO_MUCH + v.see_int(2) + assert v.status == SEEN_TOO_MUCH + v.see_int(3) + assert v.status == SEEN_TOO_MUCH + + v = ValueProf() + assert v.status == SEEN_NOTHING + v.see_write(ValueInt(1)) + v.see_write(Value()) + assert v.status == SEEN_TOO_MUCH + v.see_write(Value()) + assert v.status == SEEN_TOO_MUCH + + +def test_obj(): + v = ValueProf() + value = Value() + assert v.status == SEEN_NOTHING + v.see_write(value) + v.see_write(value) + v.see_write(value) + v.see_write(value) + assert v.try_read_constant_obj() is value + assert v.status == SEEN_OBJ + v.see_int(2) + assert v.status == SEEN_TOO_MUCH + + v = ValueProf() + assert v.status == SEEN_NOTHING + v.see_write(Value()) + v.see_write(Value()) + assert v.status == SEEN_TOO_MUCH + + +def test_none(): + v = ValueProf() + assert v.status == SEEN_NOTHING + v.see_write(None) + assert v.status == SEEN_TOO_MUCH + v.see_write(None) + assert v.status == SEEN_TOO_MUCH + + v = ValueProf() + v.see_write(ValueInt(1)) + assert v.status == SEEN_INT + v.see_write(None) + assert v.status == SEEN_TOO_MUCH + + v = ValueProf() + v.see_write(Value()) + assert v.status == SEEN_OBJ + v.see_write(None) + assert v.status == SEEN_TOO_MUCH diff --git a/pypy/interpreter/valueprof.py b/pypy/interpreter/valueprof.py --- a/pypy/interpreter/valueprof.py +++ b/pypy/interpreter/valueprof.py @@ -1,75 +1,74 @@ from rpython.rlib import jit from rpython.rlib.rweakref import ref, dead_ref +SEEN_NOTHING = '\x00' +SEEN_INT = '\x01' +SEEN_OBJ = '\x02' +SEEN_TOO_MUCH = '\x03' + class ValueProf(object): - def __init__(self, size, threshold=200): - self.values_wref = [dead_ref] * size - self.values_int = [-1] * size - self.counters = [0] * size - self.threshold = 200 - self.frozen = False + _mixin_ = True + _immutable_fields_ = ['status?'] + + def __init__(self): + # only if you subclass normally + self.init_valueprof() + + def init_valueprof(self): + self.status = SEEN_NOTHING + self.value_int = 0 + self.value_wref = dead_ref + + def is_int(self, w_obj): + raise NotImplementedError("abstract base") + + def get_int_val(self, w_obj): + raise NotImplementedError("abstract base") + + def see_write(self, w_value): + if self.is_int(w_value): + return self.see_int(self.get_int_val(w_value)) + return self.see_object(w_value) + + def see_int(self, value): + status = self.status + if status == SEEN_NOTHING: + self.value_int = value + self.status = SEEN_INT + elif status == SEEN_INT: + if self.read_constant_int() != value: + self.status = SEEN_TOO_MUCH + elif status == SEEN_OBJ: + self.status = SEEN_TOO_MUCH + + def see_object(self, value): + status = self.status + if value is None: + if status != SEEN_TOO_MUCH: + self.status = SEEN_TOO_MUCH + elif status == SEEN_NOTHING: + self.value_wref = ref(value) + self.status = SEEN_OBJ + elif status == SEEN_INT: + self.status = SEEN_TOO_MUCH + elif status == SEEN_OBJ: + if self.try_read_constant_obj() is not value: + self.status = SEEN_TOO_MUCH + + def can_fold_read_int(self): + return self.status == SEEN_INT + + def can_fold_read_obj(self): + return self.status == SEEN_OBJ @jit.elidable - def freeze(self): - # this works because we only ever change it in one direction - self.frozen = True - return True - - def see_int(self, index, value): - if self.frozen: - return 0 - count = self.counters[index] - if count < 0: - if self.values_int[index] == value: - new_count = count - 1 - self.counters[index] = new_count - return -new_count - else: - self.values_wref[index] = dead_ref - self.values_int[index] = value - self.counters[index] = -1 - return 1 - - def see_object(self, index, value): - if self.frozen: - return 0 - if value is None: - self.values_wref[index] = dead_ref - self.counters[index] = 0 - return 0 - count = self.counters[index] - if count > 0: - if self.values_wref[index]() is value: - new_count = count + 1 - self.counters[index] = new_count - return new_count - else: - self.values_int[index] = -1 - self.values_wref[index] = ref(value) - self.counters[index] = 1 - return 1 + def read_constant_int(self): + assert self.can_fold_read_int() + return self.value_int @jit.elidable - def is_variable_constant(self, index): - assert self.frozen - counter = self.counters[index] - if counter > 0: - return counter > self.threshold - else: - return -counter > self.threshold + def try_read_constant_obj(self): + assert self.can_fold_read_obj() + return self.value_wref() - @jit.elidable - def is_variable_int(self, index): - assert self.frozen - assert self.is_variable_constant(index) - return self.counters[index] < 0 - @jit.elidable - def variable_value_int(self, index): - assert self.is_variable_int(index) - return self.values_int[index] - - @jit.elidable - def variable_value_object(self, index): - assert self.is_variable_constant(index) and not self.is_variable_int(index) - return self.values_wref[index]() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit