summaryrefslogtreecommitdiffstatshomepage
path: root/py
diff options
context:
space:
mode:
Diffstat (limited to 'py')
-rw-r--r--py/gc.c74
1 files changed, 64 insertions, 10 deletions
diff --git a/py/gc.c b/py/gc.c
index a8463fe9b0..969b6edd29 100644
--- a/py/gc.c
+++ b/py/gc.c
@@ -46,6 +46,9 @@
#define DEBUG_printf(...) (void)0
#endif
+// make this 1 to dump the heap each time it changes
+#define EXTENSIVE_HEAP_PROFILING (0)
+
#define WORDS_PER_BLOCK (4)
#define BYTES_PER_BLOCK (WORDS_PER_BLOCK * BYTES_PER_WORD)
#define STACK_SIZE (64) // tunable; minimum is 1
@@ -386,13 +389,6 @@ void *gc_alloc(mp_uint_t n_bytes, bool has_finaliser) {
if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; }
if (ATB_3_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3; goto found; } } else { n_free = 0; }
}
- for (i = 0; i < gc_last_free_atb_index; i++) {
- byte a = gc_alloc_table_start[i];
- if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; }
- if (ATB_1_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1; goto found; } } else { n_free = 0; }
- if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; }
- if (ATB_3_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3; goto found; } } else { n_free = 0; }
- }
// nothing found!
if (collected) {
@@ -409,8 +405,14 @@ found:
end_block = i;
start_block = i - n_free + 1;
- // set last free ATB index to last block we found, for start of next scan
- gc_last_free_atb_index = i / BLOCKS_PER_ATB;
+ // Set last free ATB index to block after last block we found, for start of
+ // next scan. To reduce fragmentation, we only do this if we were looking
+ // for a single free block, which guarantees that there are no free blocks
+ // before this one. Also, whenever we free or shink a block we must check
+ // if this index needs adjusting (see gc_realloc and gc_free).
+ if (n_free == 1) {
+ gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB;
+ }
// mark first block as used head
ATB_FREE_TO_HEAD(start_block);
@@ -441,6 +443,10 @@ found:
}
#endif
+ #if EXTENSIVE_HEAP_PROFILING
+ gc_dump_alloc_table();
+ #endif
+
return ret_ptr;
}
@@ -467,11 +473,20 @@ void gc_free(void *ptr_in) {
if (VERIFY_PTR(ptr)) {
mp_uint_t block = BLOCK_FROM_PTR(ptr);
if (ATB_GET_KIND(block) == AT_HEAD) {
+ // set the last_free pointer to this block if it's earlier in the heap
+ if (block / BLOCKS_PER_ATB < gc_last_free_atb_index) {
+ gc_last_free_atb_index = block / BLOCKS_PER_ATB;
+ }
+
// free head and all of its tail blocks
do {
ATB_ANY_TO_FREE(block);
block += 1;
} while (ATB_GET_KIND(block) == AT_TAIL);
+
+ #if EXTENSIVE_HEAP_PROFILING
+ gc_dump_alloc_table();
+ #endif
}
}
}
@@ -583,6 +598,16 @@ void *gc_realloc(void *ptr_in, mp_uint_t n_bytes) {
for (mp_uint_t bl = block + new_blocks; ATB_GET_KIND(bl) == AT_TAIL; bl++) {
ATB_ANY_TO_FREE(bl);
}
+
+ // set the last_free pointer to end of this block if it's earlier in the heap
+ if ((block + new_blocks) / BLOCKS_PER_ATB < gc_last_free_atb_index) {
+ gc_last_free_atb_index = (block + new_blocks) / BLOCKS_PER_ATB;
+ }
+
+ #if EXTENSIVE_HEAP_PROFILING
+ gc_dump_alloc_table();
+ #endif
+
return ptr_in;
}
@@ -597,6 +622,10 @@ void *gc_realloc(void *ptr_in, mp_uint_t n_bytes) {
// zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc)
memset((byte*)ptr_in + n_bytes, 0, new_blocks * BYTES_PER_BLOCK - n_bytes);
+ #if EXTENSIVE_HEAP_PROFILING
+ gc_dump_alloc_table();
+ #endif
+
return ptr_in;
}
@@ -630,9 +659,34 @@ void gc_dump_info() {
}
void gc_dump_alloc_table(void) {
+ static const mp_uint_t DUMP_BYTES_PER_LINE = 64;
+ #if !EXTENSIVE_HEAP_PROFILING
+ // When comparing heap output we don't want to print the starting
+ // pointer of the heap because it changes from run to run.
printf("GC memory layout; from %p:", gc_pool_start);
+ #endif
for (mp_uint_t bl = 0; bl < gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) {
- if (bl % 64 == 0) {
+ if (bl % DUMP_BYTES_PER_LINE == 0) {
+ // a new line of blocks
+ #if EXTENSIVE_HEAP_PROFILING
+ {
+ // check if this line contains only free blocks
+ bool only_free_blocks = true;
+ for (mp_uint_t bl2 = bl; bl2 < gc_alloc_table_byte_len * BLOCKS_PER_ATB && bl2 < bl + DUMP_BYTES_PER_LINE; bl2++) {
+ if (ATB_GET_KIND(bl2) != AT_FREE) {
+
+ only_free_blocks = false;
+ break;
+ }
+ }
+ if (only_free_blocks) {
+ // line contains only free blocks, so skip printing it
+ bl += DUMP_BYTES_PER_LINE - 1;
+ continue;
+ }
+ }
+ #endif
+ // print header for new line of blocks
printf("\n%04x: ", (uint)bl);
}
int c = ' ';