[pypy-commit] pypy value-profiling: for local variables, only track the type to make the interpreter less bad
cfbolz
noreply at buildbot.pypy.org
Tue Aug 18 12:07:48 CEST 2015
Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: value-profiling
Changeset: r79032:4c1c11cfc6a9
Date: 2015-08-18 11:43 +0200
http://bitbucket.org/pypy/pypy/changeset/4c1c11cfc6a9/
Log: for local variables, only track the type to make the interpreter
less bad
diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py
--- a/pypy/interpreter/pycode.py
+++ b/pypy/interpreter/pycode.py
@@ -50,22 +50,15 @@
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 KnownTypesVersion(object):
+ pass
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[*]", "vprofs[*]"]
+ "_args_as_cellvars[*]", "_known_types_version?"]
def __init__(self, space, argcount, nlocals, stacksize, flags,
code, consts, names, varnames, filename,
@@ -95,7 +88,13 @@
self._signature = cpython_code_signature(self)
self._initialize()
self._init_ready()
- self.vprofs = [ValueProf('%s %s' % (self.co_name, self.co_varnames[i])) for i in range(self.co_nlocals)]
+ # a list of either None, W_Root, or a subclass thereof
+ # None means "have not seen a value in that local variable yet
+ # W_Root can be anything
+ # otherwise it's the precise class of *all* values ever stored in that
+ # local
+ self._known_types = [None] * self.co_nlocals
+ self._known_types_version = KnownTypesVersion()
def _initialize(self):
if self.co_cellvars:
@@ -140,6 +139,21 @@
def _init_ready(self):
"This is a hook for the vmprof module, which overrides this method."
+ def _get_known_type(self, varindex):
+ # somewhat subtle:
+ if not jit.we_are_jitted():
+ return self._known_types[varindex]
+ return self._get_known_type_elidable(varindex, self._known_types_version)
+
+ @jit.elidable
+ def _get_known_type_elidable(self, varindex, version):
+ assert version is self._known_types_version
+ return self._known_types[varindex]
+
+ def _update_known_type(self, varindex, cls):
+ self._known_types[varindex] = cls
+ self._known_types_version = KnownTypesVersion()
+
def _cleanup_(self):
if (self.magic == cpython_magic and
'__pypy__' not in sys.builtin_module_names):
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -4,7 +4,7 @@
from rpython.rlib import jit
from rpython.rlib.debug import make_sure_not_resized, check_nonneg
from rpython.rlib.jit import hint, we_are_jitted
-from rpython.rlib.objectmodel import we_are_translated, instantiate
+from rpython.rlib.objectmodel import we_are_translated, instantiate, specialize
from rpython.rlib.rarithmetic import intmask, r_uint
from rpython.tool.pairtype import extendabletype
@@ -146,36 +146,38 @@
return cell
def _getlocal(self, varindex):
- from pypy.objspace.std.intobject import W_IntObject
- # some careful logic there
- if we_are_jitted():
- vprof = self.getcode().vprofs[varindex]
- 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().vprofs[varindex]
- if vprof.class_is_known():
- jit.record_exact_class(w_res, vprof.read_constant_cls())
+ cls = self.getcode()._get_known_type(varindex)
+ if cls is not None and cls is not W_Root:
+ jit.record_exact_class(w_res, cls)
return w_res
- def _setlocal(self, varindex, value):
- self._value_profile_local(varindex, value)
+ @specialize.arg(3)
+ def _setlocal(self, varindex, value, can_be_None=True):
+ self._see_write(varindex, value, can_be_None)
self.locals_cells_stack_w[varindex] = value
- def _value_profile_local(self, varindex, value):
- from pypy.objspace.std.intobject import W_IntObject
- vprof = self.getcode().vprofs[varindex]
- vprof.see_write(value)
+ @specialize.arg(3)
+ def _see_write(self, varindex, value, can_be_None=True):
+ cls = self.getcode()._get_known_type(varindex)
+ if cls is W_Root:
+ return
+ if can_be_None and value is None:
+ new_cls = W_Root
+ else:
+ new_cls = value.__class__
+ if cls is not None:
+ if new_cls is not cls:
+ new_cls = W_Root
+ else:
+ return
+ self.getcode()._update_known_type(varindex, new_cls)
@jit.unroll_safe
def _all_locals_changed(self):
- for i, vprof in enumerate(self.getcode().vprofs):
- vprof.see_write(self.locals_cells_stack_w[i])
+ for i in range(self.getcode().co_nlocals):
+ self._see_write(i, self.locals_cells_stack_w[i])
def mark_as_escaped(self):
"""
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -505,7 +505,7 @@
def STORE_FAST(self, varindex, next_instr):
w_newvalue = self.popvalue()
assert w_newvalue is not None
- self._setlocal(varindex, w_newvalue)
+ self._setlocal(varindex, w_newvalue, can_be_None=False)
def getfreevarname(self, index):
freevarnames = self.pycode.co_cellvars + self.pycode.co_freevars
diff --git a/pypy/interpreter/test/test_pyframe.py b/pypy/interpreter/test/test_pyframe.py
--- a/pypy/interpreter/test/test_pyframe.py
+++ b/pypy/interpreter/test/test_pyframe.py
@@ -537,15 +537,29 @@
sys.settrace(None)
assert res == 10
-class TestValueProf(object):
- def test_argument_is_constant(self):
+class TestLocalTypeProfiling(object):
+ def test_argument_is_constant_type(self):
+ from pypy.objspace.std.intobject import W_IntObject
+ from pypy.interpreter.baseobjspace import W_Root
space = self.space
w_f = space.appexec([], """():
def f(x):
y = x + 1
return f""")
+ v1 = w_f.code._known_types_version
+ assert w_f.code._known_types == [None] * 2
space.call_function(w_f, space.wrap(1))
- assert len(w_f.code.vprofs) == 2
- assert w_f.code.vprofs[0].can_fold_read_int()
- assert w_f.code.vprofs[1].can_fold_read_int()
+ v2 = w_f.code._known_types_version
+ assert v2 is not v1
+ assert w_f.code._known_types == [W_IntObject] * 2
+ space.call_function(w_f, space.wrap(2))
+ v3 = w_f.code._known_types_version
+ assert v3 is v2
+ assert w_f.code._known_types == [W_IntObject] * 2
+
+ space.call_function(w_f, space.wrap(1.1))
+ v4 = w_f.code._known_types_version
+ assert v4 is not v3
+ assert w_f.code._known_types == [W_Root] * 2
+
More information about the pypy-commit
mailing list