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