[pypy-commit] lang-smalltalk storage-vref: Added vref refactoring: keeping a virtual back-reference to the sender as long as possible.

anton_gulenko noreply at buildbot.pypy.org
Mon Jul 28 10:11:29 CEST 2014


Author: Anton Gulenko <anton.gulenko at googlemail.com>
Branch: storage-vref
Changeset: r992:3fa384225c32
Date: 2014-07-10 12:56 +0200
http://bitbucket.org/pypy/lang-smalltalk/changeset/3fa384225c32/

Log:	Added vref refactoring: keeping a virtual back-reference to the
	sender as long as possible. TODO: Explore why this breaks
	performance; it should actually improve it.

diff --git a/spyvm/interpreter.py b/spyvm/interpreter.py
--- a/spyvm/interpreter.py
+++ b/spyvm/interpreter.py
@@ -67,6 +67,9 @@
         s_new_context = w_active_context.as_context_get_shadow(self.space)
         while True:
             assert self.current_stack_depth == 0
+            # Need to save s_sender, loop_bytecodes will nil this on return
+            # Virtual references are not allowed here, and neither are "fresh" contexts (except for the toplevel one).
+            assert s_new_context.virtual_sender is jit.vref_None
             s_sender = s_new_context.s_sender()
             try:
                 self.loop_bytecodes(s_new_context)
@@ -119,19 +122,27 @@
     # This is a wrapper around loop_bytecodes that cleanly enters/leaves the frame
     # and handles the stack overflow protection mechanism.
     def stack_frame(self, s_frame, s_sender, may_context_switch=True):
+        assert s_frame.virtual_sender is jit.vref_None
         try:
-            if s_frame._s_sender is None and s_sender is not None:
-                s_frame.store_s_sender(s_sender, raise_error=False)
-            
+            # Enter the context - store a virtual reference back to the sender
+            # Non-fresh contexts can happen, e.g. when activating a stored BlockContext.
+            # The same frame object must not pass through here recursively!
+            if s_frame.is_fresh() and s_sender is not None:
+                s_frame.virtual_sender = jit.virtual_ref(s_sender)
+
             self.current_stack_depth += 1
             if self.max_stack_depth > 0:
                 if self.current_stack_depth >= self.max_stack_depth:
                     raise StackOverflow(s_frame)
-            
+
             # Now (continue to) execute the context bytecodes
             self.loop_bytecodes(s_frame, may_context_switch)
         finally:
             self.current_stack_depth -= 1
+            # Cleanly leave the context. This will finish the virtual sender-reference, if
+            # it is still there, which can happen in case of ProcessSwitch or StackOverflow;
+            # in case of a Return, this will already be handled while unwinding the stack.
+            s_frame.finish_virtual_sender(s_sender)
 
     def step(self, context):
         bytecode = context.fetch_next_bytecode()
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
--- a/spyvm/shadow.py
+++ b/spyvm/shadow.py
@@ -607,13 +607,13 @@
 class ContextPartShadow(AbstractRedirectingShadow):
 
     __metaclass__ = extendabletype
-    _attrs_ = ['_s_sender',
+    _attrs_ = ['direct_sender', 'virtual_sender',
             '_pc', '_temps_and_stack',
             '_stack_ptr', 'instances_w']
     repr_classname = "ContextPartShadow"
 
     _virtualizable_ = [
-        '_s_sender',
+        'direct_sender', 'virtual_sender',
         "_pc", "_temps_and_stack[*]", "_stack_ptr",
         "_w_self", "_w_self_size"
     ]
@@ -622,7 +622,8 @@
     # Initialization
 
     def __init__(self, space, w_self):
-        self._s_sender = None
+        self.direct_sender = None
+        self.virtual_sender = jit.vref_None
         AbstractRedirectingShadow.__init__(self, space, w_self)
         self.instances_w = {}
 
@@ -691,9 +692,25 @@
             raise error.WrapperException("Index in context out of bounds")
 
     # === Sender ===
+    # There are two fields for the sender (virtual and direct). Only one of them is can be set at a time.
+    # As long as the frame object is virtualized, using the virtual reference should increase performance.
+    # As soon as a frame object is forced to the heap, the direct reference must be used.
+
+    def is_fresh(self):
+        return self.direct_sender is None and self.virtual_sender is jit.vref_None
+
+    def finish_virtual_sender(self, s_sender):
+        if self.virtual_sender is not jit.vref_None:
+            if self.pc() != -1:
+                # stack is unrolling, but this frame was not
+                # marked_returned: it is an escaped frame
+                sender = self.virtual_sender()
+                self.direct_sender = sender
+            jit.virtual_ref_finish(self.virtual_sender, s_sender)
+            self.virtual_sender = jit.vref_None
 
     def store_s_sender(self, s_sender, raise_error=True):
-        self._s_sender = s_sender
+        self.direct_sender = s_sender
         if raise_error:
             raise error.SenderChainManipulation(self)
 
@@ -704,7 +721,11 @@
         return sender.w_self()
 
     def s_sender(self):
-        return self._s_sender
+        if self.direct_sender:
+            return self.direct_sender
+        else:
+            result = self.virtual_sender()
+            return result
 
     # === Stack Pointer ===
 


More information about the pypy-commit mailing list