[pypy-commit] stmgc default: In-progress: resharing of pages. Doesn't work right now, so is not enabled.

arigo noreply at buildbot.pypy.org
Sun Mar 2 12:30:27 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r922:2e8187050db2
Date: 2014-03-02 12:30 +0100
http://bitbucket.org/pypy/stmgc/changeset/2e8187050db2/

Log:	In-progress: resharing of pages. Doesn't work right now, so is not
	enabled.

diff --git a/c7/stm/contention.c b/c7/stm/contention.c
--- a/c7/stm/contention.c
+++ b/c7/stm/contention.c
@@ -50,7 +50,9 @@
     uint8_t prev_owner = ((volatile uint8_t *)write_locks)[lock_idx];
     if (prev_owner != 0 && prev_owner != STM_PSEGMENT->write_lock_num) {
 
-        uint8_t other_segment_num = prev_owner - 1;
+        uint8_t other_segment_num = prev_owner - _SINGLE_SEGMENT_PAGE;
+        assert(get_priv_segment(other_segment_num)->write_lock_num ==
+               prev_owner);
         contention_management(other_segment_num);
 
         /* The rest of this code is for the case where we continue to
diff --git a/c7/stm/gcpage.c b/c7/stm/gcpage.c
--- a/c7/stm/gcpage.c
+++ b/c7/stm/gcpage.c
@@ -189,6 +189,112 @@
     }
 }
 
+/************************************************************/
+
+
+static inline void mark_single_flag_private(uintptr_t pagenum, uint8_t flagnum)
+{
+    uint8_t old_flag = flag_page_private[pagenum];
+
+    if (old_flag == SHARED_PAGE)  /* nothing to do, page already shared */
+        return;
+
+    if (old_flag == flagnum)      /* page already marked for this segment */
+        return;
+
+    if (old_flag == PRIVATE_PAGE) {    /* a not-seen-before private page */
+        flag_page_private[pagenum] = flagnum;
+        return;
+    }
+
+    /* else, conflict: the page has been seen from two different segments.
+       Use REMAPPING_PAGE to mean this situation here. */
+    flag_page_private[pagenum] = REMAPPING_PAGE;
+}
+
+static inline void mark_flag_page_private(object_t *obj, uint8_t flag_num,
+                                          char *segment_base)
+{
+    uintptr_t first_page = ((uintptr_t)obj) / 4096UL;
+
+    if (LIKELY((obj->stm_flags & GCFLAG_SMALL_UNIFORM) != 0)) {
+        mark_single_flag_private(first_page, flag_num);
+    }
+    else {
+        char *realobj;
+        size_t obj_size;
+        uintptr_t end_page;
+
+        /* get the size of the object */
+        realobj = REAL_ADDRESS(segment_base, obj);
+        obj_size = stmcb_size_rounded_up((struct object_s *)realobj);
+
+        /* that's the page *following* the last page with the object */
+        end_page = (((uintptr_t)obj) + obj_size + 4095) / 4096UL;
+
+        while (first_page < end_page)
+            mark_single_flag_private(first_page++, flag_num);
+    }
+}
+
+static void major_reshare_pages_range(uintptr_t first_page, uintptr_t end_page)
+{
+    uintptr_t i;
+    for (i = first_page; i < end_page; i++) {
+
+        uint8_t flag = flag_page_private[i];
+
+        if (flag == REMAPPING_PAGE) {
+            /* this page stays private after major collection */
+            flag_page_private[i] = PRIVATE_PAGE;
+        }
+        else if (flag >= PRIVATE_PAGE) {
+            /* this page becomes shared again */
+
+            /* XXX rather slow version here.  improve! */
+
+            abort(); /* doesn't work, actually.  we can't keep object data
+                        from segment 1 and largemalloc's chunk data from
+                        segment 0.  mess mess mess */
+
+            char buffer[4096 + 64];
+            char *pbuffer = buffer;
+            pbuffer += ((-(uintptr_t)pbuffer) & 63);   /* align */
+
+            char *ppage0 = get_segment_base(0) + i * 4096;
+            char *ppage1 = get_segment_base(1) + i * 4096;
+
+            /* do two copies: out of the page seen now as in the seg 0,
+               and then back into the same location after remapping */
+            pagecopy(pbuffer, ppage0);
+            /* a better approach is possible in which we don't have this */
+            madvise(ppage0, 4096, MADV_DONTNEED);
+            madvise(ppage1, 4096, MADV_DONTNEED);
+            d_remap_file_pages(ppage0, 4096, i);
+            d_remap_file_pages(ppage1, 4096, i);
+            pagecopy(ppage0, pbuffer);
+            flag_page_private[i] = SHARED_PAGE;
+
+            increment_total_allocated(-4096 * (NB_SEGMENTS-1));
+        }
+    }
+}
+
+static void major_reshare_pages(void)
+{
+    /* re-share pages if possible.  Each re-sharing decreases
+       total_allocated by 4096. */
+    major_reshare_pages_range(
+        END_NURSERY_PAGE,
+        (uninitialized_page_start - stm_object_pages) / 4096UL);
+    major_reshare_pages_range(
+        (uninitialized_page_stop - stm_object_pages) / 4096UL,
+        NB_PAGES);
+}
+
+/************************************************************/
+
+
 static inline void mark_record_trace(object_t **pobj)
 {
     /* takes a normal pointer to a thread-local pointer to an object */
@@ -198,13 +304,34 @@
         return;    /* already visited this object */
 
     LIST_APPEND(mark_objects_to_trace, obj);
+
+    /* Note: this obj might be visited already, but from a different
+       segment.  We ignore this case and skip re-visiting the object
+       anyway.  The idea is that such an object is old (not from the
+       current transaction), otherwise it would not be possible to see
+       it in two segments; and moreover it is not modified, otherwise
+       mark_trace() would have been called on two different segments
+       already.  That means that this object is identical in all
+       segments and only needs visiting once.  (It may actually be in a
+       shared page, or maybe not.)
+    */
 }
 
 static void mark_trace(object_t *obj, char *segment_base)
 {
+    uint8_t flag_num =
+        ((struct stm_priv_segment_info_s *)
+             REAL_ADDRESS(segment_base, STM_PSEGMENT))->write_lock_num;
+
     assert(list_is_empty(mark_objects_to_trace));
 
     while (1) {
+
+        /* first update the flag in flag_page_private[] to correspond
+           to this segment */
+        if (0) mark_flag_page_private(obj, flag_num, segment_base);
+
+        /* trace into the object (the version from 'segment_base') */
         struct object_s *realobj =
             (struct object_s *)REAL_ADDRESS(segment_base, obj);
         stmcb_trace(realobj, &mark_record_trace);
@@ -373,7 +500,7 @@
 static void major_collection_now_at_safe_point(void)
 {
     dprintf(("\n"));
-    dprintf((" .----- major_collection_now_at_safe_point -----\n"));
+    dprintf((" .----- major collection -----------------------\n"));
     assert(_has_mutex());
 
     /* first, force a minor collection in each of the other segments */
@@ -396,6 +523,7 @@
 
     /* sweeping */
     mutex_pages_lock();
+    if (0) major_reshare_pages();
     sweep_large_objects();
     //sweep_uniform_pages();
     mutex_pages_unlock();
@@ -403,9 +531,6 @@
     clean_write_locks();
     major_set_write_locks();
 
-    /* XXX should re-share pages if possible; and each re-sharing
-       decreases total_allocated by 4096 */
-
     dprintf((" | used after collection:  %ld\n",
              (long)pages_ctl.total_allocated));
     dprintf((" `----------------------------------------------\n"));
diff --git a/c7/stm/pages.h b/c7/stm/pages.h
--- a/c7/stm/pages.h
+++ b/c7/stm/pages.h
@@ -13,6 +13,10 @@
 
     /* Page is private for each segment. */
     PRIVATE_PAGE,
+
+    /* Higher values are used by gcpage.c to mark pages that are privatized
+       but where so far only one segment was found. */
+    _SINGLE_SEGMENT_PAGE
 };
 
 static uint8_t flag_page_private[NB_PAGES];
diff --git a/c7/stm/setup.c b/c7/stm/setup.c
--- a/c7/stm/setup.c
+++ b/c7/stm/setup.c
@@ -49,8 +49,8 @@
                      PROT_NONE);
 
         struct stm_priv_segment_info_s *pr = get_priv_segment(i);
-        assert(i + 1 <= 255);
-        pr->write_lock_num = i + 1;
+        assert(_SINGLE_SEGMENT_PAGE + i <= 255);
+        pr->write_lock_num = _SINGLE_SEGMENT_PAGE + i;
         pr->pub.segment_num = i;
         pr->pub.segment_base = segment_base;
         pr->objects_pointing_to_nursery = NULL;
diff --git a/c7/test/test_gcpage.py b/c7/test/test_gcpage.py
--- a/c7/test/test_gcpage.py
+++ b/c7/test/test_gcpage.py
@@ -182,3 +182,28 @@
 
     def test_trace_correct_version_of_overflow_objects_2(self):
         self.test_trace_correct_version_of_overflow_objects_1(size=5000)
+
+    def test_reshare_if_no_longer_modified_0(self, invert=0):
+        if invert:
+            self.switch(1)
+        self.start_transaction()
+        x = stm_allocate(5000)
+        self.push_root(x)
+        self.commit_transaction()
+        x = self.pop_root()
+        #
+        self.switch(1 - invert)
+        self.start_transaction()
+        self.push_root(x)
+        stm_set_char(x, 'A')
+        stm_major_collect()
+        assert lib._stm_total_allocated() == 5000 + LMO + 2 * 4096  # 2 pages
+        self.commit_transaction()
+        #
+        self.start_transaction()
+        stm_major_collect()
+        py.test.skip("XXX implement me")
+        assert lib._stm_total_allocated() == 5000 + LMO    # shared again
+
+    def test_reshare_if_no_longer_modified_1(self):
+        self.test_reshare_if_no_longer_modified_0(invert=1)
diff --git a/c7/test/test_random.py b/c7/test/test_random.py
--- a/c7/test/test_random.py
+++ b/c7/test/test_random.py
@@ -364,6 +364,7 @@
     thread_state.push_roots(ex)
 
     ex.do('%s = stm_allocate(%s)' % (r, size))
+    ex.do('# 0x%x' % (int(ffi.cast("uintptr_t", ex.content[r]))))
     thread_state.transaction_state.add_root(r, 0, True)
 
     thread_state.pop_roots(ex)
@@ -375,6 +376,7 @@
     r = global_state.get_new_root_name(True, num)
     thread_state.push_roots(ex)
     ex.do('%s = stm_allocate_refs(%s)' % (r, num))
+    ex.do('# 0x%x' % (int(ffi.cast("uintptr_t", ex.content[r]))))
     thread_state.transaction_state.add_root(r, "ffi.NULL", True)
 
     thread_state.pop_roots(ex)


More information about the pypy-commit mailing list