Skip to content

FreeRTOS — heap mode

Raw cross-binding benchmark results measured on STM32F746G-DISCOVERY (Cortex-M7 @ 216 MHz) running FreeRTOS in default heap-allocation mode (_create() / _destroy() API), with 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.

Methodology and reproduction steps: benchmarks overview. Same numbers under CONFIG_OVE_ZERO_HEAP=y: FreeRTOS zero-heap. 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 883 ns 1.2 µs +31.9% 1.1 µs +21.2% 1.0 µs +15.7%
time delay_1ms 984.4 µs 984.0 µs -0.0% 983.1 µs -0.1% 984.2 µs -0.0%
thread create_destroy 176.4 µs 184.2 µs +4.4% 46.6 µs -73.6% 176.3 µs -0.1%
thread yield 4.5 µs 4.3 µs -4.9% 4.4 µs -1.4% 4.5 µs +0.7%
thread get_self 2.6 µs 2.8 µs +6.7% 2.8 µs +8.0% 2.6 µs +0.2%
thread sleep_1ms 984.4 µs 984.0 µs -0.0% 983.1 µs -0.1% 984.2 µs -0.0%
thread context_switch 51.9 µs 50.7 µs -2.4% 51.7 µs -0.5%
sync mutex_memory 96 B 96 B 96 B 96 B
sync sem_memory 96 B 96 B 96 B 96 B
sync event_memory 96 B 96 B 96 B 96 B
sync condvar_memory 16 B 16 B 16 B 16 B
sync mutex_lock_unlock 8.1 µs 8.2 µs +1.6% 8.5 µs +5.7% 8.0 µs -1.1%
sync mutex_create_destroy 18.9 µs 18.3 µs -3.0% 19.2 µs +1.4% 18.9 µs -0.2%
sync mutex_contention_2t 8.2 µs 18.7 µs +128.8% 8.9 µs +8.4% 26.2 µs +220.7%
sync sem_take_give 6.5 µs 6.4 µs -1.4% 6.6 µs +1.5% 6.4 µs -1.8%
sync sem_create_destroy 15.3 µs 14.4 µs -5.7% 15.6 µs +2.3% 15.6 µs +2.0%
sync event_signal_wait 50.2 µs 49.8 µs -0.9% 49.3 µs -1.7% 50.0 µs -0.3%
sync condvar_signal_wait 34.2 µs 33.9 µs -0.8% 34.3 µs +0.3% 34.2 µs -0.0%
sync recursive_mutex_lock_unlock 10.2 µs 10.2 µs -0.0% 10.1 µs -0.6% 9.9 µs -3.1%
queue memory 128 B 128 B 128 B 128 B
queue send_receive 8.2 µs 8.2 µs +0.5% 9.3 µs +12.9% 8.5 µs +3.4%
queue create_destroy 15.0 µs 14.3 µs -4.2% 15.3 µs +2.2% 14.9 µs -0.7%
queue throughput_2t 4.0 µs 4.2 µs +4.7% 4.8 µs +20.6% 4.1 µs +0.9%
timer memory 64 B 64 B 64 B 64 B
timer create_destroy 45.6 µs 44.6 µs -2.3% 45.4 µs -0.6% 48.6 µs +6.4%
timer start_stop 67.7 µs 66.0 µs -2.5% 66.6 µs -1.6% 73.4 µs +8.5%
eventgroup memory 48 B 48 B 48 B 48 B
eventgroup set_get_bits 7.3 µs 6.6 µs -10.2% 6.9 µs -6.2% 7.1 µs -3.4%
eventgroup create_destroy 13.8 µs 13.4 µs -3.1% 14.2 µs +2.5% 14.1 µs +2.2%
workqueue memory 2520 B 2520 B 2520 B 2520 B
workqueue create_destroy 357.9 µs 374.8 µs +4.7% 97.4 µs -72.8% 357.7 µs -0.0%
workqueue submit_execute 57.7 µs 54.9 µs -4.8% 56.7 µs -1.7% 57.8 µs +0.2%
stream memory 352 B 352 B 352 B 352 B
stream send_recv_64B 20.6 µs 20.5 µs -0.6% 25.0 µs +21.3% 33.4 µs +62.1%
stream create_destroy 59.4 µs 61.7 µs +3.9% 23.3 µs -60.8% 59.3 µs -0.2%
stream throughput 31.2 µs 26.5 µs -15.1% 49.6 µs +58.9% 39.7 µs +27.1%
native_freertos native_mutex_lock_unlock 7.4 µs 7.8 µs +5.1% 8.0 µs +8.7% 7.2 µs -2.3%
native_freertos native_mutex_create_destroy 17.4 µs 16.9 µs -3.3% 16.7 µs -4.1% 17.5 µs +0.1%
native_freertos native_mutex_contention_2t 7.5 µs 7.7 µs +3.8% 8.1 µs +8.6% 7.3 µs -2.8%
native_freertos native_recursive_mutex_lock_unlock 9.3 µs 9.6 µs +4.2% 9.6 µs +3.7% 9.1 µs -1.8%
native_freertos native_sem_take_give 5.8 µs 5.6 µs -3.3% 6.3 µs +7.4% 5.8 µs -1.0%
native_freertos native_sem_create_destroy 13.2 µs 12.7 µs -3.6% 12.6 µs -4.8% 13.4 µs +1.1%
native_freertos native_condvar_signal_wait 20.3 µs 19.4 µs -4.2% 19.6 µs -3.4% 20.5 µs +1.1%
native_freertos native_event_signal_wait 20.3 µs 19.4 µs -4.0% 19.6 µs -3.2% 20.3 µs +0.4%
native_freertos native_thread_yield 4.4 µs 4.1 µs -6.6% 4.2 µs -3.9% 4.3 µs -2.6%
native_freertos native_thread_sleep_1ms 984.3 µs 983.9 µs -0.0% 983.1 µs -0.1% 984.2 µs -0.0%
native_freertos native_thread_create_destroy 166.0 µs 174.9 µs +5.4% 36.0 µs -78.3% 165.9 µs -0.1%
native_freertos native_thread_context_switch 34.9 µs 34.4 µs -1.2% 34.4 µs -1.3% 34.8 µs -0.2%
native_freertos native_queue_send_receive 7.3 µs 7.5 µs +3.1% 8.5 µs +17.1% 7.8 µs +7.8%
native_freertos native_queue_create_destroy 13.4 µs 13.0 µs -2.9% 12.8 µs -4.5% 13.5 µs +0.9%
native_freertos native_stream_send_recv_64B 19.4 µs 19.0 µs -1.6% 17.0 µs -12.0% 31.7 µs +63.8%
thread ctx_switch 51.4 µs

Cases with |Δ| > 10.0% vs C:

  • ZIG sync/mutex_contention_2t 26212 vs 8173 (+220.7%)
  • CPP sync/mutex_contention_2t 18701 vs 8173 (+128.8%)
  • RUST native_freertos/native_thread_create_destroy 35952 vs 165977 (-78.3%)
  • RUST thread/create_destroy 46573 vs 176444 (-73.6%)
  • RUST workqueue/create_destroy 97440 vs 357866 (-72.8%)
  • ZIG native_freertos/native_stream_send_recv_64B 31718 vs 19361 (+63.8%)
  • ZIG stream/send_recv_64B 33385 vs 20598 (+62.1%)
  • RUST stream/create_destroy 23305 vs 59390 (-60.8%)
  • RUST stream/throughput 49598 vs 31212 (+58.9%)
  • CPP time/time_get_us_overhead 1165 vs 883 (+31.9%)
  • ZIG stream/throughput 39666 vs 31212 (+27.1%)
  • RUST stream/send_recv_64B 24989 vs 20598 (+21.3%)
  • RUST time/time_get_us_overhead 1070 vs 883 (+21.2%)
  • RUST queue/throughput_2t 4850 vs 4021 (+20.6%)
  • RUST native_freertos/native_queue_send_receive 8512 vs 7272 (+17.1%)
  • ZIG time/time_get_us_overhead 1022 vs 883 (+15.7%)
  • CPP stream/throughput 26494 vs 31212 (-15.1%)
  • RUST queue/send_receive 9258 vs 8202 (+12.9%)
  • RUST native_freertos/native_stream_send_recv_64B 17028 vs 19361 (-12.0%)
  • CPP eventgroup/set_get_bits 6600 vs 7348 (-10.2%)

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 4488 ns 4379 ns +109 ns
Thread yield CPP ove::Thread::yield 4267 ns 4092 ns +175 ns
Thread yield RUST ove::Thread::yield 4423 ns 4209 ns +214 ns
Thread yield ZIG ove.Thread.yield 4519 ns 4266 ns +253 ns
Thread sleep 1ms C ove_thread_sleep_ms(1) 984370 ns 984340 ns +30 ns
Thread sleep 1ms CPP ove::Thread::sleep_ms(1) 984014 ns 983923 ns +91 ns
Thread sleep 1ms RUST ove::Thread::sleep_ms(1) 983131 ns 983093 ns +38 ns
Thread sleep 1ms ZIG ove.Thread.sleepMs(1) 984214 ns 984184 ns +30 ns
Thread create+destroy C ove_thread_create+destroy 176444 ns 165977 ns +10467 ns
Thread create+destroy CPP ove::Thread (ctor+dtor) 184190 ns 174932 ns +9258 ns
Thread create+destroy RUST ove::Thread::spawn+join 46573 ns 35952 ns +10621 ns
Thread create+destroy ZIG ove.Thread.spawn+join 176270 ns 165893 ns +10377 ns
Thread context_switch (2t) C ove ping-pong (2t) 51932 ns 34865 ns +17067 ns
Thread context_switch (2t) CPP ove ping-pong (2t) 50679 ns 34438 ns +16241 ns
Thread context_switch (2t) ZIG ove ping-pong (2t) 51691 ns 34806 ns +16885 ns
Mutex lock+unlock C ove_mutex_lock+unlock 8057 ns 7376 ns +681 ns
Mutex lock+unlock CPP ove::Mutex::lock+unlock 8182 ns 7750 ns +432 ns
Mutex lock+unlock RUST ove::Mutex::lock+unlock 8514 ns 8016 ns +498 ns
Mutex lock+unlock ZIG ove.Mutex.lock+unlock 7970 ns 7207 ns +763 ns
Mutex create+destroy C ove_mutex_create+destroy 18904 ns 17439 ns +1465 ns
Mutex create+destroy CPP ove::Mutex (ctor+dtor) 18345 ns 16855 ns +1490 ns
Mutex create+destroy RUST ove::Mutex (new+drop) 19176 ns 16725 ns +2451 ns
Mutex create+destroy ZIG ove.Mutex.create+destroy 18874 ns 17456 ns +1418 ns
Mutex contention (2t) C ove_mutex_lock+unlock (×2t) 8173 ns 7466 ns +707 ns
Mutex contention (2t) CPP ove::Mutex::lock+unlock (×2t) 18701 ns 7746 ns +10955 ns
Mutex contention (2t) RUST ove::Mutex::lock+unlock (×2t) 8863 ns 8108 ns +755 ns
Mutex contention (2t) ZIG ove.Mutex.lock+unlock (×2t) 26212 ns 7254 ns +18958 ns
Recursive mutex lock+unlock C ove_rmtx_lock+unlock 10200 ns 9260 ns +940 ns
Recursive mutex lock+unlock CPP ove::RMutex::lock+unlock 10196 ns 9645 ns +551 ns
Recursive mutex lock+unlock RUST ove::RMutex::lock+unlock 10136 ns 9601 ns +535 ns
Recursive mutex lock+unlock ZIG ove.RMutex.lock+unlock 9882 ns 9093 ns +789 ns
Sem take+give C ove_sem_take+give 6508 ns 5828 ns +680 ns
Sem take+give CPP ove::Sem::take+give 6418 ns 5635 ns +783 ns
Sem take+give RUST ove::Sem::take+give 6603 ns 6258 ns +345 ns
Sem take+give ZIG ove.Sem.take+give 6389 ns 5771 ns +618 ns
Sem create+destroy C ove_sem_create+destroy 15261 ns 13208 ns +2053 ns
Sem create+destroy CPP ove::Sem (ctor+dtor) 14394 ns 12734 ns +1660 ns
Sem create+destroy RUST ove::Sem (new+drop) 15618 ns 12570 ns +3048 ns
Sem create+destroy ZIG ove.Sem.create+destroy 15562 ns 13354 ns +2208 ns
Condvar signal+wait C ove_condvar_signal+wait 34217 ns 20273 ns +13944 ns
Condvar signal+wait CPP ove::Condvar::signal+wait 33947 ns 19418 ns +14529 ns
Condvar signal+wait RUST ove::Condvar::signal+wait 34304 ns 19581 ns +14723 ns
Condvar signal+wait ZIG ove.Condvar.signal+wait 34200 ns 20505 ns +13695 ns
Event signal+wait C ove_event_signal+wait 50197 ns 20252 ns +29945 ns
Event signal+wait CPP ove::Event::signal+wait 49770 ns 19437 ns +30333 ns
Event signal+wait RUST ove::Event::signal+wait 49346 ns 19612 ns +29734 ns
Event signal+wait ZIG ove.Event.signal+wait 50044 ns 20328 ns +29716 ns
Queue send+receive C ove_queue_send+receive 8202 ns 7272 ns +930 ns
Queue send+receive CPP ove::Queue::send+recv 8244 ns 7497 ns +747 ns
Queue send+receive RUST ove::Queue::send+recv 9258 ns 8512 ns +746 ns
Queue send+receive ZIG ove.Queue.send+recv 8478 ns 7837 ns +641 ns
Queue create+destroy C ove_queue_create+destroy 14961 ns 13386 ns +1575 ns
Queue create+destroy CPP ove::Queue (ctor+dtor) 14336 ns 13000 ns +1336 ns
Queue create+destroy RUST ove::Queue (new+drop) 15286 ns 12783 ns +2503 ns
Queue create+destroy ZIG ove.Queue.create+destroy 14851 ns 13511 ns +1340 ns
Stream send+recv 64B C ove_stream_send+recv 64B 20598 ns 19361 ns +1237 ns
Stream send+recv 64B CPP ove::Stream::send+recv 64B 20468 ns 19046 ns +1422 ns
Stream send+recv 64B RUST ove::Stream::send+recv 64B 24989 ns 17028 ns +7961 ns
Stream send+recv 64B ZIG ove.Stream.send+recv 64B 33385 ns 31718 ns +1667 ns