FreeRTOS — zero-heap mode
Raw cross-binding benchmark results measured on STM32F746G-DISCOVERY (Cortex-M7 @ 216 MHz) running FreeRTOS with CONFIG_OVE_ZERO_HEAP=y (_init() / _deinit() API, caller-supplied static storage, heap locked at ove_run()) and CONFIG_OVE_BENCHMARK_WORST_CASE_TIMING=y — caches and flash accelerators disabled to approximate cacheless ARM-MCU timing. See benchmarks overview for the worst-case-timing methodology.
Under zero-heap, *_create_destroy and *_memory cases are gated out — the create/destroy API isn't generated.
Methodology and reproduction steps: benchmarks overview. Heap-mode counterpart: FreeRTOS heap mode. Interpretation: heap vs zero-heap, per-binding analysis, wrapper-vs-native notes.
Generated by
scripts/bench_compare.py. Trimmed-mean (top 1% dropped) when available, else avg. Delta column is(binding − C) / C— values within ±10.0% are within typical measurement noise.
native_* rows. bench_native_freertos.c is C code calling raw FreeRTOS APIs (xSemaphoreTake, xQueueSend, xTaskCreateStatic), compiled identically into every binary; the CPP/RUST/ZIG columns for those rows are the same C code measured in three different processes.
FreeRTOS
| Suite | Case | C | CPP | Δ CPP | RUST | Δ RUST | ZIG | Δ ZIG |
|---|---|---|---|---|---|---|---|---|
| time | time_get_us_overhead | 1.1 µs | 977 ns | -8.3% | 1.2 µs | +9.8% | 1.2 µs | +9.0% |
| time | delay_1ms | 984.0 µs | 984.1 µs | +0.0% | 982.9 µs | -0.1% | 984.2 µs | +0.0% |
| thread | yield | 4.4 µs | 4.4 µs | -0.5% | 4.6 µs | +6.1% | 4.5 µs | +1.9% |
| thread | get_self | 2.5 µs | 2.6 µs | +5.3% | 2.3 µs | -8.3% | 2.7 µs | +6.5% |
| thread | sleep_1ms | 984.1 µs | 984.0 µs | -0.0% | 982.9 µs | -0.1% | 984.2 µs | +0.0% |
| thread | context_switch | 51.1 µs | 50.9 µs | -0.4% | — | — | 51.6 µs | +1.0% |
| sync | mutex_lock_unlock | 7.7 µs | 7.6 µs | -1.0% | 7.8 µs | +1.4% | 8.3 µs | +8.4% |
| sync | mutex_contention_2t | 8.8 µs | 18.3 µs | +107.0% | 15.1 µs | +71.2% | 25.6 µs | +190.6% |
| sync | sem_take_give | 6.1 µs | 6.2 µs | +1.6% | 6.2 µs | +2.7% | 6.7 µs | +9.8% |
| sync | event_signal_wait | 49.8 µs | 49.7 µs | -0.2% | 50.4 µs | +1.3% | 49.8 µs | +0.2% |
| sync | condvar_signal_wait | 33.6 µs | 33.9 µs | +1.0% | 34.1 µs | +1.6% | 34.8 µs | +3.7% |
| sync | recursive_mutex_lock_unlock | 9.4 µs | 9.5 µs | +1.6% | 9.7 µs | +3.6% | 10.2 µs | +8.2% |
| queue | send_receive | 8.6 µs | 8.8 µs | +2.4% | 9.0 µs | +5.5% | 8.3 µs | -2.5% |
| queue | throughput_2t | 4.3 µs | 4.5 µs | +5.0% | 4.8 µs | +13.6% | 4.2 µs | -2.4% |
| timer | start_stop | 71.2 µs | 71.4 µs | +0.3% | 66.3 µs | -6.9% | 67.7 µs | -4.9% |
| eventgroup | set_get_bits | 6.8 µs | 7.0 µs | +2.3% | 7.1 µs | +4.6% | 7.1 µs | +4.2% |
| workqueue | submit_execute | 56.7 µs | 56.8 µs | +0.1% | 57.7 µs | +1.7% | 56.7 µs | -0.0% |
| stream | send_recv_64B | 32.9 µs | 32.9 µs | +0.0% | 24.2 µs | -26.4% | 20.2 µs | -38.7% |
| stream | throughput | 41.9 µs | 41.3 µs | -1.4% | 49.1 µs | +17.3% | 29.6 µs | -29.2% |
| native_freertos | native_mutex_lock_unlock | 7.4 µs | 7.1 µs | -3.2% | 7.3 µs | -1.1% | 7.8 µs | +6.3% |
| native_freertos | native_mutex_contention_2t | 7.5 µs | 7.2 µs | -4.2% | 7.3 µs | -1.9% | 7.8 µs | +4.8% |
| native_freertos | native_recursive_mutex_lock_unlock | 9.1 µs | 8.8 µs | -3.4% | 9.3 µs | +2.4% | 9.6 µs | +5.2% |
| native_freertos | native_sem_take_give | 5.8 µs | 5.5 µs | -5.6% | 5.7 µs | -1.5% | 6.2 µs | +6.3% |
| native_freertos | native_condvar_signal_wait | 20.3 µs | 20.6 µs | +1.0% | 20.8 µs | +2.3% | 20.4 µs | +0.2% |
| native_freertos | native_event_signal_wait | 20.4 µs | 20.5 µs | +0.4% | 20.9 µs | +2.4% | 20.2 µs | -1.1% |
| native_freertos | native_thread_yield | 4.3 µs | 4.3 µs | -1.6% | 4.6 µs | +4.6% | 4.3 µs | -0.2% |
| native_freertos | native_thread_sleep_1ms | 984.1 µs | 984.1 µs | +0.0% | 982.9 µs | -0.1% | 984.2 µs | +0.0% |
| native_freertos | native_thread_context_switch | 34.1 µs | 34.2 µs | +0.1% | 34.2 µs | +0.1% | 35.1 µs | +2.7% |
| native_freertos | native_queue_send_receive | 7.7 µs | 7.9 µs | +1.9% | 8.2 µs | +5.8% | 7.5 µs | -2.4% |
| native_freertos | native_stream_send_recv_64B | 31.4 µs | 31.5 µs | +0.3% | 16.9 µs | -46.2% | 18.8 µs | -40.0% |
| thread | ctx_switch | — | — | — | 51.7 µs | — | — | — |
Cases with |Δ| > 10.0% vs C:
- ZIG
sync/mutex_contention_2t25625 vs 8818 (+190.6%) - CPP
sync/mutex_contention_2t18253 vs 8818 (+107.0%) - RUST
sync/mutex_contention_2t15094 vs 8818 (+71.2%) - RUST
native_freertos/native_stream_send_recv_64B16911 vs 31415 (-46.2%) - ZIG
native_freertos/native_stream_send_recv_64B18842 vs 31415 (-40.0%) - ZIG
stream/send_recv_64B20162 vs 32879 (-38.7%) - ZIG
stream/throughput29640 vs 41891 (-29.2%) - RUST
stream/send_recv_64B24196 vs 32879 (-26.4%) - RUST
stream/throughput49146 vs 41891 (+17.3%) - RUST
queue/throughput_2t4848 vs 4266 (+13.6%)
Wrapper vs native FreeRTOS API (within-run delta)
Each row pairs one binding's wrapper measurement against the raw FreeRTOS API baseline measured in the same process. See wrapper-vs-native notes for IPC caveats, lifecycle/intrinsic-cost interpretation, and notes on cross-process baseline variance.
| Operation | Binding | Wrapper called | Wrapper ns | Native ns | Δ |
|---|---|---|---|---|---|
| Thread yield | C | ove_thread_yield | 4376 ns | 4350 ns | +26 ns |
| Thread yield | CPP | ove::Thread::yield | 4352 ns | 4281 ns | +71 ns |
| Thread yield | RUST | ove::Thread::yield | 4643 ns | 4552 ns | +91 ns |
| Thread yield | ZIG | ove.Thread.yield | 4460 ns | 4341 ns | +119 ns |
| Thread sleep 1ms | C | ove_thread_sleep_ms(1) | 984061 ns | 984125 ns | -64 ns |
| Thread sleep 1ms | CPP | ove::Thread::sleep_ms(1) | 984020 ns | 984150 ns | -130 ns |
| Thread sleep 1ms | RUST | ove::Thread::sleep_ms(1) | 982937 ns | 982851 ns | +86 ns |
| Thread sleep 1ms | ZIG | ove.Thread.sleepMs(1) | 984238 ns | 984238 ns | +0 ns |
| Thread context_switch (2t) | C | ove ping-pong (2t) | 51079 ns | 34128 ns | +16951 ns |
| Thread context_switch (2t) | CPP | ove ping-pong (2t) | 50851 ns | 34168 ns | +16683 ns |
| Thread context_switch (2t) | ZIG | ove ping-pong (2t) | 51599 ns | 35065 ns | +16534 ns |
| Mutex lock+unlock | C | ove_mutex_lock+unlock | 7686 ns | 7373 ns | +313 ns |
| Mutex lock+unlock | CPP | ove::Mutex::lock+unlock | 7610 ns | 7135 ns | +475 ns |
| Mutex lock+unlock | RUST | ove::Mutex::lock+unlock | 7790 ns | 7289 ns | +501 ns |
| Mutex lock+unlock | ZIG | ove.Mutex.lock+unlock | 8333 ns | 7841 ns | +492 ns |
| Mutex contention (2t) | C | ove_mutex_lock+unlock (×2t) | 8818 ns | 7485 ns | +1333 ns |
| Mutex contention (2t) | CPP | ove::Mutex::lock+unlock (×2t) | 18253 ns | 7172 ns | +11081 ns |
| Mutex contention (2t) | RUST | ove::Mutex::lock+unlock (×2t) | 15094 ns | 7343 ns | +7751 ns |
| Mutex contention (2t) | ZIG | ove.Mutex.lock+unlock (×2t) | 25625 ns | 7845 ns | +17780 ns |
| Recursive mutex lock+unlock | C | ove_rmtx_lock+unlock | 9400 ns | 9082 ns | +318 ns |
| Recursive mutex lock+unlock | CPP | ove::RMutex::lock+unlock | 9548 ns | 8773 ns | +775 ns |
| Recursive mutex lock+unlock | RUST | ove::RMutex::lock+unlock | 9739 ns | 9302 ns | +437 ns |
| Recursive mutex lock+unlock | ZIG | ove.RMutex.lock+unlock | 10173 ns | 9553 ns | +620 ns |
| Sem take+give | C | ove_sem_take+give | 6085 ns | 5798 ns | +287 ns |
| Sem take+give | CPP | ove::Sem::take+give | 6180 ns | 5472 ns | +708 ns |
| Sem take+give | RUST | ove::Sem::take+give | 6249 ns | 5711 ns | +538 ns |
| Sem take+give | ZIG | ove.Sem.take+give | 6682 ns | 6163 ns | +519 ns |
| Condvar signal+wait | C | ove_condvar_signal+wait | 33552 ns | 20346 ns | +13206 ns |
| Condvar signal+wait | CPP | ove::Condvar::signal+wait | 33877 ns | 20558 ns | +13319 ns |
| Condvar signal+wait | RUST | ove::Condvar::signal+wait | 34074 ns | 20818 ns | +13256 ns |
| Condvar signal+wait | ZIG | ove.Condvar.signal+wait | 34806 ns | 20386 ns | +14420 ns |
| Event signal+wait | C | ove_event_signal+wait | 49767 ns | 20401 ns | +29366 ns |
| Event signal+wait | CPP | ove::Event::signal+wait | 49677 ns | 20476 ns | +29201 ns |
| Event signal+wait | RUST | ove::Event::signal+wait | 50438 ns | 20882 ns | +29556 ns |
| Event signal+wait | ZIG | ove.Event.signal+wait | 49842 ns | 20178 ns | +29664 ns |
| Queue send+receive | C | ove_queue_send+receive | 8558 ns | 7725 ns | +833 ns |
| Queue send+receive | CPP | ove::Queue::send+recv | 8766 ns | 7874 ns | +892 ns |
| Queue send+receive | RUST | ove::Queue::send+recv | 9032 ns | 8172 ns | +860 ns |
| Queue send+receive | ZIG | ove.Queue.send+recv | 8345 ns | 7538 ns | +807 ns |
| Stream send+recv 64B | C | ove_stream_send+recv 64B | 32879 ns | 31415 ns | +1464 ns |
| Stream send+recv 64B | CPP | ove::Stream::send+recv 64B | 32879 ns | 31497 ns | +1382 ns |
| Stream send+recv 64B | RUST | ove::Stream::send+recv 64B | 24196 ns | 16911 ns | +7285 ns |
| Stream send+recv 64B | ZIG | ove.Stream.send+recv 64B | 20162 ns | 18842 ns | +1320 ns |