8000 py/gc: Speed up incremental GC cycles by tracking the last used block. · micropython/micropython@2dcd745 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2dcd745

Browse files
damzdpgeorge
authored andcommitted
py/gc: Speed up incremental GC cycles by tracking the last used block.
In applications that use little memory and run GC regularly, the cost of the sweep phase quickly becomes prohibitives as the amount of RAM increases. On an ESP32-S3 with 2 MB of external SPIRAM, for example, a trivial GC cycle takes a minimum of 40ms, virtually all of it in the sweep phase. Similarly, on the UNIX port with 1 GB of heap, a trivial GC takes 47 ms, again virtually all of it in the sweep phase. This commit speeds up the sweep phase in the case most of the heap is empty by keeping track of the ID of the highest block we allocated in an area since the last GC. The performance benchmark run on PYBV10 shows between +0 and +2% improvement across the existing performance tests. These tests don't really stress the GC, so they were also run with gc.threshold(30000) and gc.threshold(10000). For the 30000 case, performance improved by up to +10% with this commit. For the 10000 case, performance improved by at least +10% on 6 tests, and up to +25%. Signed-off-by: Damien George <damien@micropython.org>
1 parent 70c5643 commit 2dcd745

File tree

2 files changed

+21
-2
lines changed

2 files changed

+21
-2
lines changed

py/gc.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ STATIC void gc_setup_area(mp_state_mem_area_t *area, void *start, void *end) {
158158
#endif
159159

160160
area->gc_last_free_atb_index = 0;
161+
area->gc_last_used_block = 0;
161162

162163
#if MICROPY_GC_SPLIT_HEAP
163164
area->next = NULL;
@@ -378,7 +379,14 @@ STATIC void gc_sweep(void) {
378379
// free unmarked heads and their tails
379380
int free_tail = 0;
380381
for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) {
381-
for (size_t block = 0; block < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) {
382+
size_t end_block = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB;
383+
if (area->gc_last_used_block < end_block) {
384+
end_block = area->gc_last_used_block + 1;
385+
}
386+
387+
size_t last_used_block = 0;
388+
389+
for (size_t block = 0; block < end_block; block++) {
382390
MICROPY_GC_HOOK_LOOP(block);
383391
switch (ATB_GET_KIND(area, block)) {
384392
case AT_HEAD:
@@ -418,15 +426,20 @@ STATIC void gc_sweep(void) {
418426
#if CLEAR_ON_SWEEP
419427
memset((void *)PTR_FROM_BLOCK(area, block), 0, BYTES_PER_BLOCK);
420428
#endif
429+
} else {
430+
last_used_block = block;
421431
}
422432
break;
423433

424434
case AT_MARK:
425435
ATB_MARK_TO_HEAD(area, block);
426436
free_tail = 0;
437+
last_used_block = block;
427438
break;
428439
}
429440
}
441+
442+
area->gc_last_used_block = last_used_block;
430443
}
431444
}
432445

@@ -680,6 +693,8 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
680693
area->gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB;
681694
}
682695

696+
area->gc_last_used_block = MAX(area->gc_last_used_block, end_block);
697+
683698
// mark first block as used head
684699
ATB_FREE_TO_HEAD(area, start_block);
685700

@@ -971,11 +986,14 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
971986
// check if we can expand in place
972987
if (new_blocks <= n_blocks + n_free) {
973988
// mark few more blocks as used tail
974-
for (size_t bl = block + n_blocks; bl < block + new_blocks; bl++) {
989+
size_t end_block = block + new_blocks;
990+
for (size_t bl = block + n_blocks; bl < end_block; bl++) {
975991
assert(ATB_GET_KIND(area, bl) == AT_FREE);
976992
ATB_FREE_TO_TAIL(area, bl);
977993
}
978994

995+
area->gc_last_used_block = MAX(area->gc_last_used_block, end_block);
996+
979997
GC_EXIT();
980998

981999
#if MICROPY_GC_CONSERVATIVE_CLEAR

py/mpstate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ typedef struct _mp_state_mem_area_t {
9393
byte *gc_pool_end;
9494

9595
size_t gc_last_free_atb_index;
96+
size_t gc_last_used_block; // The block ID of the highest block allocated in the area
9697
} mp_state_mem_area_t;
9798

9899
// This structure hold information about the memory allocation system.

0 commit comments

Comments
 (0)
0