Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r298:c27e67b3f4ff
Date: 2013-06-27 09:16 +0200
http://bitbucket.org/pypy/stmgc/changeset/c27e67b3f4ff/

Log:    Improve the previous hack.

diff --git a/c4/dbgmem.c b/c4/dbgmem.c
--- a/c4/dbgmem.c
+++ b/c4/dbgmem.c
@@ -1,10 +1,10 @@
 #include "stmimpl.h"
+#include <sys/mman.h>
+
 
 #ifdef _GC_DEBUG
 /************************************************************/
 
-#include <sys/mman.h>
-
 #define PAGE_SIZE  4096
 #define MMAP_TOTAL  671088640   /* 640MB */
 
@@ -89,3 +89,40 @@
 
 /************************************************************/
 #endif
+
+
+void stm_clear_large_memory_chunk(void *base, size_t size,
+                                  size_t already_cleared)
+{
+    char *baseaddr = base;
+
+    if (size > 2 * PAGE_SIZE) {
+        int lowbits = ((intptr_t)baseaddr) & (PAGE_SIZE-1);
+        if (lowbits) {   /*  clear the initial misaligned part, if any */
+            int partpage = PAGE_SIZE - lowbits;
+            memset(baseaddr, 0, partpage);
+            baseaddr += partpage;
+            size -= partpage;
+        }
+        /* 'already_cleared' bytes at the end are assumed to be already
+           cleared.  Reduce 'size' accordingly, but avoid getting a
+           misaligned 'size'. */
+        size_t length = size & (-PAGE_SIZE);
+        if (already_cleared > (size - length)) {
+            already_cleared -= (size - length);
+            already_cleared &= -PAGE_SIZE;
+            length -= already_cleared;
+            size = length;
+            already_cleared = 0;
+        }
+
+        int err = madvise(baseaddr, length, MADV_DONTNEED);
+        if (err == 0) {   /*  madvise() worked */
+            baseaddr += length;
+            size -= length;
+        }
+    }
+    if (size > already_cleared) { /* clear the final misaligned part, if any */
+        memset(baseaddr, 0, size - already_cleared);
+    }
+}
diff --git a/c4/dbgmem.h b/c4/dbgmem.h
--- a/c4/dbgmem.h
+++ b/c4/dbgmem.h
@@ -15,5 +15,7 @@
 
 #endif
 
+void stm_clear_large_memory_chunk(void *, size_t, size_t);
+
 
 #endif
diff --git a/c4/nursery.c b/c4/nursery.c
--- a/c4/nursery.c
+++ b/c4/nursery.c
@@ -3,8 +3,6 @@
 
 static int is_in_nursery(struct tx_descriptor *d, gcptr obj)
 {
-    assert(!(d->nursery_base_for_next_collect <= (char*)obj &&
-             ((char*)obj) < d->nursery_base));
     return (d->nursery_base <= (char*)obj && ((char*)obj) < d->nursery_end);
 }
 
@@ -20,6 +18,10 @@
     stmcb_trace(obj, visit);
 }
 
+/* forward declarations */
+static int minor_collect_anything_to_do(struct tx_descriptor *);
+static gcptr allocate_next_section(size_t size, revision_t tid);
+
 /************************************************************/
 
 void stmgc_init_nursery(void)
@@ -31,7 +33,7 @@
     d->nursery_end = d->nursery_base + GC_NURSERY;  /* end of nursery */
     d->nursery_current = d->nursery_base;           /* current position */
     d->nursery_nextlimit = d->nursery_base;         /* next section limit */
-    d->nursery_base_for_next_collect = d->nursery_base;
+    d->nursery_cleared = NC_REGULAR;
 
     dprintf(("minor: nursery is at [%p to %p]\n", d->nursery_base,
              d->nursery_end));
@@ -40,7 +42,7 @@
 void stmgc_done_nursery(void)
 {
     struct tx_descriptor *d = thread_descriptor;
-    assert(!stmgc_minor_collect_anything_to_do(d));
+    assert(!minor_collect_anything_to_do(d));
     stm_free(d->nursery_base, GC_NURSERY);
 
     gcptrlist_delete(&d->old_objects_to_trace);
@@ -53,8 +55,6 @@
     d->nursery_current = d->nursery_end;
 }
 
-static gcptr allocate_next_section(size_t size, revision_t tid);  /* forward */
-
 inline static gcptr allocate_nursery(size_t size, revision_t tid)
 {
     /* if 'tid == -1', we must not collect */
@@ -540,29 +540,37 @@
     stm_free(d->nursery_base, GC_NURSERY);
     d->nursery_base = stm_malloc(GC_NURSERY);
     d->nursery_end = d->nursery_base + GC_NURSERY;
-    d->nursery_current = d->nursery_base;
     d->nursery_nextlimit = d->nursery_base;
     dprintf(("minor: nursery moved to [%p to %p]\n", d->nursery_base,
              d->nursery_end));
 #endif
 
-    /* when doing minor collections with the nursery "mostly empty",
+    /* When doing minor collections with the nursery "mostly empty",
        as occurs when other threads force major collections but this
-       thread didn't do much at all, then we don't actually reset
-       'nursery_current' to 'nursery_base'.  Instead we keep it
-       unmodified.  It will continue to use the already-partially-
-       cleared section.
+       thread didn't do much at all, then we clear the nursery using
+       the system's madvise().  The goal is twofold: first, if this
+       thread only uses very small amounts of memory, it avoids doing
+       a memset() to clear a complete section after every major GC.
+       Second, if the thread is really idle, then its nursery is sent
+       back to the system until it's really needed.
     */
-    if ((d->nursery_nextlimit - d->nursery_base) < GC_NURSERY / 8 &&
-        (d->nursery_nextlimit - d->nursery_current) > GC_NURSERY_SECTION / 4) {
+    if ((d->nursery_nextlimit - d->nursery_base) < GC_NURSERY / 10) {
+        size_t already_cleared = 0;
+        if (d->nursery_cleared == NC_ALREADY_CLEARED) {
+            already_cleared = d->nursery_end - d->nursery_current;
+        }
+        stm_clear_large_memory_chunk(d->nursery_base, GC_NURSERY,
+                                     already_cleared);
+        d->nursery_cleared = NC_ALREADY_CLEARED;
     }
     else {
-        d->nursery_current = d->nursery_base;
-        d->nursery_nextlimit = d->nursery_base;
+        d->nursery_cleared = NC_REGULAR;
     }
-    d->nursery_base_for_next_collect = d->nursery_current;
 
-    assert(!stmgc_minor_collect_anything_to_do(d));
+    d->nursery_current = d->nursery_base;
+    d->nursery_nextlimit = d->nursery_base;
+
+    assert(!minor_collect_anything_to_do(d));
 }
 
 void stmgc_minor_collect(void)
@@ -579,9 +587,10 @@
     minor_collect(d);
 }
 
-int stmgc_minor_collect_anything_to_do(struct tx_descriptor *d)
+#ifndef NDEBUG
+int minor_collect_anything_to_do(struct tx_descriptor *d)
 {
-    if (d->nursery_current == d->nursery_base_for_next_collect /*&&
+    if (d->nursery_current == d->nursery_base /*&&
         !g2l_any_entry(&d->young_objects_outside_nursery)*/ ) {
         /* there is no young object */
         assert(gcptrlist_size(&d->public_with_young_copy) == 0);
@@ -600,6 +609,7 @@
         return 1;
     }
 }
+#endif
 
 static gcptr allocate_next_section(size_t allocate_size, revision_t tid)
 {
@@ -641,13 +651,13 @@
         stmgc_minor_collect();
         stmgcpage_possibly_major_collect(0);
 
-        assert(d->nursery_base_for_next_collect == d->nursery_base);
         assert(d->nursery_current == d->nursery_base);
         assert(d->nursery_nextlimit == d->nursery_base);
     }
 
     /* Clear the next section */
-    memset(d->nursery_nextlimit, 0, GC_NURSERY_SECTION);
+    if (d->nursery_cleared != NC_ALREADY_CLEARED)
+        memset(d->nursery_nextlimit, 0, GC_NURSERY_SECTION);
     d->nursery_nextlimit += GC_NURSERY_SECTION;
 
     /* Return the object from there */
diff --git a/c4/nursery.h b/c4/nursery.h
--- a/c4/nursery.h
+++ b/c4/nursery.h
@@ -25,7 +25,7 @@
     char *nursery_nextlimit;                                            \
     char *nursery_end;                                                  \
     char *nursery_base;                                                 \
-    char *nursery_base_for_next_collect;                                \
+    enum { NC_REGULAR, NC_ALREADY_CLEARED } nursery_cleared;            \
                                                                         \
     /* Between collections, we add to 'old_objects_to_trace' the        \
        private objects that are old but may contain pointers to         \
@@ -56,7 +56,6 @@
 void stmgc_done_nursery(void);
 void stmgc_minor_collect(void);
 void stmgc_minor_collect_no_abort(void);
-int stmgc_minor_collect_anything_to_do(struct tx_descriptor *);
 gcptr stmgc_duplicate(gcptr);
 gcptr stmgc_duplicate_old(gcptr);
 size_t stmgc_size(gcptr);
diff --git a/c4/test/support.py b/c4/test/support.py
--- a/c4/test/support.py
+++ b/c4/test/support.py
@@ -84,6 +84,7 @@
     void *STUB_THREAD(gcptr);
     void stm_clear_read_cache(void);
     int in_nursery(gcptr);
+    void stm_clear_large_memory_chunk(void *, size_t, size_t);
 
     gcptr getptr(gcptr, long);
     void setptr(gcptr, long, gcptr);
@@ -95,6 +96,8 @@
     long rawgetlong(gcptr, long);
     void rawsetlong(gcptr, long, long);
 
+    void *memset(void *s, int c, size_t n);
+
     gcptr pseudoprebuilt(size_t size, int tid);
     gcptr pseudoprebuilt_with_hash(size_t size, int tid, revision_t hash);
     revision_t get_private_rev_num(void);
diff --git a/c4/test/test_dbgmem.py b/c4/test/test_dbgmem.py
new file mode 100644
--- /dev/null
+++ b/c4/test/test_dbgmem.py
@@ -0,0 +1,45 @@
+from support import *
+import random
+
+
+def test_clear_large_memory_chunk():
+    PAGE_SIZE = 4096
+    blocksize = 8 * PAGE_SIZE
+    block = ffi.new("char[]", blocksize)
+    blockbase = int(ffi.cast("intptr_t", block))
+
+    for i in range(1000):
+
+        if random.random() < 0.25:
+            base = PAGE_SIZE - (blockbase & (PAGE_SIZE-1))
+            assert ((blockbase + base) & (PAGE_SIZE-1)) == 0
+        else:
+            base = random.randrange(1, PAGE_SIZE+1)
+        assert base > 0
+
+        length = random.randrange(1, 6 * PAGE_SIZE)
+        if random.random() < 0.25:
+            blockend = blockbase + base + length
+            blockend &= ~(PAGE_SIZE-1)
+            length = blockend - (blockbase + base)
+            if length <= 0:
+                continue
+
+        assert length > 0
+        assert base + length + 1 < blocksize
+
+        already_cleared = random.randrange(0, length+1)
+        already_cleared &= ((1 << random.randrange(0, 16)) - 1)
+        assert 0 <= already_cleared <= length
+
+        block[base - 1] = '<'
+        lib.memset(block + base, ord('.'), length - already_cleared)
+        lib.memset(block + base + length - already_cleared, 0, already_cleared)
+        block[base + length] = '>'
+
+        print hex(blockbase + base), hex(length), hex(already_cleared)
+        lib.stm_clear_large_memory_chunk(block + base, length, already_cleared)
+
+        assert block[base - 1] == '<'
+        assert block[base + length] == '>'
+        assert ffi.buffer(block + base, length)[:] == '\x00' * length
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to