Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork8.5k
py/emitglue: Reduce RAM footprint of frozen code and SYS_SETTRACE feature#18346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:master
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Conversation
When a port defines MICROPY_PY_SYS_SETTRACE, there is currently alarge overhead in memory usage due to the extra pointers requiredto access code information for tracing. This overhead exists evenwhen tracing is not active. It impacts both RAM and frozen codein flash memory.This commit reduces this overhead by:- removing unused fields in the profiling data structure- optimizing the size of mp_raw_code_t, which is a temporary structure for normal use but is persistent when SYS_SETTRACE is enabled (and also impacts frozen code size).As a side effect, this commit also reduce the code footprint offrozen functions when MICROPY_PERSISTENT_CODE_SAVE is enabled,even for builds without SYS_SETTRACE enabled.Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
codecovbot commentedOct 30, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Codecov Report❌ Patch coverage is
Additional details and impacted files☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
github-actionsbot commentedOct 30, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Code size report: |
When a port defines MICROPY_PY_SYS_SETTRACE, there is currently alarge overhead in memory usage due to the mp_obj_fun_bc_t objecttaking 2 GC units per function instead of 1. This can be avoidedby removing redundant information from mp_obj_fun_bc_t when thefunction kind is BYTECODE and SYS_SETTRACE is enabled.This requires however to make the storage underlying mp_obj_fun_*objects different for bytecode and native/viper function objects.There is currently a grey zone with regard to the exact underlyingdata structure for function objects: inline_asm functions havetheir own mp_obj_fun_asm_t structure, but native and viper functionscurrently use the mp_obj_fun_bc_t structure, although they arenot really bytecode. This commit make them explicitly differentto increase the code readability, and clarify assumptions in code.Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
Currently, loading a frozen module with many functions andmethods results in substantial RAM usage due to the creationfunction objects (one GC block is created for each functionor method defined in the module).This commit provides a way to freeze the bytecode functionobjects in ROM, along with the raw_code objects. In order toachieve this, the module context structure must also be frozen,to allow frozen bytecode functions to reference it. Forfrozen module contexts, the module-specific globals dict istherefore referenced using an indirect pointer in RAM.Signed-off-by: Yoctopuce dev <dev@yoctopuce.com>
de19fdb to6849d21Compare
Uh oh!
There was an error while loading.Please reload this page.
Summary
My MicroPython port has relatively large flash storage, but not much RAM (only 100 KB is available for MicroPython heap). Therefore I rely on frozen modules to save RAM. My port includes SYS_SETTRACE to allow the end user to debug their own code using Visual Studio Code.
Examining my runtime heap map in detail, I realised that frozen functions use a significant amount of RAM: 32 bytes per function or method. This equated to 20 KB of RAM when importing my main library.
Further investigation revealed a significant waste of ROM and RAM when enabling SYS_SETTRACE. This pull request attempts to address this issue:
mp_raw_code_tstructure, which uses unnecessary ROM space in frozen code, but also uses RAM for dynamically loaded functions. Depending on the port options, this may also reduce the code footprint for builds not using SYS_SETTRACE.mp_obj_fun_bc_tobjects using two GC units instead of one when SYS_SETTRACE is enabled. This requires the use of a different structure for each kind of function object, instead of just two types as it is currently the case, so some assumptions about the usage of function objects had to be made more explicit.mp_obj_fun_bc_tobjects, not using any RAM. This improvement may benefit to any port with frozen modules, even when SYS_SETTRACE is not enabled. The price to pay for this is of course a bit of ROM, and the use of an indirect pointer to load the module globals dictionary.edit on 2025-10-31: this third commit has been updated based on the alternative implementation that I had considered in my first submit, i.e. frozing the
mp_module_context_tobject as well, with just the pointer to the module-specific globals dictionary in RAM. This results in simpler code so I think this is preferable. The first version is still available on my repository for reference if needed.Testing
The code has been extensively tested on my port, executing both frozen and runtime code.
I started from a build with 185 KB of rodata for frozen modules. Loading my main frozen library used 36KB RAM (for defining classes involving about 600 methods). After the optimizations, I ended up with 162 KB of rodata for frozen modules, and only 17KB RAM used after loading my main library.
So the total savings in my case are 20 KB flash, 20 KB RAM.
I have enabled the new features for some configurations of CI tests to be sure they are covered.
Trade-offs and Alternatives
There are other ways to somehow reduce the RAM footprint of frozen modules, such as lazy loading (as done in asyncio for instance). I have gone that way first, but ultimately:
So ultimately, freezing
mp_obj_fun_bc_tobjects was a much cleaner solution.