diff --git a/py/gc.c b/py/gc.c index 1c1865cdb4..97868c07f8 100644 --- a/py/gc.c +++ b/py/gc.c @@ -152,6 +152,12 @@ void gc_init(void *start, void *end) { // allow auto collection MP_STATE_MEM(gc_auto_collect_enabled) = 1; + #if MICROPY_GC_ALLOC_THRESHOLD + // by default, maxuint for gc threshold, effectively turning gc-by-threshold off + MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1; + MP_STATE_MEM(gc_alloc_amount) = 0; + #endif + #if MICROPY_PY_THREAD mp_thread_mutex_init(&MP_STATE_MEM(gc_mutex)); #endif @@ -294,6 +300,9 @@ STATIC void gc_sweep(void) { void gc_collect_start(void) { GC_ENTER(); MP_STATE_MEM(gc_lock_depth)++; + #if MICROPY_GC_ALLOC_THRESHOLD + MP_STATE_MEM(gc_alloc_amount) = 0; + #endif MP_STATE_MEM(gc_stack_overflow) = 0; MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack); // Trace root pointers. This relies on the root pointers being organised @@ -405,6 +414,15 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser) { size_t start_block; size_t n_free = 0; int collected = !MP_STATE_MEM(gc_auto_collect_enabled); + + #if MICROPY_GC_ALLOC_THRESHOLD + if (!collected && MP_STATE_MEM(gc_alloc_amount) >= MP_STATE_MEM(gc_alloc_threshold)) { + GC_EXIT(); + gc_collect(); + GC_ENTER(); + } + #endif + for (;;) { // look for a run of n_blocks available blocks @@ -456,6 +474,10 @@ found: void *ret_ptr = (void*)(MP_STATE_MEM(gc_pool_start) + start_block * BYTES_PER_BLOCK); DEBUG_printf("gc_alloc(%p)\n", ret_ptr); + #if MICROPY_GC_ALLOC_THRESHOLD + MP_STATE_MEM(gc_alloc_amount) += n_blocks; + #endif + GC_EXIT(); // zero out the additional bytes of the newly allocated blocks diff --git a/py/modgc.c b/py/modgc.c index d68ff7e6c0..976fb89980 100644 --- a/py/modgc.c +++ b/py/modgc.c @@ -83,6 +83,25 @@ STATIC mp_obj_t gc_mem_alloc(void) { } MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_alloc_obj, gc_mem_alloc); +#if MICROPY_GC_ALLOC_THRESHOLD +STATIC mp_obj_t gc_threshold(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + if (MP_STATE_MEM(gc_alloc_threshold) == (size_t)-1) { + return MP_OBJ_NEW_SMALL_INT(-1); + } + return mp_obj_new_int(MP_STATE_MEM(gc_alloc_threshold) * MICROPY_BYTES_PER_GC_BLOCK); + } + mp_int_t val = mp_obj_get_int(args[0]); + if (val < 0) { + MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1; + } else { + MP_STATE_MEM(gc_alloc_threshold) = val / MICROPY_BYTES_PER_GC_BLOCK; + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gc_threshold_obj, 0, 1, gc_threshold); +#endif + STATIC const mp_rom_map_elem_t mp_module_gc_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gc) }, { MP_ROM_QSTR(MP_QSTR_collect), MP_ROM_PTR(&gc_collect_obj) }, @@ -91,6 +110,9 @@ STATIC const mp_rom_map_elem_t mp_module_gc_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_isenabled), MP_ROM_PTR(&gc_isenabled_obj) }, { MP_ROM_QSTR(MP_QSTR_mem_free), MP_ROM_PTR(&gc_mem_free_obj) }, { MP_ROM_QSTR(MP_QSTR_mem_alloc), MP_ROM_PTR(&gc_mem_alloc_obj) }, + #if MICROPY_GC_ALLOC_THRESHOLD + { MP_ROM_QSTR(MP_QSTR_threshold), MP_ROM_PTR(&gc_threshold_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_gc_globals, mp_module_gc_globals_table); diff --git a/py/mpconfig.h b/py/mpconfig.h index aec5d40826..19e290c37e 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -107,6 +107,12 @@ #define MICROPY_ALLOC_GC_STACK_SIZE (64) #endif +// Support automatic GC when reaching allocation threshold, +// configurable by gc.threshold(). +#ifndef MICROPY_GC_ALLOC_THRESHOLD +#define MICROPY_GC_ALLOC_THRESHOLD (1) +#endif + // Number of bytes to allocate initially when creating new chunks to store // interned string data. Smaller numbers lead to more chunks being needed // and more wastage at the end of the chunk. Larger numbers lead to wasted diff --git a/py/mpstate.h b/py/mpstate.h index 281795773f..439ed66066 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -76,6 +76,11 @@ typedef struct _mp_state_mem_t { // you can still allocate/free memory and also explicitly call gc_collect. uint16_t gc_auto_collect_enabled; + #if MICROPY_GC_ALLOC_THRESHOLD + size_t gc_alloc_amount; + size_t gc_alloc_threshold; + #endif + size_t gc_last_free_atb_index; #if MICROPY_PY_GC_COLLECT_RETVAL