[pypy-commit] pypy default: More tweaks: pass a (w_func, w_type) pair to the elidable function

arigo noreply at buildbot.pypy.org
Thu May 1 13:41:45 CEST 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r71128:3f23bbb810db
Date: 2014-05-01 11:16 +0200
http://bitbucket.org/pypy/pypy/changeset/3f23bbb810db/

Log:	More tweaks: pass a (w_func, w_type) pair to the elidable function
	instead of a string, and make the function @elidable_promote as it
	should be what we want anyway. Use a new small container,
	W_DelayedBuiltinStr; see docstring.

diff --git a/pypy/module/_lsprof/interp_lsprof.py b/pypy/module/_lsprof/interp_lsprof.py
--- a/pypy/module/_lsprof/interp_lsprof.py
+++ b/pypy/module/_lsprof/interp_lsprof.py
@@ -59,7 +59,7 @@
             self.tt, self.it, calls_repr))
 
     def get_code(self, space):
-        return self.frame
+        return returns_code(space, self.frame)
 
 W_StatsEntry.typedef = TypeDef(
     'StatsEntry',
@@ -86,7 +86,7 @@
             frame_repr, self.callcount, self.reccallcount, self.tt, self.it))
 
     def get_code(self, space):
-        return self.frame
+        return returns_code(space, self.frame)
 
 W_StatsSubEntry.typedef = TypeDef(
     'SubStatsEntry',
@@ -215,18 +215,55 @@
     return '<%s>' % w_func.name
 
 
-def create_spec_for_object(space, w_obj):
-    class_name = space.type(w_obj).getname(space)
+def create_spec_for_object(space, w_type):
+    class_name = w_type.getname(space)
     return "<'%s' object>" % (class_name,)
 
 
-def create_spec(space, w_arg):
+class W_DelayedBuiltinStr(W_Root):
+    # This class should not be seen at app-level, but is useful to
+    # contain a (w_func, w_type) pair returned by prepare_spec().
+    # Turning this pair into a string cannot be done eagerly in
+    # an @elidable function because of space.str_w(), but it can
+    # be done lazily when we really want it.
+
+    _immutable_fields_ = ['w_func', 'w_type']
+
+    def __init__(self, w_func, w_type):
+        self.w_func = w_func
+        self.w_type = w_type
+        self.w_string = None
+
+    def wrap_string(self, space):
+        if self.w_string is None:
+            if self.w_type is None:
+                s = create_spec_for_function(space, self.w_func)
+            elif self.w_func is None:
+                s = create_spec_for_object(space, self.w_type)
+            else:
+                s = create_spec_for_method(space, self.w_func, self.w_type)
+            self.w_string = space.wrap(s)
+        return self.w_string
+
+W_DelayedBuiltinStr.typedef = TypeDef(
+    'DelayedBuiltinStr',
+    __str__ = interp2app(W_DelayedBuiltinStr.wrap_string),
+)
+
+def returns_code(space, w_frame):
+    if isinstance(w_frame, W_DelayedBuiltinStr):
+        return w_frame.wrap_string(space)
+    return w_frame    # actually a PyCode object
+
+
+def prepare_spec(w_arg):
     if isinstance(w_arg, Method):
-        return create_spec_for_method(space, w_arg.w_function, w_arg.w_class)
+        return (w_arg.w_function, w_arg.w_class)
     elif isinstance(w_arg, Function):
-        return create_spec_for_function(space, w_arg)
+        return (w_arg, None)
     else:
-        return create_spec_for_object(space, w_arg)
+        return (None, space.type(w_arg))
+prepare_spec._always_inline_ = True
 
 
 def lsprof_call(space, w_self, frame, event, w_arg):
@@ -239,12 +276,10 @@
         w_self._enter_return(code)
     elif event == 'c_call':
         if w_self.builtins:
-            key = create_spec(space, w_arg)
-            w_self._enter_builtin_call(key)
+            w_self._enter_builtin_call(w_arg)
     elif event == 'c_return' or event == 'c_exception':
         if w_self.builtins:
-            key = create_spec(space, w_arg)
-            w_self._enter_builtin_return(key)
+            w_self._enter_builtin_return(w_arg)
     else:
         # ignore or raise an exception???
         pass
@@ -307,13 +342,14 @@
                 return entry
             raise
 
-    @jit.elidable
-    def _get_or_make_builtin_entry(self, key, make=True):
+    @jit.elidable_promote()
+    def _get_or_make_builtin_entry(self, w_func, w_type, make):
+        key = (w_func, w_type)
         try:
             return self.builtin_data[key]
         except KeyError:
             if make:
-                entry = ProfilerEntry(self.space.wrap(key))
+                entry = ProfilerEntry(W_DelayedBuiltinStr(w_func, w_type))
                 self.builtin_data[key] = entry
                 return entry
             raise
@@ -337,20 +373,18 @@
             context._stop(self, entry)
         self.current_context = context.previous
 
-    def _enter_builtin_call(self, key):
-        self = jit.promote(self)
-        key = jit.promote_string(key)
-        entry = self._get_or_make_builtin_entry(key)
+    def _enter_builtin_call(self, w_arg):
+        w_func, w_type = prepare_spec(w_arg)
+        entry = self._get_or_make_builtin_entry(w_func, w_type, True)
         self.current_context = ProfilerContext(self, entry)
 
-    def _enter_builtin_return(self, key):
+    def _enter_builtin_return(self, w_arg):
         context = self.current_context
         if context is None:
             return
-        self = jit.promote(self)
-        key = jit.promote_string(key)
+        w_func, w_type = prepare_spec(w_arg)
         try:
-            entry = self._get_or_make_builtin_entry(key, False)
+            entry = self._get_or_make_builtin_entry(w_func, w_type, False)
         except KeyError:
             pass
         else:
diff --git a/pypy/module/_lsprof/test/test_cprofile.py b/pypy/module/_lsprof/test/test_cprofile.py
--- a/pypy/module/_lsprof/test/test_cprofile.py
+++ b/pypy/module/_lsprof/test/test_cprofile.py
@@ -27,6 +27,32 @@
         for entry in stats:
             assert entry.code in expected
 
+    def test_builtins_callers(self):
+        import _lsprof
+        prof = _lsprof.Profiler(subcalls=True)
+        lst = []
+        def f1():
+            lst.append(len(lst))
+        prof.enable(subcalls=True)
+        f1()
+        prof.disable()
+        stats = prof.getstats()
+        expected = (
+            "<len>",
+            "<method 'append' of 'list' objects>",
+        )
+        by_id = set()
+        for entry in stats:
+            if entry.code == f1.func_code:
+                assert len(entry.calls) == 2
+                for subentry in entry.calls:
+                    assert subentry.code in expected
+                    by_id.add(id(subentry.code))
+            elif entry.code in expected:
+                by_id.add(id(entry.code))
+        #  :-(  cProfile.py relies on the id() of the strings...
+        assert len(by_id) == len(expected)
+
     def test_direct(self):
         import _lsprof
         def getticks():


More information about the pypy-commit mailing list