[pypy-commit] pypy stm-gc: Random progress.

arigo noreply at buildbot.pypy.org
Fri Apr 13 19:06:12 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r54341:e1e05308a3c9
Date: 2012-04-13 19:05 +0200
http://bitbucket.org/pypy/pypy/changeset/e1e05308a3c9/

Log:	Random progress.

diff --git a/pypy/rpython/memory/gc/stmgc.py b/pypy/rpython/memory/gc/stmgc.py
--- a/pypy/rpython/memory/gc/stmgc.py
+++ b/pypy/rpython/memory/gc/stmgc.py
@@ -43,6 +43,7 @@
 GCFLAG_HAS_SHADOW = first_gcflag << 2
 GCFLAG_FIXED_HASH = first_gcflag << 3
 GCFLAG_WEAKREF    = first_gcflag << 4
+GCFLAG_VISITED    = first_gcflag << 5
 
 
 def always_inline(fn):
@@ -70,18 +71,18 @@
         'stm_operations': 'use_real_one',
         'nursery_size': 32*1024*1024,           # 32 MB
 
-        "page_size": 1024*WORD,                 # copied from minimark.py
-        "arena_size": 65536*WORD,               # copied from minimark.py
-        "small_request_threshold": 35*WORD,     # copied from minimark.py
+        #"page_size": 1024*WORD,                 # copied from minimark.py
+        #"arena_size": 65536*WORD,               # copied from minimark.py
+        #"small_request_threshold": 35*WORD,     # copied from minimark.py
     }
 
     def __init__(self, config,
                  stm_operations='use_emulator',
                  nursery_size=1024,
-                 page_size=16*WORD,
-                 arena_size=64*WORD,
-                 small_request_threshold=5*WORD,
-                 ArenaCollectionClass=None,
+                 #page_size=16*WORD,
+                 #arena_size=64*WORD,
+                 #small_request_threshold=5*WORD,
+                 #ArenaCollectionClass=None,
                  **kwds):
         MovingGCBase.__init__(self, config, **kwds)
         #
@@ -95,9 +96,7 @@
         from pypy.rpython.memory.gc import stmshared
         self.stm_operations = stm_operations
         self.nursery_size = nursery_size
-        self.sharedarea = stmshared.StmGCSharedArea(self, ArenaCollectionClass,
-                                                    page_size, arena_size,
-                                                    small_request_threshold)
+        self.sharedarea = stmshared.StmGCSharedArea(self)
         #
         def _get_size(obj):     # indirection to hide 'self'
             return self.get_size(obj)
diff --git a/pypy/rpython/memory/gc/stmshared.py b/pypy/rpython/memory/gc/stmshared.py
--- a/pypy/rpython/memory/gc/stmshared.py
+++ b/pypy/rpython/memory/gc/stmshared.py
@@ -1,18 +1,58 @@
+from pypy.rpython.lltypesystem import lltype, llmemory, llarena
+from pypy.rlib.objectmodel import free_non_gc_object
+
+NULL = llmemory.NULL
 
 
 class StmGCSharedArea(object):
+    _alloc_flavor_ = 'raw'
 
-    def __init__(self, gc, ArenaCollectionClass,
-                 page_size, arena_size, small_request_threshold):
+    def __init__(self, gc):
         self.gc = gc
-        # The ArenaCollection() handles the nonmovable objects allocation.
-        # It contains all small GCFLAG_GLOBAL objects.  The non-small ones
-        # are directly malloc'ed.
-        if ArenaCollectionClass is None:
-            from pypy.rpython.memory.gc import minimarkpage
-            ArenaCollectionClass = minimarkpage.ArenaCollection
-        self.ac = ArenaCollectionClass(arena_size, page_size,
-                                       small_request_threshold)
 
     def setup(self):
         pass
+
+
+class StmGCThreadLocalAllocator(object):
+    """A thread-local allocator for the shared area.
+    This is an optimization only: it lets us use thread-local variables
+    to keep track of what we allocated.
+    """
+    _alloc_flavor_ = 'raw'
+
+    def __init__(self, sharedarea):
+        self.gc = sharedarea.gc
+        self.sharedarea = sharedarea
+        self.chained_list = NULL
+        self.special_stack = self.gc.AddressStack()
+
+    def delete(self):
+        self.special_stack.delete()
+        free_non_gc_object(self)
+
+    def malloc_regular(self, size):
+        """Malloc for an object where the 'version' field can be used
+        internally for a chained list."""
+        adr1 = llarena.arena_malloc(size, 0)
+        adr2 = adr1 + self.gc.gcheaderbuilder
+        hdr = llmemory.cast_adr_to_ptr(adr1, lltype.Ptr(self.gc.HDR))
+        hdr.version = self.chained_list
+        self.chained_list = adr2
+        return adr2
+
+    def malloc_special(self, size):
+        """Malloc for an object where the 'version' field cannot be
+        used internally.  It's the rare case here."""
+        adr1 = llarena.arena_malloc(size, 0)
+        adr2 = adr1 + self.gc.gcheaderbuilder.size_gc_header
+        self.special_stack.append(adr2)
+        return adr2
+
+    def free_object(self, adr2):
+        adr1 = adr2 - self.gc.gcheaderbuilder.size_gc_header
+        llarena.arena_free(adr1)
+
+    def replace_special_stack(self, new_special_stack):
+        self.special_stack.delete()
+        self.special_stack = new_special_stack
diff --git a/pypy/rpython/memory/gc/stmtls.py b/pypy/rpython/memory/gc/stmtls.py
--- a/pypy/rpython/memory/gc/stmtls.py
+++ b/pypy/rpython/memory/gc/stmtls.py
@@ -7,7 +7,7 @@
 
 from pypy.rpython.memory.gc.stmgc import WORD, NULL
 from pypy.rpython.memory.gc.stmgc import always_inline, dont_inline
-from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL
+from pypy.rpython.memory.gc.stmgc import GCFLAG_GLOBAL, GCFLAG_VISITED
 
 
 class StmGCTLS(object):
@@ -19,6 +19,7 @@
     nontranslated_dict = {}
 
     def __init__(self, gc, in_main_thread):
+        from pypy.rpython.memory.gc.stmshared import StmGCThreadLocalAllocator
         self.gc = gc
         self.in_main_thread = in_main_thread
         self.stm_operations = self.gc.stm_operations
@@ -37,13 +38,16 @@
         self.nursery_start = self._alloc_nursery(self.nursery_size)
         #
         # --- the local raw-malloced objects (chained list via hdr.version)
-        self.rawmalloced_objects = NULL
+        #self.rawmalloced_objects = NULL
         # --- the local "normal" old objects (chained list via hdr.version)
         self.old_objects = NULL
         # --- the local objects with weakrefs (chained list via hdr.version)
         #self.young_objects_with_weakrefs = NULL
         #self.old_objects_with_weakrefs = NULL
         #
+        # --- a thread-local allocator for the shared area
+        self.sharedarea_tls = StmGCThreadLocalAllocator(gc.sharedarea)
+        #
         self._register_with_C_code()
 
     def teardown_thread(self):
@@ -137,70 +141,52 @@
     # ------------------------------------------------------------
 
     def local_collection(self, run_finalizers=True):
-        """Do a local collection.  Finds all surviving young objects
-        and make them old.  Also looks for roots from the stack.
-        The flag GCFLAG_WAS_COPIED is kept and the C tree is updated
-        if the local young object moves.
+        """Do a local collection.  This should be equivalent to a minor
+        collection only, but the GC is not generational so far, so it is
+        for now the same as a full collection --- but only on LOCAL
+        objects, not touching the GLOBAL objects.  More precisely, this
+        finds all YOUNG LOCAL objects, move them out of the nursery if
+        necessary, and make them OLD LOCAL objects.  This starts from
+        the roots from the stack.  The flag GCFLAG_WAS_COPIED is kept
+        and the C tree is updated if the local young objects move.
         """
         #
         debug_start("gc-local")
         #
-        # First, find the roots that point to young objects.  All nursery
-        # objects found are copied out of the nursery, and the occasional
-        # young raw-malloced object is flagged with GCFLAG_VISITED.
-        # Note that during this step, we ignore references to further
-        # young objects; only objects directly referenced by roots
-        # are copied out or flagged.  They are also added to the list
-        # 'old_objects_pointing_to_young'.
+        # Linked list of LOCAL objects pending a visit.  Note that no
+        # GLOBAL object can at any point contain a reference to a LOCAL
+        # object.
+        self.pending_list = NULL
+        #
+        # First, find the roots that point to LOCAL objects.  All YOUNG
+        # (i.e. nursery) objects found are copied out of the nursery.
+        # All OLD objects found are flagged with GCFLAG_VISITED.  At this
+        # point, the content of the objects is not modified; the objects
+        # are merely added to the chained list 'pending_list'.
         self.collect_roots_in_nursery()
         #
-        while True:
-            # If we are using card marking, do a partial trace of the arrays
-            # that are flagged with GCFLAG_CARDS_SET.
-            if self.card_page_indices > 0:
-                self.collect_cardrefs_to_nursery()
-            #
-            # Now trace objects from 'old_objects_pointing_to_young'.
-            # All nursery objects they reference are copied out of the
-            # nursery, and again added to 'old_objects_pointing_to_young'.
-            # All young raw-malloced object found are flagged GCFLAG_VISITED.
-            # We proceed until 'old_objects_pointing_to_young' is empty.
-            self.collect_oldrefs_to_nursery()
-            #
-            # We have to loop back if collect_oldrefs_to_nursery caused
-            # new objects to show up in old_objects_with_cards_set
-            if self.card_page_indices > 0:
-                if self.old_objects_with_cards_set.non_empty():
-                    continue
-            break
+        # Also find the roots that are the local copy of GCFLAG_WAS_COPIED
+        # objects.
+        self.collect_roots_from_tldict()
         #
-        # Now all live nursery objects should be out.  Update the young
-        # weakrefs' targets.
-        if self.young_objects_with_weakrefs.non_empty():
-            self.invalidate_young_weakrefs()
-        if self.young_objects_with_light_finalizers.non_empty():
-            self.deal_with_young_objects_with_finalizers()
+        # Now repeat following objects until 'pending_list' is empty.
+        self.collect_oldrefs_to_nursery()
         #
-        # Clear this mapping.
-        if self.nursery_objects_shadows.length() > 0:
-            self.nursery_objects_shadows.clear()
+        # Walk the list of LOCAL raw-malloced objects, and free them if
+        # necessary.
+        #self.free_local_rawmalloced_objects()
         #
-        # Walk the list of young raw-malloced objects, and either free
-        # them or make them old.
-        if self.young_rawmalloced_objects:
-            self.free_young_rawmalloced_objects()
+        # Ask the ArenaCollection to visit all objects.  Free the ones
+        # that have not been visited above, and reset GCFLAG_VISITED on
+        # the others.
+        self.ac.mass_free(self._free_if_unvisited)
         #
         # All live nursery objects are out, and the rest dies.  Fill
         # the whole nursery with zero and reset the current nursery pointer.
         llarena.arena_reset(self.nursery, self.nursery_size, 2)
-        self.debug_rotate_nursery()
-        self.nursery_free = self.nursery
+        self.nursery_free = self.nursery_start
         #
-        debug_print("minor collect, total memory used:",
-                    self.get_total_memory_used())
-        if self.DEBUG >= 2:
-            self.debug_check_consistency()     # expensive!
-        debug_stop("gc-minor")
+        debug_stop("gc-local")
 
     def end_of_transaction_collection(self):
         """Do an end-of-transaction collection.  Finds all surviving
@@ -229,7 +215,7 @@
     def allocate_object_of_size(self, size):
         if not self.nursery_free:
             fatalerror("malloc in a non-main thread but outside a transaction")
-        if size > self.nursery_size:
+        if size > self.nursery_size // 8 * 7:
             fatalerror("object too large to ever fit in the nursery")
         while True:
             self.local_collection()
@@ -251,12 +237,12 @@
             hdr.tid |= GCFLAG_GLOBAL
             obj = hdr.version
         #
-        obj = self.rawmalloced_objects
-        self.rawmalloced_objects = NULL
-        while obj:
-            hdr = self.header(obj)
-            hdr.tid |= GCFLAG_GLOBAL
-            obj = hdr.version
+##        obj = self.rawmalloced_objects
+##        self.rawmalloced_objects = NULL
+##        while obj:
+##            hdr = self.header(obj)
+##            hdr.tid |= GCFLAG_GLOBAL
+##            obj = hdr.version
 
     def _cleanup_state(self):
         if self.rawmalloced_objects:
diff --git a/pypy/rpython/memory/gc/test/test_stmtls.py b/pypy/rpython/memory/gc/test/test_stmtls.py
--- a/pypy/rpython/memory/gc/test/test_stmtls.py
+++ b/pypy/rpython/memory/gc/test/test_stmtls.py
@@ -2,18 +2,42 @@
 from pypy.rpython.lltypesystem import lltype, llmemory, llarena, llgroup, rffi
 from pypy.rpython.memory.gc.stmtls import StmGCTLS, WORD
 from pypy.rpython.memory.gc.test.test_stmgc import StmGCTests
+from pypy.rpython.memory.support import get_address_stack, get_address_deque
 
 
 S = lltype.GcStruct('S', ('a', lltype.Signed), ('b', lltype.Signed),
                          ('c', lltype.Signed))
 
 
-class TestStmGCTLS(StmGCTests):
-    current_stack = ()
+class FakeStmOperations:
+    def set_tls(self, tlsaddr, num):
+        pass
+    def del_tls(self, tlsaddr):
+        pass
+
+class FakeSharedArea:
+    pass
+
+class FakeGC:
+    from pypy.rpython.memory.support import AddressDict, null_address_dict
+    AddressStack = get_address_stack()
+    AddressDeque = get_address_deque()
+    nursery_size = 128
+    stm_operations = FakeStmOperations()
+    sharedarea = FakeSharedArea()
+
+
+class TestStmGCTLS(object):
+
+    def setup_method(self, meth):
+        self.current_stack = []
+        self.gc = FakeGC()
+        self.gc.sharedarea.gc = self.gc
+        self.gctls_main = StmGCTLS(self.gc, in_main_thread=True)
+        self.gctls_thrd = StmGCTLS(self.gc, in_main_thread=False)
+        self.gc.main_thread_tls = self.gctls_main
 
     def stack_add(self, p):
-        if self.current_stack == ():
-            self.current_stack = []
         self.current_stack.append(p)
 
     def stack_pop(self):
diff --git a/pypy/translator/c/src/allocator.h b/pypy/translator/c/src/allocator.h
--- a/pypy/translator/c/src/allocator.h
+++ b/pypy/translator/c/src/allocator.h
@@ -1,3 +1,14 @@
+#if defined(RPY_STM)
+
+
+/* XXX no special malloc function, use the thread-safe system-provided one */
+#define PyObject_Malloc malloc
+#define PyObject_Realloc realloc
+#define PyObject_Free free
+
+
+#else
+
 
 /* allocation functions prototypes */
 void *PyObject_Malloc(size_t n);
@@ -29,3 +40,4 @@
 #endif
 
 #endif
+#endif
diff --git a/pypy/translator/stm/stmgcintf.py b/pypy/translator/stm/stmgcintf.py
--- a/pypy/translator/stm/stmgcintf.py
+++ b/pypy/translator/stm/stmgcintf.py
@@ -11,7 +11,8 @@
 eci = ExternalCompilationInfo(
     include_dirs = [cdir, cdir2],
     includes = ['src_stm/et.h', 'src_stm/et.c'],
-    pre_include_bits = ['#define PYPY_LONG_BIT %d' % LONG_BIT],
+    pre_include_bits = ['#define PYPY_LONG_BIT %d' % LONG_BIT,
+                        '#define RPY_STM 1'],
     separate_module_sources = ['\n'],    # hack for test_rffi_stm
 )
 


More information about the pypy-commit mailing list