1) 要約(3行)
同一ボード・同一ツールチェーン・同一設定で FreeRTOS / Zephyr / ThreadX を実機ベンチ。
評価軸は割り込みレイテンシ、コンテキストスイッチ、タイマジッタ、メッセージ往復、RAM/Flash、ブート時間、任意でネットワーク。
本稿は測定手順と再現性を重視。未取得値は TBD/不明 と明示し、脚注に注意点を記す。[^tbd]
想定読者/結論の先出し
- 実務開発者:再現手順と最小スニペットがすぐ使える
- マネージャ:1画面比較表と選定フローチャートで短時間判断
- 学生/初学者:計測方法の擬似コードでベンチ設計の勘所を学べる
- 結論(暫定):用途適合で選ぶべき。数値は環境依存が大きく、統一手順での社内再測定を推奨。[^env]
2) テスト環境
テスト環境表
項目 | 値 |
---|---|
計測日 | {{MEASURED_DATE}} |
ボード/MCU | {{BOARD_MODEL}} / {{MCU_PART}} |
クロック | {{CPU_CLOCK_MHZ}} MHz |
周辺 | {{PERIPHERALS}} |
コンパイラ/最適化 | {{COMPILER}} / {{OPT_FLAGS}} |
共通条件 | {{BUILD_CONDS}} |
ティック | {{TICK_HZ}} Hz |
スケジューラ | {{SCHEDULER_MODE}} |
メモリ割当 | {{MEM_ALLOC}} |
RTOSバージョン表
RTOS | バージョン/タグ |
---|---|
FreeRTOS | {{FREERTOS_VER}} |
Zephyr | {{ZEPHYR_VER}} |
ThreadX | {{THREADX_VER}} |
表示ルール:未入力の値は後述の表では <span style="color:gray">TBD/不明</span> としてグレー表示する。[^tbd]
公平性のための統一ルール
- 同一ハード・同一クロック・同一電源・同一温度帯(室温/不明は明記)。
- 同一コンパイラ/同一最適化フラグ、リンク時の GC 等も一致。
- カーネルティック {{TICK_HZ}} Hz を共通。ティックレスは無効(比較容易化)。[^tickless]
- 割り込み源・優先度・測定回数・外れ値処理を共通化。
- タイムベースは DWT(Data Watchpoint and Trace)カウンタまたはロジックアナライザで同一基準。
- RAM/Flash はリンカマップから最小構成を抽出(アプリは最小)。
3) 評価軸と計測方法(図解/疑似コード)
割り込みレイテンシ(IRQ→最初のタスク実行)
- 狙い:外部 IRQ 立上りから ISR 経由で最初に実行されるタスク開始までの時間。
- 手段:
- DWT_CYCCNT を有効化。IRQエッジで GPIO をトグル→ISR でタイムスタンプ→タスク起動箇所でもう一度トグル。
- 代替:ロジアナで IRQ 入力線とプローブ GPIO を測る。
- 回数/外れ値:N=10,000 推奨。p95/p99 と箱ひげ図で外れ値可視化。外れ値は除外せず分布として報告。
// 擬似コード(C)
volatile uint32_t t_isr, t_task;
void IRQ_Handler(void){
GPIO_SET(PROBE1);
t_isr = DWT_CYCCNT;
xTaskNotifyFromISR(taskH, 1, eSetBits, &hpw);
portYIELD_FROM_ISR(hpw);
}
void task(void*){
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
t_task = DWT_CYCCNT;
GPIO_SET(PROBE2);
}
コンテキストスイッチ(同優先度2タスクのピンポン)
- 狙い:A→B→A の往復に要する時間。
- 手段:同優先度・同コアでタスク通知/セマフォ ping-pong。タイムスタンプは DWT または GPIO。
// 擬似コード(C)
void taskA(void*){
for(;;){
uint32_t t0 = DWT_CYCCNT;
xTaskNotifyGive(taskB);
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
uint32_t dt = DWT_CYCCNT - t0; log(dt);
}
}
タイマジッタ(1kHz 周期タスク)
- 狙い:周期 1ms タスクの到着時刻ばらつき(p-p)。
- 手段:ソフトタイマ/カーネルディレイ禁止。ハードタイマ→タスク起床で timestamp 収集。
メッセージ往復(64B)
- 狙い:キュー/メイルボックスで 64B を A→B→A 往復した 1 回あたりの遅延。
- 手段:RTOS 標準 API。割り込み禁止区間の影響に注意。
メモリ/フラッシュ
- 狙い:最小構成の静的 RAM / Flash フットプリント。
- 手段:リンカマップから .bss/.data/.rodata/.text を抽出し合算。ブートローダは除外を明記。
ブート時間
- 狙い:リセットベクタ→最初のユーザタスク開始まで。
- 手段:Reset_Handler 先頭で GPIO Low、タスク開始直前で High。ロジアナで測定。
注意:TBD の項目はグレー表示し、脚注で追試予定と明記する。[^tbd]
4) 比較サマリ表(1画面で俯瞰)
指標 | FreeRTOS | Zephyr | ThreadX | 備考 |
---|---|---|---|---|
IRQレイテンシ (μs) | {{IRQLAT_FR}} | {{IRQLAT_ZEP}} | {{IRQLAT_TX}} | 低いほど良い |
CtxSwitch往復 (μs) | {{CS_FR}} | {{CS_ZEP}} | {{CS_TX}} | 低いほど良い |
タイマジッタ p-p (μs) | {{JITTER_FR}} | {{JITTER_ZEP}} | {{JITTER_TX}} | 低いほど良い |
キュー往復 64B (μs) | {{MQ_FR}} | {{MQ_ZEP}} | {{MQ_TX}} | 低いほど良い |
RAM最小 (KiB) | {{RAM_FR}} | {{RAM_ZEP}} | {{RAM_TX}} | リンカマップから算出 |
Flash最小 (KiB) | {{FLASH_FR}} | {{FLASH_ZEP}} | {{FLASH_TX}} | |
ブート時間 (ms) | {{BOOT_FR}} | {{BOOT_ZEP}} | {{BOOT_TX}} | 低いほど良い |
ネットワーク (kpps) | {{NET_FR}} | {{NET_ZEP}} | {{NET_TX}} | 任意テスト |
ライセンス | <span style="color:gray">不明</span> | <span style="color:gray">不明</span> | <span style="color:gray">不明</span> | 本稿では未確認[^license] |
設定難易度 | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | 主観を避け定量化予定 |
エコシステム | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | ボード/ドライバ数等で後日評価 |
ドライバ/Devicetree有無 | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | 本ベンチの範囲外 |
セキュリティ(TrustZone/MPU) | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | 将来追補 |
Long-termサポート | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | ベンダ/コミュニティ情報要確認 |
注:表中の
{{...}}
は測定で置換される値。未取得は <span style="color:gray">TBD</span> に差し替える。[^tbd]
5) 結果(チャート+表)
レンダリングについて:本稿は Markdown ベース。
{{...}}
が実数に置換されていない場合、以下の Mermaid グラフは描画されない。未取得値はグラフから除外し、表では <span style="color:gray">TBD</span> とする。単位を凡例に明記。
IRQレイテンシ(μs, 低いほど良い)
bar
title IRQ Latency (μs) - lower is better
x-axis [FreeRTOS, Zephyr, ThreadX]
y-axis "μs"
series "Latency" [{{IRQLAT_FR}}, {{IRQLAT_ZEP}}, {{IRQLAT_TX}}]
コンテキストスイッチ往復(μs, 低いほど良い)
bar
title Context Switch Round-trip (μs) - lower is better
x-axis [FreeRTOS, Zephyr, ThreadX]
y-axis "μs"
series "CtxSwitch" [{{CS_FR}}, {{CS_ZEP}}, {{CS_TX}}]
タイマジッタ p-p(μs, 低いほど良い)
bar
title Timer Jitter p-p (μs) - lower is better
x-axis [FreeRTOS, Zephyr, ThreadX]
y-axis "μs"
series "Jitter" [{{JITTER_FR}}, {{JITTER_ZEP}}, {{JITTER_TX}}]
メッセージ往復 64B(μs/回, 低いほど良い)
bar
title Message Queue RTT 64B (μs) - lower is better
x-axis [FreeRTOS, Zephyr, ThreadX]
y-axis "μs/operation"
series "MQ RTT" [{{MQ_FR}}, {{MQ_ZEP}}, {{MQ_TX}}]
フットプリント(KiB, 低いほど良い)
bar
title Static RAM (KiB) - lower is better
x-axis [FreeRTOS, Zephyr, ThreadX]
y-axis "KiB"
series "RAM" [{{RAM_FR}}, {{RAM_ZEP}}, {{RAM_TX}}]
bar
title Flash (KiB) - lower is better
x-axis [FreeRTOS, Zephyr, ThreadX]
y-axis "KiB"
series "Flash" [{{FLASH_FR}}, {{FLASH_ZEP}}, {{FLASH_TX}}]
ブート時間(ms, 低いほど良い)
bar
title Boot Time (ms) - lower is better
x-axis [FreeRTOS, Zephyr, ThreadX]
y-axis "ms"
series "Boot" [{{BOOT_FR}}, {{BOOT_ZEP}}, {{BOOT_TX}}]
ネットワーク(UDP 1KB, kpps, 高いほど良い)
bar
title UDP 1KB throughput (kpps) - higher is better
x-axis [FreeRTOS, Zephyr, ThreadX]
y-axis "kpps"
series "UDP" [{{NET_FR}}, {{NET_ZEP}}, {{NET_TX}}]
指標別の表(平均/中央値/標準偏差を併記)
本稿では単一条件の代表値のみが与えられているため、統計量は <span style="color:gray">TBD</span>。複数ロット/温度で再測定予定。[^stats]
指標 | 単位 | FreeRTOS | Zephyr | ThreadX | 平均 | 中央値 | 標準偏差 |
---|---|---|---|---|---|---|---|
IRQレイテンシ | μs | {{IRQLAT_FR}} | {{IRQLAT_ZEP}} | {{IRQLAT_TX}} | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> |
CtxSwitch往復 | μs | {{CS_FR}} | {{CS_ZEP}} | {{CS_TX}} | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> |
タイマジッタ p-p | μs | {{JITTER_FR}} | {{JITTER_ZEP}} | {{JITTER_TX}} | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> |
MQ 往復 64B | μs/回 | {{MQ_FR}} | {{MQ_ZEP}} | {{MQ_TX}} | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> |
RAM最小 | KiB | {{RAM_FR}} | {{RAM_ZEP}} | {{RAM_TX}} | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> |
Flash最小 | KiB | {{FLASH_FR}} | {{FLASH_ZEP}} | {{FLASH_TX}} | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> |
ブート時間 | ms | {{BOOT_FR}} | {{BOOT_ZEP}} | {{BOOT_TX}} | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> |
ネットワーク | kpps | {{NET_FR}} | {{NET_ZEP}} | {{NET_TX}} | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> | <span style="color:gray">TBD</span> |
優劣の要因仮説(短評)
- IRQレイテンシ:**ISR出口処理(コンテキスト切替/クリティカル区間)**の実装差が影響しやすい。[^hyp]
- CtxSwitch:レディーリスト実装(ビットマップ/リスト)や割り込みマスク範囲の差。
- ジッタ:ティック割り込みの位相関係と優先度の逆転回避が効く。
- MQ 往復:コピー回数と**通知原語(セマフォ/キュー/イベント)**の違い。
- フットプリント:リンク時最適化/スタブ化の度合いで大きく変動。
6) 実装スニペット(最小タスク起動)
すべて概略であり、プロジェクトのスタートポイントとして自己記述化した最小例。無断コピペではない。
FreeRTOS(最小)
// main.c
#include "FreeRTOS.h"
#include "task.h"
static void AppTask(void *arg){
(void)arg;
// ここでGPIO点灯やUART出力など
for(;;){ vTaskDelay(pdMS_TO_TICKS(1000)); }
}
int main(void){
// hw_init(); // クロック/ピン/コンソール初期化
xTaskCreate(AppTask, "app", 512, NULL, tskIDLE_PRIORITY+1, NULL);
vTaskStartScheduler();
for(;;); // ここには戻らない
}
// FreeRTOSConfig.h(抜粋)
#define configUSE_PREEMPTION 1
#define configTICK_RATE_HZ ( ( TickType_t ) {{TICK_HZ}} )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( /* 例: 16*1024 */ ) ) // {{MEM_ALLOC}}
Zephyr(最小)
// prj.conf(抜粋)
# スレッド/タイマ関連
CONFIG_MAIN_STACK_SIZE=1024
CONFIG_SYSTEM_CLOCK_TICKS_PER_SEC={{TICK_HZ}}
CONFIG_PREEMPT_ENABLED=y
// app.overlay(DT抜粋のイメージ。必要に応じてGPIO/UARTノードを有効化)
/ {
aliases { console = &uart0; };
}
// src/main.c
#include <zephyr/kernel.h>
void main(void){
// hw_init();
while (1) {
// printk("tick\n");
k_msleep(1000);
}
}
ThreadX(Azure RTOS)(最小)
// tx_app.c
#include "tx_api.h"
TX_THREAD app_thread;
UCHAR app_stack[1024];
void app_entry(ULONG input){
(void)input;
for(;;){ tx_thread_sleep(TX_TICKS_PER_SECOND); }
}
void tx_application_define(void *first_unused_memory){
(void)first_unused_memory;
tx_thread_create(&app_thread, "app", app_entry, 0,
app_stack, sizeof(app_stack),
1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
}
int main(void){
// hw_init();
tx_kernel_enter();
}
7) 選定ガイド(用途別おすすめ)
- 超省リソース(RAM 8〜64 KiB):フットプリント最小の構成が作れる RTOS を優先。割込み応答と静的割当の容易さを重視。
- 大規模 SoC / Connectivity 重視:ドライバ群/ネットワークスタックの有無、ビルドシステムの規模感を確認。
- 量産保守/商用サポート重視:長期提供・脆弱性対応窓口や認証/規格対応の実績を確認。
- 教育/試作:ドキュメント/サンプルの豊富さと学習曲線を最優先。
意思決定フローチャート(簡易)
[要件定義: RAM/Flash/IRQ要件/周辺/認証]
|
v
[ドライバ/スタック要件 あり?]--No-->[最小フットプリント重視系へ]
|Yes
v
[公式/商用サポート必要?]--Yes-->[サポート重視系へ]
|No
v
[ビルド/設定の組織適合?]--No-->[教育/導入容易系へ]
|Yes
v
[PoCで実測→しきい値を満たすRTOSを採択]
8) 落とし穴と対策チェックリスト
- [ ] DWT無効で計測値が0に近づく(要 DWT_ENABLE)。
- [ ] 最適化差(-O0/-O2/-Os)の混在。共通化。
- [ ] 割り込み優先度の不整合(ベンダ HAL 初期化が上書き)。
- [ ] ティックレス混入で比較不能(明示的に無効化)。
- [ ] LTO/GC 設定差でフットプリントが変動。
- [ ] GPIO測定負荷を過小評価(トグル自体の遅延を校正)。
- [ ] 温度/電圧依存を無視(室温/電源条件を記録)。
- [ ] RTOSヒープ/スタック初期サイズの不一致。
- [ ] 外れ値除外で都合の良い平均にしない(分布で併記)。
- [ ] ブートローダ混在でブート時間が伸びる(含む/除くを明示)。
9) ライセンス/商用利用の注意
本稿では各 RTOS の具体的ライセンスは未確認のため言及を避ける。一般論として、MITやApache-2.0は再配布や改変に寛容で、特許ライセンス条項の有無が異なる。一方、ベンダ EULAや商用サブスクリプションは使用条件やサポート範囲が契約で定義される。本節は法的助言ではないため、採用前に必ず一次情報と法務レビューを行うこと。[^license]
10) 再現手順
リポジトリ:{{REPO_URL_OR_NONE}}(リポジトリがない場合は手順のみ記載)
# 1) 取得
git clone {{REPO_URL_OR_NONE}}
cd rtos-bench
# 2) ツールチェーン
# 例: arm-none-eabi-gcc / CMake / Ninja を PATH に通す
# バージョン: {{COMPILER}}
# 3) FreeRTOS ビルド
cmake -S freertos -B build/fr -DCMAKE_BUILD_TYPE=Release -DTICK_HZ={{TICK_HZ}}
cmake --build build/fr -j
# 4) Zephyr ビルド
west init -l zephyr
west update
west build -b {{BOARD_MODEL}} zephyr/app -t run
# 5) ThreadX ビルド
cmake -S threadx -B build/tx -DCMAKE_BUILD_TYPE=Release -DTICK_HZ={{TICK_HZ}}
cmake --build build/tx -j
# 6) 書き込み(例: OpenOCD)
openocd -f interface/jlink.cfg -f target/{{MCU_PART}}.cfg -c "program build/fr/app.elf verify reset exit"
# 7) 計測
# ロジアナで GPIO をキャプチャ、または SWO/ITM で DWT タイムスタンプを取得