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

Reply via email to