[pypy-commit] pypy concurrent-marksweep: Started.
arigo
noreply at buildbot.pypy.org
Fri Oct 7 08:25:35 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: concurrent-marksweep
Changeset: r47849:3bcb6e4544b8
Date: 2011-10-06 16:18 +0200
http://bitbucket.org/pypy/pypy/changeset/3bcb6e4544b8/
Log: Started.
diff --git a/pypy/rpython/memory/gc/concurrentms.py b/pypy/rpython/memory/gc/concurrentms.py
new file mode 100644
--- /dev/null
+++ b/pypy/rpython/memory/gc/concurrentms.py
@@ -0,0 +1,141 @@
+from pypy.rpython.lltypesystem import lltype, llmemory, llarena, rffi
+from pypy.rpython.lltypesystem.lloperation import llop
+from pypy.rpython.lltypesystem.llmemory import raw_malloc_usage
+from pypy.rlib.debug import ll_assert
+from pypy.rlib.rarithmetic import LONG_BIT
+from pypy.rpython.memory.gc.base import GCBase
+
+#
+# A "mostly concurrent" mark&sweep GC. It can delegate most of the GC
+# operations to a separate thread, which runs concurrently with the
+# mutator (i.e. the rest of the program). Based on the idea that the
+# concurrent collection should be relatively fast --- 20-25% of the
+# time? after which the collector thread just sleeps --- it uses a
+# snapshot-at-the-beginning technique with a "deletion barrier", i.e. a
+# write barrier that prevents changes to objects that have not been
+# scanned yet (Abraham and Patel, Yuasa).
+#
+# Reference: The Garbage Collection Handbook, Richard Jones and Antony
+# Hosking and Eliot Moss.
+#
+
+WORD = LONG_BIT // 8
+NULL = llmemory.NULL
+WORD_POWER_2 = {32: 2, 64: 3}[LONG_BIT]
+assert 1 << WORD_POWER_2 == WORD
+size_of_addr = llmemory.sizeof(llmemory.Address)
+
+
+class MostlyConcurrentMarkSweepGC(GCBase):
+ _alloc_flavor_ = "raw"
+ inline_simple_malloc = True
+ inline_simple_malloc_varsize = True
+ needs_write_barrier = True
+ prebuilt_gc_objects_are_static_roots = False
+ malloc_zero_filled = True
+ #gcflag_extra = GCFLAG_FINALIZATION_ORDERING
+
+ HDR = lltype.Struct('header', ('tid', lltype.Signed))
+ typeid_is_in_field = 'tid'
+
+ TRANSLATION_PARAMS = {'page_size': 4096,
+ 'small_request_threshold': 35*WORD,
+ }
+
+ def __init__(self, config, page_size=64, small_request_threshold=24,
+ **kwds):
+ # 'small_request_threshold' is the largest size that we will
+ # satisfy using our own pages mecanism. Larger requests just
+ # go to the system malloc().
+ GCBase.__init__(self, config, **kwds)
+ assert small_request_threshold % WORD == 0
+ self.small_request_threshold = small_request_threshold
+ self.page_size = page_size
+ self.free_pages = NULL
+ length = small_request_threshold // WORD + 1
+ self.free_lists = lltype.malloc(rffi.CArray(llmemory.Address),
+ length, flavor='raw', zero=True,
+ immortal=True)
+
+ def combine(self, typeid16, flags):
+ return llop.combine_ushort(lltype.Signed, typeid16, flags)
+
+ def malloc_fixedsize_clear(self, typeid, size,
+ needs_finalizer=False, contains_weakptr=False):
+ assert not needs_finalizer # XXX
+ assert not contains_weakptr # XXX
+ size_gc_header = self.gcheaderbuilder.size_gc_header
+ totalsize = size_gc_header + size
+ rawtotalsize = raw_malloc_usage(totalsize)
+ if rawtotalsize <= self.small_request_threshold:
+ n = (rawtotalsize + WORD - 1) >> WORD_POWER_2
+ result = self.free_lists[n]
+ if result != llmemory.NULL:
+ self.free_lists[n] = result.address[0]
+ #
+ llarena.arena_reset(result, size_of_addr, 0)
+ llarena.arena_reserve(result, totalsize)
+ hdr = llmemory.cast_adr_to_ptr(result, lltype.Ptr(self.HDR))
+ hdr.tid = self.combine(typeid, flags=0)
+ #
+ obj = result + size_gc_header
+ return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+ #
+ return self._malloc_slowpath(typeid, size)
+
+ def _malloc_slowpath(self, typeid, size):
+ size_gc_header = self.gcheaderbuilder.size_gc_header
+ totalsize = size_gc_header + size
+ rawtotalsize = raw_malloc_usage(totalsize)
+ if rawtotalsize <= self.small_request_threshold:
+ #
+ # Case 1: we have run out of the free list corresponding to
+ # the size. Grab the next free page.
+ newpage = self.free_pages
+ if newpage == llmemory.NULL:
+ self.allocate_next_arena()
+ newpage = self.free_pages
+ self.free_pages = newpage.address[0]
+ llarena.arena_reset(newpage, size_of_addr, 0)
+ #
+ # Initialize the free page to contain objects of the given
+ # size. This requires setting up all object locations in the
+ # page, linking them in the free list.
+ n = (rawtotalsize + WORD - 1) >> WORD_POWER_2
+ head = self.free_lists[n]
+ ll_assert(not head, "_malloc_slowpath: unexpected free_lists[n]")
+ i = self.page_size - rawtotalsize
+ while i >= rawtotalsize:
+ llarena.arena_reserve(newpage + i, size_of_addr)
+ (newpage + i).address[0] = head
+ head = newpage + i
+ i -= rawtotalsize
+ self.free_lists[n] = head
+ result = head - rawtotalsize
+ #
+ # Done: all object locations are linked, apart from 'result',
+ # which is the first object location in the page. Note that
+ # if the size is not a multiple of 2, there are a few wasted
+ # WORDs, which we place at the start of the page rather than
+ # at the end (Hans Boehm, xxx ref).
+ llarena.arena_reserve(result, totalsize)
+ hdr = llmemory.cast_adr_to_ptr(result, lltype.Ptr(self.HDR))
+ hdr.tid = self.combine(typeid, flags=0)
+ #
+ obj = result + size_gc_header
+ return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
+ #
+ else:
+ # Case 2: the object is too big, so allocate it directly
+ # with the system malloc().
+ xxxxx
+ _malloc_slowpath._dont_inline_ = True
+
+ def allocate_next_arena(self):
+ # xxx for now, allocate one page at a time with the system malloc()
+ page = llarena.arena_malloc(self.page_size, 2) # zero-filled
+ ll_assert(bool(page), "out of memory!")
+ llarena.arena_reserve(page, size_of_addr)
+ page.address[0] = NULL
+ self.free_pages = page
+ allocate_next_arena._dont_inline_ = True
diff --git a/pypy/rpython/memory/gc/test/test_direct.py b/pypy/rpython/memory/gc/test/test_direct.py
--- a/pypy/rpython/memory/gc/test/test_direct.py
+++ b/pypy/rpython/memory/gc/test/test_direct.py
@@ -599,3 +599,7 @@
class TestMiniMarkGCFull(DirectGCTest):
from pypy.rpython.memory.gc.minimark import MiniMarkGC as GCClass
+
+class TestMostlyConcurrentMarkSweepGC(DirectGCTest):
+ from pypy.rpython.memory.gc.concurrentms \
+ import MostlyConcurrentMarkSweepGC as GCClass
More information about the pypy-commit
mailing list