Context
Several recent example PRs (#390, #391) use @micropython.native on functions to get faster execution. The decorator compiles a function to native ARM code instead of MicroPython bytecode, which can yield 2-10x speedups on CPU-bound loops. However, it is not always beneficial and has constraints that should be documented.
Guidelines to adopt
Where @micropython.native is useful
Example files with pixel-level rendering loops, where the CPU is the bottleneck:
draw_character() (tamagotchi) — 4 nested loops, pixel by pixel
fill_circle() (spirit_level) — double loop with x²+y² test
draw_maze() (maze_game) — double loop over the grid
- Any tight numerical loop that does not call I/O
Where @micropython.native is NOT useful
- Driver code (
device.py) — methods are dominated by I2C/SPI bus time (100-400 kHz). Compiling _read_reg() to native code saves microseconds on a millisecond-scale operation. The added complexity (larger binary, reduced debuggability) is not worth it.
- Functions with few operations — e.g.
compute_display() in radar_screen.py does 3 comparisons; native compilation gains nothing measurable.
- Functions using unsupported features — generators, closures,
with statements, and some exception patterns are not supported by the native emitter and will cause a compile-time error.
Constraints
- Only works on ARM targets (not a problem for STeaMi, always STM32)
- Native code uses more RAM per function than bytecode
- Errors in native functions produce less helpful tracebacks
- Not portable to CPython (requires a
try: import micropython guard or per-file ignore in tests/stubs)
Proposed policy
- Drivers: do not use
@micropython.native. Keep driver code portable and debuggable.
- Examples: accept on a case-by-case basis for rendering hot-paths where the speedup is measurable and the function is compatible with native compilation.
- Do not enforce via linting — this is a judgment call, not a mechanical rule.
- Document this policy in CONTRIBUTING.md under "Coding conventions".
Tasks
Context
Several recent example PRs (#390, #391) use
@micropython.nativeon functions to get faster execution. The decorator compiles a function to native ARM code instead of MicroPython bytecode, which can yield 2-10x speedups on CPU-bound loops. However, it is not always beneficial and has constraints that should be documented.Guidelines to adopt
Where
@micropython.nativeis usefulExample files with pixel-level rendering loops, where the CPU is the bottleneck:
draw_character()(tamagotchi) — 4 nested loops, pixel by pixelfill_circle()(spirit_level) — double loop with x²+y² testdraw_maze()(maze_game) — double loop over the gridWhere
@micropython.nativeis NOT usefuldevice.py) — methods are dominated by I2C/SPI bus time (100-400 kHz). Compiling_read_reg()to native code saves microseconds on a millisecond-scale operation. The added complexity (larger binary, reduced debuggability) is not worth it.compute_display()inradar_screen.pydoes 3 comparisons; native compilation gains nothing measurable.withstatements, and some exception patterns are not supported by the native emitter and will cause a compile-time error.Constraints
try: import micropythonguard or per-file ignore in tests/stubs)Proposed policy
@micropython.native. Keep driver code portable and debuggable.Tasks
@micropython.nativefromcompute_display()inradar_screen.py(no measurable benefit on 3 comparisons)