[pypy-svn] r79105 - in pypy/branch/jit-free/pypy/jit/metainterp: . test

arigo at codespeak.net arigo at codespeak.net
Mon Nov 15 15:34:27 CET 2010


Author: arigo
Date: Mon Nov 15 15:34:25 2010
New Revision: 79105

Modified:
   pypy/branch/jit-free/pypy/jit/metainterp/compile.py
   pypy/branch/jit-free/pypy/jit/metainterp/history.py
   pypy/branch/jit-free/pypy/jit/metainterp/memmgr.py
   pypy/branch/jit-free/pypy/jit/metainterp/test/test_compile.py
   pypy/branch/jit-free/pypy/jit/metainterp/test/test_memmgr.py
   pypy/branch/jit-free/pypy/jit/metainterp/warmspot.py
   pypy/branch/jit-free/pypy/jit/metainterp/warmstate.py
Log:
(antocuni, arigo)
In-progress: the plan is to use weakrefs, giving much saner code.
See the comment in memmgr.py.


Modified: pypy/branch/jit-free/pypy/jit/metainterp/compile.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/compile.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/compile.py	Mon Nov 15 15:34:25 2010
@@ -42,8 +42,8 @@
     name = metainterp.staticdata.stats.name_for_new_loop()
     return TreeLoop(name)
 
-def make_loop_token(nb_args, jitdriver_sd, greenkey):
-    loop_token = LoopToken()
+def make_loop_token(cpu, nb_args, jitdriver_sd, greenkey):
+    loop_token = LoopToken(cpu)
     loop_token.specnodes = [prebuiltNotSpecNode] * nb_args
     loop_token.outermost_jitdriver_sd = jitdriver_sd
     loop_token.outermost_greenkey = greenkey
@@ -65,7 +65,8 @@
     loop.operations = [h_ops[i].clone() for i in range(start, len(h_ops))]
     metainterp_sd = metainterp.staticdata
     jitdriver_sd = metainterp.jitdriver_sd
-    loop_token = make_loop_token(len(loop.inputargs), jitdriver_sd, greenkey)
+    loop_token = make_loop_token(metainterp.cpu, len(loop.inputargs),
+                                 jitdriver_sd, greenkey)
     loop.token = loop_token
     loop.operations[-1].setdescr(loop_token)     # patch the target of the JUMP
     try:
@@ -98,9 +99,6 @@
     loop_token = loop.token
     loop_token.number = n = globaldata.loopnumbering
     globaldata.loopnumbering += 1
-    desc = metainterp_sd.warmrunnerdesc
-    if desc is not None:   # for tests
-        desc.memory_manager.record_loop(loop_token)
 
     metainterp_sd.logger_ops.log_loop(loop.inputargs, loop.operations, n, type)
     if not we_are_translated():
@@ -479,7 +477,7 @@
 
 class ResumeFromInterpDescr(ResumeDescr):
     def __init__(self, metainterp, greenkey, redkey):
-        original_loop_token = make_loop_token(len(redkey),
+        original_loop_token = make_loop_token(metainterp.cpu, len(redkey),
                                               metainterp.jitdriver_sd,
                                               greenkey)
         ResumeDescr.__init__(self, original_loop_token)
@@ -575,7 +573,7 @@
     """
     # 'redboxes' is only used to know the types of red arguments.
     inputargs = [box.clonebox() for box in redboxes]
-    loop_token = make_loop_token(len(inputargs), jitdriver_sd, greenboxes)
+    loop_token = make_loop_token(cpu, len(inputargs), jitdriver_sd, greenboxes)
     # 'nb_red_args' might be smaller than len(redboxes),
     # because it doesn't include the virtualizable boxes.
     nb_red_args = jitdriver_sd.num_red_args

Modified: pypy/branch/jit-free/pypy/jit/metainterp/history.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/history.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/history.py	Mon Nov 15 15:34:25 2010
@@ -728,14 +728,16 @@
     generated assembler.
     """
     terminating = False # see TerminatingLoopToken in compile.py
-    has_been_freed = False
     outermost_jitdriver_sd = None
     outermost_greenkey = None
     # specnodes = ...
     # and more data specified by the backend when the loop is compiled
+    cpu = None
+    number = 0
 
-    def __init__(self, number=0):
-        self.number = number
+    def __init__(self, cpu=None):
+        assert not isinstance(cpu, int)    # xxx temporary
+        self.cpu = cpu
         # See get_fail_descr_number() in backend/model.py: this growing
         # list gives the 'descr_number' of all fail descrs that belong to
         # this loop or to a bridge attached to it.
@@ -744,6 +746,15 @@
         self.contains_jumps_to = {}      # set of other LoopTokens
         self.generation = r_longlong(0)
 
+    def __del__(self):
+        if self.cpu is None:
+            return
+        if self.generation > r_longlong(0):
+            # MemoryManager.keep_loop_alive() has been called on this
+            # loop token, which means that it has been successfully
+            # compiled by the backend.  Free it now.
+            self.cpu.free_loop_and_bridges(self)
+
     def repr_of_descr(self):
         return '<Loop%d>' % self.number
 

Modified: pypy/branch/jit-free/pypy/jit/metainterp/memmgr.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/memmgr.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/memmgr.py	Mon Nov 15 15:34:25 2010
@@ -1,30 +1,25 @@
 import math
 from pypy.rlib.rarithmetic import r_longlong
-from pypy.rlib.objectmodel import we_are_translated
 
 #
 # Logic to decide which loops are old and not used any more.
 #
-# Idea: We use the notion of a global 'current generation' which
-# is, in practice, the total number of loops and bridges produced
-# so far.  When executing a loop:
-#     (1) we set 'generation' to -1
-#     (2) we execute it
-#     (3) we set 'generation' to the latest generation
-# (with a bit extra work to handle nested calls and to guarantee
-# that 'generation' is always < 0 on a loop that is currently
-# executing).
+# All the long-lived references to LoopToken are weakrefs, apart from
+# the 'alive_loops' set in MemoryManager, which is the only (long-living)
+# place that keeps them alive.  If a loop was not called for long enough,
+# then it is removed from 'alive_loops'.  It will soon be freed by the
+# GC.  LoopToken.__del__ calls the method cpu.free_loop_and_bridges().
 #
-# A loop is said "old" if its generation is >= 0 but much smaller
-# than the current generation.  If a loop L is old, and if all
-# other loops from which we can reach L through
-# 'contains_jumps_to' are also old, then we can free L.
+# The alive_loops set is maintained using the notion of a global
+# 'current generation' which is, in practice, the total number of loops
+# and bridges produced so far.  A LoopToken is declared "old" if its
+# 'generation' field is much smaller than the current generation, and
+# removed from the set.
 #
 
 class MemoryManager(object):
 
-    def __init__(self, cpu):
-        self.cpu = cpu
+    def __init__(self):
         self.check_frequency = -1
         # NB. use of r_longlong to be extremely far on the safe side:
         # this is increasing by one after each loop or bridge is
@@ -34,9 +29,9 @@
         # enough.  But in this day and age, you'd still never have the
         # patience of waiting for a slowly-increasing 64-bit number to
         # overflow :-)
-        self.current_generation = r_longlong(0)
+        self.current_generation = r_longlong(1)
         self.next_check = r_longlong(-1)
-        self.looptokens = []
+        self.alive_loops = {}
 
     def set_max_age(self, max_age, check_frequency=0):
         if max_age <= 0:
@@ -51,56 +46,16 @@
     def next_generation(self):
         self.current_generation += 1
         if self.current_generation == self.next_check:
-            self._free_old_loops_now()
+            self._kill_old_loops_now()
             self.next_check = self.current_generation + self.check_frequency
 
-    def enter_loop(self, looptoken):
-        if not we_are_translated():
-            assert looptoken in self.looptokens
-            assert not looptoken.has_been_freed
-        if looptoken.generation >= 0:
-            looptoken.generation = -1
-        else:
-            looptoken.generation -= 1   # nested enter_loop()
-
-    def leave_loop(self, looptoken):
-        assert looptoken.generation < 0
-        if looptoken.generation == -1:
+    def keep_loop_alive(self, looptoken):
+        if looptoken.generation != self.current_generation:
             looptoken.generation = self.current_generation
-        else:
-            looptoken.generation += 1   # nested leave_loop()
+            self.alive_loops[looptoken] = None
 
-    def record_loop(self, looptoken):
-        looptoken.generation = self.current_generation
-        self.looptokens.append(looptoken)
-
-    def _free_old_loops_now(self):
-        #
-        # Initialize '_is_young' on all loop tokens
+    def _kill_old_loops_now(self):
         max_generation = self.current_generation - self.max_age
-        youngloops = []
-        for looptoken in self.looptokens:
+        for looptoken in self.alive_loops.keys():
             if 0 <= looptoken.generation < max_generation:
-                looptoken._is_young = False   # but may be turned to True later
-            else:
-                looptoken._is_young = True
-                youngloops.append(looptoken)
-        #
-        # Propagate forward the knowledge of "is a young loop"
-        while len(youngloops) > 0:
-            looptoken = youngloops.pop()
-            for jumptargettok in looptoken.contains_jumps_to:
-                if not jumptargettok._is_young:
-                    jumptargettok._is_young = True
-                    youngloops.append(jumptargettok)
-        #
-        # Now free all looptokens that still have _is_young == False.
-        i = 0
-        while i < len(self.looptokens):
-            looptoken = self.looptokens[i]
-            if looptoken._is_young:
-                i += 1
-            else:
-                self.looptokens[i] = self.looptokens[-1]
-                del self.looptokens[-1]
-                self.cpu.free_loop_and_bridges(looptoken)
+                del self.alive_loops[looptoken]

Modified: pypy/branch/jit-free/pypy/jit/metainterp/test/test_compile.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/test/test_compile.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/test/test_compile.py	Mon Nov 15 15:34:25 2010
@@ -218,52 +218,3 @@
         assert lltype.cast_opaque_ptr(lltype.Ptr(EXC), e.args[1]) == llexc
     else:
         assert 0, "should have raised"
-
-
-def test_send_loop_to_backend():
-    class FakeMetaInterpSD:
-        class globaldata:
-            loopnumbering = 17
-        class warmrunnerdesc:
-            class memory_manager:
-                @staticmethod
-                def record_loop(token):
-                    assert token is FakeLoop.token
-                    token._recorded = True
-        class logger_ops:
-            @staticmethod
-            def log_loop(*args, **kwds):
-                pass
-        class profiler:
-            @staticmethod
-            def start_backend():
-                pass
-            @staticmethod
-            def end_backend():
-                pass
-        class cpu:
-            @staticmethod
-            def compile_loop(inputargs, operations, token):
-                assert inputargs is FakeLoop.inputargs
-                assert operations is FakeLoop.operations
-                assert token is FakeLoop.token
-                token._compiled = True
-        class stats:
-            @staticmethod
-            def add_new_loop(loop):
-                pass
-        def log(self, text):
-            pass
-    class FakeLoop:
-        inputargs = []
-        operations = []
-        class token:
-            number = 0
-            _recorded = _compiled = False
-        def check_consistency(self):
-            pass
-    send_loop_to_backend(FakeMetaInterpSD(), FakeLoop(), "entry bridge")
-    assert FakeMetaInterpSD.globaldata.loopnumbering == 18
-    assert FakeLoop.token.number == 17
-    assert FakeLoop.token._recorded is True
-    assert FakeLoop.token._compiled is True

Modified: pypy/branch/jit-free/pypy/jit/metainterp/test/test_memmgr.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/test/test_memmgr.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/test/test_memmgr.py	Mon Nov 15 15:34:25 2010
@@ -1,158 +1,59 @@
 from pypy.jit.metainterp.memmgr import MemoryManager
-from pypy.jit.metainterp.history import LoopToken
 
 ##missing:
-
-##    in _free_old_loops_now(), remove looptoken from everywhere
-##    or mark it as freed
-
 ##    contains_jumps_to needs to be filled
 
 
-class FakeCPU:
-    def free_loop_and_bridges(self, looptoken):
-        looptoken.has_been_freed = True
-cpu = FakeCPU()
+class FakeLoopToken:
+    generation = 0
 
 
 class TestMemoryManager:
 
     def test_disabled(self):
-        memmgr = MemoryManager(cpu)
+        memmgr = MemoryManager()
         memmgr.set_max_age(0)
-        tokens = [LoopToken() for i in range(10)]
+        tokens = [FakeLoopToken() for i in range(10)]
         for token in tokens:
-            memmgr.record_loop(token)
+            memmgr.keep_loop_alive(token)
             memmgr.next_generation()
-        for token in tokens:
-            assert not token.has_been_freed
+        assert memmgr.alive_loops == dict.fromkeys(tokens)
 
     def test_basic(self):
-        memmgr = MemoryManager(cpu)
+        memmgr = MemoryManager()
         memmgr.set_max_age(3, 1)
-        tokens = [LoopToken() for i in range(10)]
+        tokens = [FakeLoopToken() for i in range(10)]
         for token in tokens:
-            memmgr.record_loop(token)
+            memmgr.keep_loop_alive(token)
             memmgr.next_generation()
-        for i in range(len(tokens)):
-            assert tokens[i].has_been_freed == (i < 7)
+        assert memmgr.alive_loops == dict.fromkeys(tokens[7:])
 
     def test_basic_2(self):
-        memmgr = MemoryManager(cpu)
+        memmgr = MemoryManager()
         memmgr.set_max_age(3, 1)
-        token = LoopToken()
-        memmgr.record_loop(token)
+        token = FakeLoopToken()
+        memmgr.keep_loop_alive(token)
         for i in range(10):
             memmgr.next_generation()
-            assert token.has_been_freed == (i >= 3)
+            if i < 3:
+                assert memmgr.alive_loops == {token: None}
+            else:
+                assert memmgr.alive_loops == {}
 
-    def test_enter_loop_1(self):
-        memmgr = MemoryManager(cpu)
+    def test_basic_3(self):
+        memmgr = MemoryManager()
         memmgr.set_max_age(3, 1)
-        tokens = [LoopToken() for i in range(10)]
+        tokens = [FakeLoopToken() for i in range(10)]
         for i in range(len(tokens)):
             print 'record tokens[%d]' % i
-            memmgr.record_loop(tokens[i])
+            memmgr.keep_loop_alive(tokens[i])
             memmgr.next_generation()
             for j in range(0, i, 2):
-                assert not tokens[j].has_been_freed
-                print 'enter and leave tokens[%d]' % j
-                memmgr.enter_loop(tokens[j])
-                memmgr.leave_loop(tokens[j])
-        for i in range(len(tokens)):
-            assert tokens[i].has_been_freed == (i < 7 and (i%2) != 0)
-
-    def test_enter_loop_2(self):
-        memmgr = MemoryManager(cpu)
-        memmgr.set_max_age(3, 1)
-        tokens = [LoopToken() for i in range(10)]
-        for i in range(len(tokens)):
-            print 'record tokens[%d]' % i
-            memmgr.record_loop(tokens[i])
-            memmgr.next_generation()
-            for j in range(i-2, i+1):
-                if j >= 0:
-                    assert not tokens[j].has_been_freed
-                    print 'enter and leave tokens[%d]' % j
-                    memmgr.enter_loop(tokens[j])
-                    memmgr.leave_loop(tokens[j])
+                assert tokens[j] in memmgr.alive_loops
+                print 'also keep alive tokens[%d]' % j
+                memmgr.keep_loop_alive(tokens[j])
         for i in range(len(tokens)):
-            assert tokens[i].has_been_freed == (i < 4)
-
-    def test_loop_is_running(self):
-        memmgr = MemoryManager(cpu)
-        memmgr.set_max_age(3, 1)
-        token = LoopToken()
-        memmgr.record_loop(token)
-        memmgr.enter_loop(token)
-        for i in range(10):
-            memmgr.next_generation()
-            assert token.has_been_freed == False
-        memmgr.leave_loop(token)
-        for i in range(10):
-            memmgr.next_generation()
-            assert token.has_been_freed == (i >= 3)
-
-    def test_nested_enter_loop(self):
-        memmgr = MemoryManager(cpu)
-        memmgr.set_max_age(3, 1)
-        token = LoopToken()
-        memmgr.record_loop(token)
-        memmgr.enter_loop(token)
-        # here we recursively end up seeing the same token again
-        memmgr.enter_loop(token)
-        for i in range(10):
-            memmgr.next_generation()
-            assert token.has_been_freed == False
-        memmgr.leave_loop(token)
-        # out of the recursive call, but the loop is still "locked"
-        for i in range(10):
-            memmgr.next_generation()
-            assert token.has_been_freed == False
-        memmgr.leave_loop(token)
-        for i in range(10):
-            memmgr.next_generation()
-            assert token.has_been_freed == (i >= 3)
-
-    def test_contains_jumps_to(self):
-        memmgr = MemoryManager(cpu)
-        memmgr.set_max_age(3, 1)
-        token1 = LoopToken()
-        token2 = LoopToken()
-        token1.contains_jumps_to[token2] = None
-        memmgr.record_loop(token1)
-        memmgr.record_loop(token2)
-        memmgr.enter_loop(token1)
-        for i in range(10):
-            memmgr.next_generation()
-            assert token1.has_been_freed == False
-            assert token2.has_been_freed == False
-        memmgr.leave_loop(token1)
-        for i in range(10):
-            memmgr.next_generation()
-            assert token1.has_been_freed == (i >= 3)
-            assert token2.has_been_freed == (i >= 3)
-
-    def test_contains_jumps_to_2(self):
-        memmgr = MemoryManager(cpu)
-        memmgr.set_max_age(3, 1)
-        token1 = LoopToken()
-        token2 = LoopToken()
-        token3 = LoopToken()
-        token1.contains_jumps_to[token2] = None
-        token2.contains_jumps_to[token3] = None
-        memmgr.record_loop(token1)
-        memmgr.record_loop(token2)
-        memmgr.record_loop(token3)
-        memmgr.enter_loop(token1)
-        for i in range(10):
-            memmgr.next_generation()
-            assert token1.has_been_freed == False
-            assert token2.has_been_freed == False
-            assert token3.has_been_freed == False
-        memmgr.leave_loop(token1)
-        for i in range(10):
-            memmgr.next_generation()
-            assert token1.has_been_freed == (i >= 3)
-            assert token2.has_been_freed == (i >= 3)
-            assert token3.has_been_freed == (i >= 3)
+            if i < 7 and (i%2) != 0:
+                assert tokens[i] not in memmgr.alive_loops
+            else:
+                assert tokens[i] in memmgr.alive_loops

Modified: pypy/branch/jit-free/pypy/jit/metainterp/warmspot.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/warmspot.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/warmspot.py	Mon Nov 15 15:34:25 2010
@@ -153,6 +153,7 @@
                  optimizer=None, ProfilerClass=EmptyProfiler, **kwds):
         pyjitpl._warmrunnerdesc = self   # this is a global for debugging only!
         self.set_translator(translator)
+        self.memory_manager = memmgr.MemoryManager()
         self.build_cpu(CPUClass, **kwds)
         self.find_portals()
         self.codewriter = codewriter.CodeWriter(self.cpu, self.jitdrivers_sd)
@@ -184,7 +185,6 @@
         self.rewrite_set_param()
         self.rewrite_force_virtual(vrefinfo)
         self.add_finish()
-        self.memory_manager = memmgr.MemoryManager(self.cpu)
         self.metainterp_sd.finish_setup(self.codewriter, optimizer=optimizer)
 
     def finish(self):
@@ -816,9 +816,8 @@
     def execute_token(self, loop_token):
         self.metainterp_sd.profiler.start_running()
         debug_start("jit-running")
-        self.memory_manager.enter_loop(loop_token)
         fail_descr = self.cpu.execute_token(loop_token)
-        self.memory_manager.leave_loop(loop_token)
         debug_stop("jit-running")
         self.metainterp_sd.profiler.end_running()
+        self.memory_manager.keep_loop_alive(loop_token)
         return fail_descr

Modified: pypy/branch/jit-free/pypy/jit/metainterp/warmstate.py
==============================================================================
--- pypy/branch/jit-free/pypy/jit/metainterp/warmstate.py	(original)
+++ pypy/branch/jit-free/pypy/jit/metainterp/warmstate.py	Mon Nov 15 15:34:25 2010
@@ -165,6 +165,7 @@
         "NOT_RPYTHON"
         self.warmrunnerdesc = warmrunnerdesc
         self.jitdriver_sd = jitdriver_sd
+        self.cpu = warmrunnerdesc.cpu
         try:
             self.profiler = warmrunnerdesc.metainterp_sd.profiler
         except AttributeError:       # for tests
@@ -233,8 +234,7 @@
         old_token = cell.entry_loop_token
         cell.entry_loop_token = entry_loop_token
         if old_token is not None:
-            cpu = self.warmrunnerdesc.cpu
-            cpu.redirect_call_assembler(old_token, entry_loop_token)
+            self.cpu.redirect_call_assembler(old_token, entry_loop_token)
 
     # ----------
 
@@ -459,7 +459,7 @@
 
         warmrunnerdesc = self.warmrunnerdesc
         jitdriver_sd   = self.jitdriver_sd
-        cpu = warmrunnerdesc.cpu
+        cpu = self.cpu
         vinfo = jitdriver_sd.virtualizable_info
         red_args_types = unrolling_iterable(jitdriver_sd._red_args_types)
         #
@@ -511,7 +511,7 @@
         unwrap_greenkey = self.make_unwrap_greenkey()
         jit_getter = self.make_jitcell_getter()
         jd = self.jitdriver_sd
-        cpu = self.warmrunnerdesc.cpu
+        cpu = self.cpu
 
         def can_inline_greenargs(*greenargs):
             if can_never_inline(*greenargs):



More information about the Pypy-commit mailing list