Design & impl notes
Unstructured, braindump-ish notes about the design and implementation of
WARNING the notes here may not accurately reflect the current implementation. This document is synchronized with the implementation at a best effort basis.
defmt optimizes for data throughput first and then for runtime cost.
No double compilation
Say you want print logs from target/device app that uses crate
foo uses the
Format trait on some of its data structures.
In this scenario we want to avoid having to compile
foo for the host.
In other words,
foo should only be (cross) compiled for the target device.
This is the biggest difference between
defmt and some
serde library that does binary serialization.
serde library requires a
Deserialize step that requires compiling
foo for the host (see
defmt avoids this by placing all the required information for formatting in a "side table" (see the interning section).
This comes with the downside that the host can only perform limited actions on the data it receives from the device: namely formatting.
The host cannot invoke
foo::Struct.method() for example but that may not even be a sensible operation on the host anyways, e.g.
foo::USB::RegisterValue.store_volatile() would make the host crash.
We want to avoid this "double" compilation (cross compile for the target and compile for the host) because:
- it doubles compilation time (wait time)
- compiling device-specific code for the host can become a headache quickly: see inline/external assembly, build scripts, etc.