RTOSは結局どれ?FreeRTOS/Zephyr/ThreadXを実機ベンチ

投稿者: | 2025年10月11日

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 の具体的ライセンスは未確認のため言及を避ける。一般論として、MITApache-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 タイムスタンプを取得

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です