Skip to content

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_2t 25625 vs 8818 (+190.6%)
  • CPP sync/mutex_contention_2t 18253 vs 8818 (+107.0%)
  • RUST sync/mutex_contention_2t 15094 vs 8818 (+71.2%)
  • RUST native_freertos/native_stream_send_recv_64B 16911 vs 31415 (-46.2%)
  • ZIG native_freertos/native_stream_send_recv_64B 18842 vs 31415 (-40.0%)
  • ZIG stream/send_recv_64B 20162 vs 32879 (-38.7%)
  • ZIG stream/throughput 29640 vs 41891 (-29.2%)
  • RUST stream/send_recv_64B 24196 vs 32879 (-26.4%)
  • RUST stream/throughput 49146 vs 41891 (+17.3%)
  • RUST queue/throughput_2t 4848 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