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