[pypy-commit] pypy stm-gc-2: in-progress: carefully copy parts of minimarkpage.py

arigo noreply at buildbot.pypy.org
Mon Apr 8 18:49:08 CEST 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc-2
Changeset: r63147:21d5dccf753c
Date: 2013-04-08 18:35 +0200
http://bitbucket.org/pypy/pypy/changeset/21d5dccf753c/

Log:	in-progress: carefully copy parts of minimarkpage.py

diff --git a/rpython/memory/gc/stmshared.py b/rpython/memory/gc/stmshared.py
--- a/rpython/memory/gc/stmshared.py
+++ b/rpython/memory/gc/stmshared.py
@@ -1,17 +1,87 @@
-from rpython.rtyper.lltypesystem import lltype, llmemory, llarena
+from rpython.rtyper.lltypesystem import lltype, llmemory, llarena, rffi
+from rpython.rlib.rarithmetic import LONG_BIT
 from rpython.rlib.objectmodel import free_non_gc_object
+from rpython.rlib.debug import fatalerror
+from rpython.rlib import rthread
 
+WORD = LONG_BIT // 8
 NULL = llmemory.NULL
+WORD_POWER_2 = {32: 2, 64: 3}[LONG_BIT]
+assert 1 << WORD_POWER_2 == WORD
+
+# Linux's glibc is good at 'malloc(1023*WORD)': the blocks ("pages") it
+# returns are exactly 1024 words apart, reserving only one extra word
+# for its internal data
+TRANSLATED_PAGE_SIZE = 1023 * WORD
+
+# This is the largest size that StmGCSharedArea will map to its internal
+# "pages" structures.
+TRANSLATED_SMALL_REQUEST_THRESHOLD = 35 * WORD
+
+# ------------------------------------------------------------
+# The basic idea here is that each page will contain objects that are
+# each of the same size.  Moreover each page belongs to one thread only:
+# only this thread can use it to satisfy more allocations --- with however
+# one exception: pages with low usage after a major collection are moved
+# to a global list where any thread can pick them up.
+
+PAGE_PTR = lltype.Ptr(lltype.ForwardReference())
+PAGE_HEADER = lltype.Struct('PageHeader',
+    # -- The following pointer makes a chained list of pages.  For non-full
+    #    pages, it is a chained list of pages having the same size class,
+    #    rooted in 'page_for_size[size_class]'.  For full pages, it is a
+    #    different chained list rooted in 'full_page_for_size[size_class]'.
+    ('nextpage', PAGE_PTR),
+    # -- The number of free blocks.  The numbers of uninitialized and
+    #    allocated blocks can be deduced from the context if needed.
+    ('nfree', lltype.Signed),
+    # -- The chained list of free blocks.  It ends as a pointer to the
+    #    first uninitialized block (pointing to data that is uninitialized,
+    #    or to the end of the page).
+    ('freeblock', llmemory.Address),
+    # -- The structure above is 3 words, which is a good value:
+    #    '(1023-3) % N' is zero or very small for various small N's,
+    #    i.e. there is not much wasted space.
+    )
+PAGE_PTR.TO.become(PAGE_HEADER)
+PAGE_NULL = lltype.nullptr(PAGE_HEADER)
+
+# ------------------------------------------------------------
 
 
 class StmGCSharedArea(object):
     _alloc_flavor_ = 'raw'
 
-    def __init__(self, gc):
+    def __init__(self, gc, page_size, small_request_threshold):
         self.gc = gc
+        self.page_size = page_size
+        self.small_request_threshold = small_request_threshold
+        #
+        # This array contains 'length' chained lists of pages.
+        # For each size N between WORD and 'small_request_threshold'
+        # (included), the corresponding chained list contains pages
+        # which store objects of size N.  This is only used for pages
+        # with low usage after a major collection; as soon as a page
+        # is used, or if its usage says high after a major collection,
+        # it belongs to the lists of StmGCThreadLocalAllocator.
+        length = small_request_threshold / WORD + 1
+        self.low_usage_page = lltype.malloc(rffi.CArray(PAGE_PTR), length,
+                                            flavor='raw', zero=True,
+                                            immortal=True)
+        self.nblocks_for_size = lltype.malloc(rffi.CArray(lltype.Signed),
+                                              length, flavor='raw',
+                                              immortal=True)
+        self.hdrsize = llmemory.raw_malloc_usage(llmemory.sizeof(PAGE_HEADER))
+        self.nblocks_for_size[0] = 0    # unused
+        for i in range(1, length):
+            self.nblocks_for_size[i] = (page_size - self.hdrsize) // (WORD * i)
+        assert self.nblocks_for_size[length-1] >= 1
 
     def setup(self):
-        pass
+        self.ll_low_usage_lock = rthread.allocate_ll_lock()
+
+
+# ------------------------------------------------------------
 
 
 class StmGCThreadLocalAllocator(object):
@@ -25,11 +95,101 @@
         self.gc = sharedarea.gc
         self.sharedarea = sharedarea
         self.chained_list = NULL
+        #
+        # These two arrays contain each 'length' chained lists of pages.
+        # For each size N between WORD and 'small_request_threshold'
+        # (included), the corresponding chained list contains pages
+        # which store objects of size N.  The 'page_for_size' lists are
+        # for pages which still have room for at least one more object,
+        # and the 'full_page_for_size' lists are for full pages.
+        length = sharedarea.small_request_threshold / WORD + 1
+        self.page_for_size = lltype.malloc(rffi.CArray(PAGE_PTR), length,
+                                           flavor='raw', zero=True,
+                                           immortal=True)
+        self.full_page_for_size = lltype.malloc(rffi.CArray(PAGE_PTR), length,
+                                                flavor='raw', zero=True,
+                                                immortal=True)
+
+    def _malloc_size_class(self, size_class):
+        """Malloc one object of the given size_class (== number of WORDs)."""
+        ll_assert(size_class > 0, "malloc_size_class: null or neg size_class")
+        ll_assert(size_class <= self.sharedarea.small_request_threshold,
+                  "malloc_size_class: too big")
+        #
+        # Get the page to use
+        page = self.page_for_size[size_class]
+        if page == PAGE_NULL:
+            page = self._allocate_new_page(size_class)
+        #
+        # The result is simply 'page.freeblock'
+        result = page.freeblock
+        if page.nfree > 0:
+            #
+            # The 'result' was part of the chained list; read the next.
+            page.nfree -= 1
+            freeblock = result.address[0]
+            llarena.arena_reset(result,
+                                llmemory.sizeof(llmemory.Address),
+                                0)
+            #
+        else:
+            # The 'result' is part of the uninitialized blocks.
+            freeblock = result + nsize
+        #
+        page.freeblock = freeblock
+        #
+        pageaddr = llarena.getfakearenaaddress(llmemory.cast_ptr_to_adr(page))
+        if freeblock - pageaddr > self.page_size - nsize:
+            # This was the last free block, so unlink the page from the
+            # chained list and put it in the 'full_page_for_size' list.
+            self.page_for_size[size_class] = page.nextpage
+            page.nextpage = self.full_page_for_size[size_class]
+            self.full_page_for_size[size_class] = page
+        #
+        return result
+
+
+    def _allocate_new_page(self, size_class):
+        """Allocate and return a new page for the given size_class."""
+        #
+        # If 'low_usage_page' has pages ready, return one of them.
+        # Check both before acquiring the lock (NULL is the common case
+        # and getting it occasionally wrong is not a problem), and after.
+        result = NULL
+        sharedarea = self.sharedarea
+        if sharedarea.low_usage_page[size_class] != PAGE_NULL:
+            XXX   # self.ll_low_usage_lock...
+        #
+        # If unsuccessful, just raw-malloc a new page.
+        if not result:
+            result = llarena.arena_malloc(sharedarea.page_size, 0)
+            if not result:
+                fatalerror("FIXME: Out of memory! (should raise MemoryError)")
+                return PAGE_NULL
+            llarena.arena_reserve(result, llmemory.sizeof(PAGE_HEADER))
+        #
+        # Initialize the fields of the resulting page
+        page = llmemory.cast_adr_to_ptr(result, PAGE_PTR)
+        page.nfree = 0
+        page.freeblock = result + sharedarea.hdrsize
+        page.nextpage = PAGE_NULL
+        ll_assert(self.page_for_size[size_class] == PAGE_NULL,
+                  "allocate_new_page() called but a page is already waiting")
+        self.page_for_size[size_class] = page
+        return page
+
 
     def malloc_object(self, totalsize):
         """Malloc.  You should also call add_regular() later, or keep it in
         some other data structure.  Note that it is not zero-filled."""
-        return llarena.arena_malloc(llmemory.raw_malloc_usage(totalsize), 0)
+        nsize = llmemory.raw_malloc_usage(totalsize)
+        if nsize <= self.sharedarea.small_request_threshold:
+            result = self._malloc_size_class(nsize >> WORD_POWER_2)
+            llarena.arena_reserve(result, _dummy_size(totalsize))
+            return result
+        else:
+            XXX
+            #llarena.arena_malloc(llmemory.raw_malloc_usage(totalsize), 0)
 
     def add_regular(self, obj):
         """After malloc_object(), register the object in the internal chained
@@ -55,3 +215,13 @@
 
     def delete(self):
         free_non_gc_object(self)
+
+
+# ____________________________________________________________
+
+def _dummy_size(size):
+    if we_are_translated():
+        return size
+    if isinstance(size, int):
+        size = llmemory.sizeof(lltype.Char) * size
+    return size


More information about the pypy-commit mailing list