From 3e8aed9fcce9068f40780b23148feaa1e041a18a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 5 Sep 2023 10:58:19 +1000 Subject: [PATCH 1/3] py/gc: Add "max new split" value in result of gc.mem_free(). Follow-up to 519c24dd487 when MICROPY_GC_SPLIT_HEAP_AUTO is enabled, based on discussion at https://github.com/orgs/micropython/discussions/12316#discussioncomment-6858007 gc.mem_free() is always a heuristic, but this makes it a more useful heuristic for common use cases. Signed-off-by: Angus Gratton --- py/gc.c | 7 ++++++- py/gc.h | 3 +++ py/modgc.c | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/py/gc.c b/py/gc.c index b2e4aa4aae8dd..80e5f80360685 100644 --- a/py/gc.c +++ b/py/gc.c @@ -701,6 +701,11 @@ void gc_info(gc_info_t *info) { info->used *= BYTES_PER_BLOCK; info->free *= BYTES_PER_BLOCK; + + #if MICROPY_GC_SPLIT_HEAP_AUTO + info->max_new_split = gc_get_max_new_split(); + #endif + GC_EXIT(); } @@ -1159,7 +1164,7 @@ void gc_dump_info(const mp_print_t *print) { mp_printf(print, "GC: total: %u, used: %u, free: %u", (uint)info.total, (uint)info.used, (uint)info.free); #if MICROPY_GC_SPLIT_HEAP_AUTO - mp_printf(print, ", max new split: %u", (uint)gc_get_max_new_split()); + mp_printf(print, ", max new split: %u", (uint)info.max_new_split); #endif mp_printf(print, "\n No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n", (uint)info.num_1block, (uint)info.num_2block, (uint)info.max_block, (uint)info.max_free); diff --git a/py/gc.h b/py/gc.h index 7eec6265c87d5..36177633062b2 100644 --- a/py/gc.h +++ b/py/gc.h @@ -75,6 +75,9 @@ typedef struct _gc_info_t { size_t num_1block; size_t num_2block; size_t max_block; + #if MICROPY_GC_SPLIT_HEAP_AUTO + size_t max_new_split; + #endif } gc_info_t; void gc_info(gc_info_t *info); diff --git a/py/modgc.c b/py/modgc.c index c11bcaecd7ddf..7b18045b083a3 100644 --- a/py/modgc.c +++ b/py/modgc.c @@ -64,7 +64,12 @@ MP_DEFINE_CONST_FUN_OBJ_0(gc_isenabled_obj, gc_isenabled); STATIC mp_obj_t gc_mem_free(void) { gc_info_t info; gc_info(&info); + #if MICROPY_GC_SPLIT_HEAP_AUTO + // Include max_new_split value here as a more useful heuristic + return MP_OBJ_NEW_SMALL_INT(info.free + info.max_new_split); + #else return MP_OBJ_NEW_SMALL_INT(info.free); + #endif } MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_free_obj, gc_mem_free); From 26160e8ed54410e3d630c03e1c46eb59e02c2594 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 5 Sep 2023 11:06:23 +1000 Subject: [PATCH 2/3] docs/library/esp32: Update ESP32 idf_heap_info docs to match behaviour. Signed-off-by: Angus Gratton --- docs/library/esp32.rst | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index d6b4051a2fa2e..7701f43539819 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -56,15 +56,6 @@ Functions two most useful ones are predefined as `esp32.HEAP_DATA` for data heap regions and `esp32.HEAP_EXEC` for executable regions as used by the native code emitter. - Free IDF heap memory in the `esp32.HEAP_DATA` region is available to be - automatically added to the MicroPython heap to prevent a MicroPython - allocation from failing. However, the information returned here is otherwise - *not* useful to troubleshoot Python allocation failures, use - `micropython.mem_info()` instead. The "max new split" value in - `micropython.mem_info()` output corresponds to the largest free block of - ESP-IDF heap that could be automatically added on demand to the MicroPython - heap. - The return value is a list of 4-tuples, where each 4-tuple corresponds to one heap and contains: the total bytes, the free bytes, the largest free block, and the minimum free seen over time. @@ -75,6 +66,21 @@ Functions [(240, 0, 0, 0), (7288, 0, 0, 0), (16648, 4, 4, 4), (79912, 35712, 35512, 35108), (15072, 15036, 15036, 15036), (113840, 0, 0, 0)] + .. note:: Free IDF heap memory in the `esp32.HEAP_DATA` region is available + to be automatically added to the MicroPython heap to prevent a + MicroPython allocation from failing. However, the information returned + here is otherwise *not* useful to troubleshoot Python allocation + failures. :func:`micropython.mem_info()` and :func:`gc.mem_free()` should + be used instead: + + The "max new split" value in :func:`micropython.mem_info()` output + corresponds to the largest free block of ESP-IDF heap that could be + automatically added on demand to the MicroPython heap. + + The result of :func:`gc.mem_free()` is the total of the current "free" + and "max new split" values printed by :func:`micropython.mem_info()`. + + Flash partitions ---------------- From 92f379cce4cd5bc9d07f1ad76cd9674ca02f54e7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 5 Sep 2023 11:05:47 +1000 Subject: [PATCH 3/3] docs/library/gc: Clarify mem_alloc and mem_free only for Python heap. As raised in discussions of the ESP32 memory management changes. Signed-off-by: Angus Gratton --- docs/library/gc.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/library/gc.rst b/docs/library/gc.rst index c823aed3e688b..2ba204e2293c0 100644 --- a/docs/library/gc.rst +++ b/docs/library/gc.rst @@ -24,7 +24,7 @@ Functions .. function:: mem_alloc() - Return the number of bytes of heap RAM that are allocated. + Return the number of bytes of heap RAM that are allocated by Python code. .. admonition:: Difference to CPython :class: attention @@ -33,8 +33,8 @@ Functions .. function:: mem_free() - Return the number of bytes of available heap RAM, or -1 if this amount - is not known. + Return the number of bytes of heap RAM that is available for Python + code to allocate, or -1 if this amount is not known. .. admonition:: Difference to CPython :class: attention