The reader will build the following competences in this tutorial:
To assess Flash and static RAM usage at compile time, by analysing memory map files.
To measure Heap and Stack usage at runtime using the technique of memory painting.
To measure execution time using internal and external tools (hardware timers and logic analysers).
To measure energy consumption using osciloscopes and power profilers.
To correlate execution time and energy consumption data in a granular and step-by-step way, allowing the identification of performance bottlenecks.
To measure message sizes of IoT protocols deployed in embedded systems, using techniques such as logging and packet sniffing.
Measuring Memory Usage
Flash and RAM (Compile Time)
compiled binary, GNU size, nm, analysing linker map files
After the successful compilation of a program, it’s possible to analyse the full Flash usage and partial RAM usage. Different techniques allow measuring usage with more or less granularity, in terms of which modules use more or less memory.
For global granularity, use the GNU size
command on your binary. The text and data sections represent memory used for code and initialized variables, respectively, and they will use space in the Flash. So in the example below, Flash usage is 15840 + 56 = 15896 bytes. The bss section (which for historical reasons stands for Block Started by Symbol) stores uninitialized variables, and therefore it does not occupy space in the Flash. Since the variables in bss and data will need to be manipulate during runtime, these occupy space in RAM, thus in this example the static RAM usage amounts to 1032 + 56 = 1088 bytes.
lakers-FORK $ size target/thumbv7em-none-eabihf/debug/lakers-no_std text data bss dec hex filename 15840 56 1032 16928 4220 target/thumbv7em-none-eabihf/debug/lakers-no_std
Sometimes, we want to measure only the sizes for certain parts of our code. For example, in lakers
, we normally want to measure how much memory is needed by the library itself, but want to discard things like the cryptographic backend, since it changes across platforms.
One way to do that is by analysing the memory map file generated by the compiler (one might need to enable that by passing a flag such as -Clink-args=-Map=/tmp/lakers.map
to the linker). Different linkers generate slightly different memory map files, but in general they present the address, size, and location of every symbol in your program. For example, …
Another way is using the nm
utility.
Stack and heap (RAM at Runtime)
Stack and heap: memory painting, gdb, jlink
Measuring Execution Time
timers, gpio's connected to logic analysers
Measuring Energy Consumption
multimeters, oscilloscopes, power profilers
Correlating Execution Time and Energy Consumption
Syncing execution time and energy consumption: power profiler with gpio
Step-by-step Granular Time and Energy Consumption
Obtaining step-by-step time and energy consumption: merging results from power profiler and logic analyser
Measuring Message Sizes
logging, hardware packet sniffing, packet analysis tools (wireshark)
Logging: can be performed on the mote or on the gateway, easier on the latter.
Packet sniffing: there are two main situations.
If the constrained devices talks to a computer or gateway, just run Wireshark on the computer.
If two devices talk between each other, you need a third device that understand the protocol to sniff the conversation. Some IoT platforms offer facilities to save the conversation as a .cap file, which can be later analyzed on Wireshark.