Windowsソフトリアルタイム実践ガイド

· · Windows開発, ソフトリアルタイム, 設計, 計測

1. 結論まとめ

  • 普通の Windows(10/11)で目指すのは hard real-time の保証ではなく、遅延とジッタを小さくし deadline miss を減らすこと
  • まず見直すべきは優先度の値より、周期スレッドの中に何を入れているか
  • 周期処理を fast path(時間厳守)と slow path(許容できる遅延)に分ける
  • fast path では Sleep 任せの待機、ブロッキングI/O、毎回のnew/malloc、無制限キューを避ける
  • 実運用では AC給電、電源モード、timer resolution、power throttling、バックグラウンド負荷 が効く
  • 評価は平均値だけでなく p99 / p99.9 / max / miss回数 / queue深さ / DPC / ISR / page fault で見る

2. 対象範囲

対象 対象外
Windows 10/11 の一般的なPC 専用RTOSやRT拡張製品
user-modeの通常アプリ(C++/C#) カーネルドライバ主体の制御
標準APIと標準設定の範囲 FPGAやマイコンへの全面移管

3. 基本用語

用語 意味
遅延 予定より処理が遅れて始まる/終わること
ジッタ 周期や処理時間のばらつき。毎回同じ間隔で動かないこと
deadline miss 決めた期限までに処理が終わらないこと

4. チェックリスト:全体像

確認項目 やること 典型的なNG
待機方法 絶対期限で回す。イベント駆動か高精度waitable timer Sleep(1)ベースの周期ループ
処理の分離 fast path と slow path を分ける fast path に保存/送信/UIを入れる
キュー 固定長にして、あふれ時の方針を決める 無制限キューで先送り
fast path の中身 割り当て・重いログ・ブロッキングI/O・重いロックを外す new/malloc/同期I/O
優先度 必要なスレッドだけ上げる。音声/映像はMMCSS いきなりREALTIME_PRIORITY_CLASS
OS/電源 AC給電、電源モード、timer resolutionを確認 バッテリー駆動や省電力モードのまま評価

5. 各項目の詳細

5.1 周期ループは Sleep 任せにしない

Sleep(1) は「1msきっかり待つ」ではなく「少なくとも1ms以上待つ」。周期のずれが累積する。

絶対期限方式に変更する:

int64_t next = QpcNow() + periodTicks;

while (running)
{
    WaitUntil(next - wakeMarginTicks);
    while (QpcNow() < next) { CpuRelax(); }  // 最後だけ短くspin

    int64_t started = QpcNow();
    FastStep();  // no blocking, no alloc, no heavy lock
    int64_t finished = QpcNow();

    RecordTiming(next, started, finished);
    next += periodTicks;  // ポイント:next = now + period にしない

    while (finished > next)
    {
        ++missedDeadlines;  // 遅れを記録
        next += periodTicks;
    }
}

2つのポイント:

  • 毎回 next = now + period にしない(ずれが累積しない)
  • 遅れたときにどうするかを先に決めておく

5.2 fast path と slow path を分ける

デバイス/タイマ → [fast path: 取得/制御/最小限のコピー] → [固定長キュー] → [slow path: 整形/保存/送信/UI]

fast path に置くものだけ:

  • データ取得
  • 制御値計算
  • 最小限のコピー
  • タイムスタンプ
  • キュー投入
  • missやoverrunの記録

それ以外はすべて slow path に送る。

5.3 キューは固定長

無制限キューは遅延を見えにくくするだけ。

あふれ時の方針を3つから選ぶ:

  • 最新値優先:古いデータを捨てる
  • 欠落不可:エラー/停止/アラート
  • ログ用途:ドロップ数を記録

5.4 fast path に重い処理を入れない

避けるべきもの:

  • ファイル書き込み
  • ネットワーク送信
  • DB書き込み
  • 重いログ出力
  • 毎回の new / malloc / List<T>.Add
  • 毎回の文字列連結や ToString()
  • 重いロック
  • ページフォルトを呼びやすい初回アクセス処理

特に注意すること:

  1. 割り当てと解放:fast path ではバッファを事前確保して再利用
  2. ブロッキングI/O:開発機では速くても本番では揺れる
  3. ページフォルト:起動時に必要なメモリに一度触れておく

5.5 優先度は必要なスレッドだけ上げる

基本方針:

  • UIや通常ワーカーは普通の優先度
  • fast path スレッドだけ必要に応じて上げる
  • 保存/送信/ログ集約は background mode を検討
  • プロセス全体よりスレッド単位で考える

音声/映像の連続ストリームではまず MMCSS を検討

DWORD taskIndex = 0;
HANDLE hAvrt = AvSetMmThreadCharacteristicsW(L"Audio", &taskIndex);
// ... 処理 ...
AvRevertMmThreadCharacteristics(hAvrt);

REALTIME_PRIORITY_CLASS は副作用が大きいので、専用機で十分検証した上で本当に必要な場合のみ。

5.6 電源設定チェックリスト

  1. AC給電で動かす(バッテリー駆動のまま詰めても安定しない)
  2. 電源モードを「最適なパフォーマンス」寄りにする
  3. 必要なら専用の電源プランを作る(普段はバランス、本番だけ専用)
  4. プロセッサの最小/最大の状態を確認(AC時100%/100%を試す価値あり)
  5. process power throttling / EcoQoS を確認
  6. 不要なバックグラウンド負荷を減らす(クラウド同期、自動更新、常駐監視など)
  7. 最小化時/非表示時もテスト(Windows 11では非表示アプリでtimer resolutionが変わることがある)
  8. BIOS/UEFIは最後(C-stateや静音設定は機種依存が強い)

6. 計測と評価

6.1 記録すべき項目

  • 周期予定時刻、実開始時刻、実終了時刻
  • lateness(遅延量)
  • 実行時間
  • missed deadline 数 / 連続 missed deadline 数
  • queue 深さ / ドロップ数
  • DPC / ISR スパイク
  • page fault
  • 温度 / クロック変動

6.2 p99 / p99.9 / max の見方

  • 平均:全体の傾向。大きな遅延は埋もれやすい
  • p99:1000サンプル中、遅い方10件を除いた上限
  • p99.9:1000サンプル中、遅い方1件を除いた上限
  • max:最悪値

「普段は大丈夫だがときどき引っかかる」を見るには p99 / p99.9 / max が必須。

6.3 使う道具

  • アプリ内計測:まず自前で period、lateness、execution time、queue depth を取る
  • ETW / WPR / WPA:CPU、context switch、DPC/ISR、page fault を見る
  • LatencyMon:ドライバ起因の揺れのあたりを付ける
  • 温度/クロック監視:サーマルスロットリングの影響を見る

6.4 テスト条件

以下の条件を分けてテストする:

  • 起動直後(ウォームアップ前) / ウォームアップ後
  • 長時間連続運転
  • UI前面 / UI最小化・非表示
  • AC給電 / バッテリー駆動
  • ネットワークやディスク負荷あり

7. ざっくり使い分け

要求レベル 現実的なアプローチ
10〜20ms級、たまの揺れは吸収可 fast/slow分離、固定長キュー、通常優先度、イベント駆動で十分
1〜5ms級、継続的に間に合わせたい fast pathの無割り当て化、専用スレッド、MMCSS、高精度waitable timer、AC給電
1ms未満、長時間高負荷 user-mode単独では厳しい。クリティカル部分を別場所(FPGA/RTOS等)に逃がす
GUI/ログ/通信/DBと同居 1プロセス1ループで抱え込まず、責務を分離する

8. まとめ

実務での改善順:

  1. 周期ループが Sleep 任せになっていないか
  2. fast path と slow path が分かれているか
  3. キューが固定長で、あふれ時の方針が決まっているか
  4. fast path にI/O、割り当て、重いロックが入っていないか
  5. 優先度やMMCSSを必要なスレッドだけに使っているか
  6. 電源設定と計測を揃えているか

普通の Windows でも、設計・待機方法・電源設定・計測を揃えれば soft real-time として十分実用的になる。

9. 参考資料

関連する記事

同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。

未想定例外発生時の対応判断フロー

想定していない例外が発生したとき、アプリを終了すべきか継続すべきかを判断するための実用的な指針をまとめた記事です。失敗単位、共有状態、外部副作用、ネイティブ境界の観点から3段階の選択肢と判断表で整理し、設計判断に直接使えます。

記事を読む

Windowsアプリのセキュリティチェックリスト

WPF・WinForms・WinUI・C++・C# など Windows アプリ開発で最低限外したくないセキュリティ項目を、権限・署名・秘密情報・通信・入力・DLL・ログの観点でチェックリスト化し、リリース前に何を確認すべきか整理した記事です。

記事を読む

FileSystemWatcherの安全な使い方

FileSystemWatcher のイベントは完了通知ではないという前提に立ち、取りこぼし、重複通知、完了判定の落とし穴を整理し、再スキャン要求への畳み込み、原子的 claim、idempotency までの設計指針をまとめます。

記事を読む

関連トピック

このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。

このテーマがつながるサービス

この記事は次のサービスページにつながります。近い入口からご覧ください。

ブログ一覧に戻る