Slide 1

Slide 1 text

Python on bare metal fast & efficient code with MicroPython Jessica Greene @sleepypioneer

Slide 2

Slide 2 text

MicroPython  A complete reimplementation of Python  Designed to be efficient with resources  Designed to run bare metal Includes:  A compiler, runtime & familiar REPL  Support for basic libraries  Extra modules to control hardware

Slide 3

Slide 3 text

PyBoard, ESP32, ESP8266 & more...

Slide 4

Slide 4 text

Challenges of working on microcontrollers BATTERY POWERED VERY LITTLE MEMORY (RAM, ROM) NORMALLY REQUIRE LOW LEVEL LANGUAGE

Slide 5

Slide 5 text

How does MicroPython solve the issue of having a low amount of RAM? Interned strings, most already in ROM Small integers stuffed in a pointer to avoid using heap (trick from lisp) Optimised method calls to not use heap Garbage collection

Slide 6

Slide 6 text

Normal python uses megabytes RAM if not 100s MicroPython can run with only 8 kilobytes & it runs without an OS, so on bare metal Python Vs MicroPython

Slide 7

Slide 7 text

How you can impact this with your code?

Slide 8

Slide 8 text

Tips: Memory allocation Doesn’t use heap Uses Heap Expressions Importing If, while, for and try statements Defining functions & classes Local variables Assigning global variables for the first time Small integer arithmetic Creating data structures Inplace operations on existing data structures Calling functions/ methods with positional or keyword args

Slide 9

Slide 9 text

Tips: CPU time USE FUNCTIONS TO AVOID GLOBAL SCOPES USE LOCAL VARIABLES CACHE MODULE FUNCTIONS & OBJECT METHODS AS LOCALS PREFER LONGER EXPRESSIONS NOT SPLIT UP ONES RUNTIME IS FAST! E.G. STR.STARTSWITH

Slide 10

Slide 10 text

Tips: RAM usage DON'T USE HEAP WHEN POSSIBLE SHORTER VARIABLE NAMES, REUSE THEM; EG X,Y,I, LEN, VAR DON'T USE * OR ** ARGS SCRIPT MINIFICATION ULTIMATE SOLUTION: FREEZE SCRIPTS INTO THE FIRMWARE (AT COMPILE TIME)

Slide 11

Slide 11 text

Code example

Slide 12

Slide 12 text

LED Blink script import time, machine led = machine.Pin('LED_BLUE') for i in range(N): led.on() led.off() 50kHz @dpgeorge

Slide 13

Slide 13 text

Inside a function - removes global variables 60kHz import time, machine led = machine.Pin('LED_BLUE') def blink_simple(n): for i in range(n): led.on() led.off() @dpgeorge

Slide 14

Slide 14 text

Preload methods & range() optimisation def blink_preload(n): on = led.on # returns a bound method off = led.off r = range(n) # optimise the range for i in r: on() off() 180kHz @dpgeorge

Slide 15

Slide 15 text

Loop unrolling def blink_preload_unrolled8(n): n //= 8 on = led.on off = led.off r = range(n) for i in r: on() off() on() off() .... 220kHz @dpgeorge

Slide 16

Slide 16 text

Native mode @micropython.native def blink_preload_unrolled8_native(n:int): n //= 4 on = led.on off = led.off r = range(n) for i in r: on() off() on() off() .... 290kHz @dpgeorge

Slide 17

Slide 17 text

Viper mode @micropython.viper def blink_unrolled8_viper(n:int): n //= 8 p = ptr16(stm.GPIOB + stm.GPIO_BSRR) for i in range(n): p[0] = 1 << 4 # high p[1] = 1 << 4 # low p[0] = 1 << 4 # high p[1] = 1 << 4 # low .... 12890kHz @dpgeorge

Slide 18

Slide 18 text

In assembler @micropython.asm_thumb def blink_asm(r0): lsr(r0, r0, 3) movwt(r1, stm.GPIOB + stm.GPIO_BSRR) mov(r2, 1 << 4) label(loop) strh(r2, [r1, 0]) # high strh(r2, [r1, 2]) # low strh(r2, [r1, 0]) # high strh(r2, [r1, 2]) # low .... 27359.25kHz @dpgeorge

Slide 19

Slide 19 text

Takeaways ○ If your code runs faster it can sleep for longer and that saves battery! ○ ROM everything that you can ○ Use runtime methods! ○ Local not global variables!

Slide 20

Slide 20 text

THANK YOU! www.github.com/sleepypioneer/fast_and_efficient_micropython www.micropython.org www.github.com/micropython