8000 py/gc: Speed up incremental GC cycles by tracking the high watermark. · micropython/micropython@f841525 · GitHub
[go: up one dir, main page]

Skip to content

Commit f841525

Browse files
committed
py/gc: Speed up incremental GC cycles by tracking the high watermark.
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. Speed 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.
1 parent 988b6e2 commit f841525

File tree

2 files changed

+24
-2
lines changed

2 files changed

+24
-2
lines changed

py/gc.c

Lines changed: 23 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_high_watermark = 0;
161162

162163
#if MICROPY_GC_SPLIT_HEAP
163164
area->next = NULL;
@@ -380,7 +381,14 @@ STATIC void gc_sweep(void) {
380381
// free unmarked heads and their tails
381382
int free_tail = 0;
382383
for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) {
383-
for (size_t block = 0; block < area->gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) {
384+
size_t end_block = area->gc_alloc_table_byte_len * BLOCKS_PER_ATB;
385+
if (area->gc_high_watermark < end_block) {
386+
end_block = area->gc_high_watermark + 1;
387+
}
388+
389+
size_t last_block = 0;
390+
391+
for (size_t block = 0; block < end_block; block++) {
384392
MICROPY_GC_HOOK_LOOP
385393
switch (ATB_GET_KIND(area, block)) {
386394
case AT_HEAD:
@@ -421,14 +429,18 @@ STATIC void gc_sweep(void) {
421429
memset((void *)PTR_FROM_BLOCK(area, block), 0, BYTES_PER_BLOCK);
422430
#endif
423431
}
432+
last_block = block;
424433
break;
425434

426435
case AT_MARK:
427436
ATB_MARK_TO_HEAD(area, block);
428437
free_tail = 0;
438+
last_block = block;
429439
break;
430440
}
431441
}
442+
443+
area->gc_high_watermark = last_block;
432444
}
433445
}
434446

@@ -680,6 +692,10 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
680692
area->gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB;
681693
}
682694

695+
if (area->gc_high_watermark < end_block) {
696+
area->gc_high_watermark = end_block;
697+
}
698+
683699
// mark first block as used head
684700
ATB_FREE_TO_HEAD(area, start_block);
685701

@@ -969,11 +985,16 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
969985
// check if we can expand in place
970986
if (new_blocks <= n_blocks + n_free) {
971987
// mark few more blocks as used tail
972-
for (size_t bl = block + n_blocks; bl < block + new_blocks; bl++) {
988+
size_t end_block = block + new_blocks;
989+
for (size_t bl = block + n_blocks; bl < end_block; bl++) {
973990
assert(ATB_GET_KIND(area, bl) == AT_FREE);
974991
ATB_FREE_TO_TAIL(area, bl);
975992
}
976993

994+
if (area->gc_high_watermark < end_block) {
995+
area->gc_high_watermark = end_block;
996+
}
997+
977998
GC_EXIT();
978999

9791000
#if MICROPY_GC_CONSERVATIVE_CLEAR

py/mpstate.h

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

8989
size_t gc_last_free_atb_index;
90+
size_t gc_high_watermark; // The block ID of the highest block allocated in the area
9091
} mp_state_mem_area_t;
9192

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

0 commit comments

Comments
 (0)
0