更新履歴(3件・最終更新 2026年08月02日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- ETW、NUMA、SMT、DPC・ISRなどの略語を初出箇所で展開しました。「かもしれません」が続いていた箇所を条件付きの断定に書き換え、タスクマネージャーで確認する手順を表で追加し、EcoQoSのC#での書き方(スレッドプールに付けてはいけない理由を含む)と、次に読む記事の導線を追加しました。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.21589830)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「Windowsアプリ開発者のためのCPU設定入門:優先度・アフィニティ・Pコア/Eコア」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21589830 https://staging.comcomponent.com/blog/2026/06/02/000-windows-app-cpu-priority-affinity-power/
- DOI(最新版)
- 10.5281/zenodo.21589830
- DOI(この版)
- 10.5281/zenodo.21732841
Windowsアプリの性能は、コードだけでは決まりません。同じ .exe でも、動かすPCによって体感が変わることがあります。
あるPCでは周期処理が安定している。
別のPCでは、たまに遅れる。
AC電源では問題ないのに、バッテリー駆動だと妙に遅い。
タスクマネージャーで優先度を上げたのに、思ったほど速くならない。
Pコアに寄せたつもりなのに、まだ処理時間が揺れる。
逆に、性能を上げようとしたら、ファンが回りっぱなしになり、他のアプリが重くなる。
Windowsアプリを作っていると、こういう「コードだけでは説明しにくい現象」に出会います。
そのときに見たいのが、この4つです。
| 見るもの | ざっくり言うと |
|---|---|
| 優先度 | どのスレッドを先に動かすか |
| アフィニティ | どのCPUで動かしてよいか |
| Pコア / Eコア | そのCPUが性能寄りか、省電力寄りか |
| 省電力設定 | CPUをどれだけ本気で回すか |
さらに現代のWindowsでは、ここに EcoQoS やタスクマネージャーの Efficiency mode も関わります。
つまり、Windowsアプリの実行環境は、単に「CPUが速いか遅いか」ではありません。
いつ実行されるか
どこで実行されるか
どの種類のコアで実行されるか
CPUがどのくらいの性能状態で動いているか
OSに「性能重視」だと思われているか、「省電力でよい」と思われているか
このあたりが組み合わさって、実際の応答性や処理時間が決まります。
この記事では、Windowsアプリ開発で見落としやすい、優先度、アフィニティ、Pコア/Eコア、省電力設定の関係を整理します。
なお、この記事に登場するコードは、ビルド・実行できるサンプル一式(優先度・アフィニティを扱う C# ライブラリとデモ、PowerShell スクリプト、ユニットテスト)として GitHub で公開しています。
windows-app-cpu-priority-affinity-power - komurasoft-blog-samples (GitHub)
1. まず全体像
最初に、全体像を図にするとこうです。
flowchart LR A["アプリのコード"] --> B["Windows スケジューラ"] P["優先度<br/>いつ実行されやすいか"] --> B AF["アフィニティ / CPU Sets<br/>どのCPUで実行してよいか"] --> B Q["QoS / EcoQoS<br/>性能重視か、省電力重視か"] --> B B --> C["実行するスレッドを選ぶ"] C --> D["実行する論理プロセッサを選ぶ"] PE["Pコア / Eコア<br/>性能寄りか、省電力寄りか"] --> D PM["電源モード / 電源プラン / PPM<br/>周波数・ブースト・Core Parking"] --> D D --> R["実際の応答性<br/>処理時間<br/>発熱<br/>バッテリー消費<br/>他アプリへの影響"]
大事なのは、これらが独立していないことです。
優先度を上げれば、実行されやすくはなります。
しかし、CPU自体が省電力寄りに制御されていれば、期待したほど速くならないことがあります。
アフィニティでCPUを絞れば、スレッドの移動は減るかもしれません。
しかし、絞った先が省電力寄りのコアだったり、Core Parkingや電源管理と噛み合わなかったりすると、逆に不利になることがあります。
Pコアに寄せれば、計算処理は速くなるかもしれません。
しかし、すべてを高性能側に寄せると、発熱、ファンノイズ、バッテリー消費、他プロセスへの影響が増えます。
Windowsアプリの性能調整は、単純な「速くするボタン探し」ではありません。
どの処理を速くしたいのか。
どの処理は遅くてもよいのか。
ユーザー操作の応答性を優先するのか。
バックグラウンド処理の完了時間を優先するのか。
バッテリーや発熱をどこまで許容するのか。
その設計の話になります。
この記事の知識マップ
Windowsアプリの実行環境は、Windowsスケジューラがどのスレッドを実行するかを決めるCPU優先度、実行してよいCPUの範囲を決めるアフィニティ、性能重視か省電力重視かを示すQoSによって構成される。優先度はI/O待ちやロック競合などCPU外のボトルネックには効かず、常用は他スレッドの応答性を悪化させることがある。実行される論理プロセッサの性格はPコア・Eコアで異なり、CPU番号の決め打ちではなくCPU SetsのEfficiencyClassで見分けるべきである。電源モードはP-state・C-state・EPP・Core Parkingを通じてCPU周波数を左右し、省電力寄りの設定は優先度やPコアへの配置だけでは補えない周期の揺れを生む。EcoQoSとタスクマネージャーのEfficiency modeは、優先度低下とEcoQoSの組み合わせにより、急がないバックグラウンド処理を前景アプリの邪魔をしない形で実行させる仕組みである。
flowchart LR
accTitle: WindowsアプリのCPU優先度・アフィニティ・電源設定の知識マップ
accDescr: Windowsスケジューラの実行順序がCPU優先度・アフィニティ・QoSで構成され、実行するコアの選択にPコア/EコアとCore Parkingを含む電源モードが関わり、EcoQoSとタスクマネージャーのEfficiency modeがバックグラウンド処理を省電力側へ逃がす仕組みであることを示す図
cpu_scheduling_priority["CPU優先度(スケジューリング優先度)"]
cpu_affinity["CPUアフィニティ"]
heterogeneous_cpu_cores["Pコア/Eコア混在CPU"]
windows_scheduler["Windowsスケジューラ"]
qos["QoS(Quality of Service)"]
non_cpu_bottleneck["CPU外のボトルネック要因"]
system_responsiveness_degradation["システム全体の応答性悪化"]
realtime_sensitive_processing["リアルタイム性が求められる処理"]
etw["ETW(Event Tracing for Windows)"]
core_parking["Core Parking"]
p_core["Pコア"]
e_core["Eコア"]
cpu_sets["CPU Sets"]
efficiency_class["EfficiencyClass(CPU Setsの効率特性値)"]
wpr_wpa["WPR/WPA(Windows Performance Recorder/Analyzer)"]
power_mode["電源モード/電源プラン(PPM)"]
p_state["P-state"]
c_state["C-state"]
epp["EPP(Energy Performance Preference)"]
reduced_cpu_frequency["CPU周波数の低下"]
periodic_timing_jitter["周期処理の揺れ"]
ecoqos["EcoQoS"]
background_maintenance_task["急がないバックグラウンド処理"]
task_manager_efficiency_mode["タスクマネージャーのEfficiency mode"]
windows_scheduler -->|"で構成できる"| cpu_scheduling_priority
windows_scheduler -->|"で構成できる"| cpu_affinity
windows_scheduler -.->|"で構成できる"| qos
cpu_scheduling_priority -->|"用いるのは非推奨"| non_cpu_bottleneck
cpu_scheduling_priority -.->|"原因になり得る"| system_responsiveness_degradation
cpu_scheduling_priority -.->|"推奨される対応"| realtime_sensitive_processing
non_cpu_bottleneck -->|"で確認できる"| etw
cpu_affinity -.->|"両立しない"| core_parking
cpu_affinity -.->|"推奨される対応"| p_core
cpu_affinity -.->|"用いるのは非推奨"| e_core
cpu_sets -->|"利用する"| efficiency_class
p_core -->|"で確認できる"| efficiency_class
e_core -->|"で確認できる"| efficiency_class
windows_scheduler -->|"で確認できる"| wpr_wpa
power_mode -->|"利用する"| p_state
power_mode -->|"利用する"| c_state
power_mode -->|"利用する"| epp
power_mode -->|"利用する"| core_parking
power_mode -.->|"原因になり得る"| reduced_cpu_frequency
reduced_cpu_frequency -.->|"原因になり得る"| periodic_timing_jitter
ecoqos -->|"利用する"| qos
ecoqos -->|"推奨される対応"| background_maintenance_task
ecoqos -->|"用いるのは非推奨"| realtime_sensitive_processing
task_manager_efficiency_mode -->|"利用する"| ecoqos
task_manager_efficiency_mode -->|"利用する"| cpu_scheduling_priority
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全25件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
2. 優先度は「いつ実行されやすいか」
Windowsでは、実行可能なスレッドが複数あるとき、スケジューラが「次にどのスレッドをCPUに乗せるか」を決めます。このときに効くのが優先度です。
優先度は、大きく見ると 2 段階で決まります。
プロセスの優先度クラス
+ スレッドの相対優先度
= スレッドのベース優先度
Win32 APIで言えば、プロセス側には SetPriorityClass、スレッド側には SetThreadPriority があります。
PowerShellで今動いているプロセスの優先度を見るなら、たとえばこうです。
Get-Process -Id $PID | Select-Object Id, ProcessName, PriorityClass
現在のPowerShellプロセスの優先度を上げるなら、こうです。
$p = Get-Process -Id $PID
$p.PriorityClass = "AboveNormal"
C#なら、次のように書けます。
using System.Diagnostics;
using var process = Process.GetCurrentProcess();
process.PriorityClass = ProcessPriorityClass.AboveNormal;
ここで注意したいのは、優先度は「CPUを速くする設定」ではないことです。優先度が関わるのは、競合があるときに、どのスレッドを先に動かすかです。
CPU周波数を上げる設定ではありません。
Pコアを選ぶ設定でもありません。
I/Oを速くする設定でもありません。
ロック待ちやネットワーク待ちを消す設定でもありません。
そのため、「優先度を上げたのに速くならない」ということは普通にあります。たとえば、遅さの原因がこうしたものなら、優先度を上げても本質的には解決しません。
- ディスクI/O待ち
- ネットワーク待ち
- DB応答待ち
- ロック競合
- GC停止
- UIスレッドのブロック
- GPUやドライバ側の待ち
- ウイルス対策ソフトによるファイルスキャン
- CPU周波数が省電力寄りに抑えられている
また、HIGH_PRIORITY_CLASS や REALTIME_PRIORITY_CLASS は安易に使うものではありません。
高い優先度で長時間動き続けるスレッドは、他のスレッドにCPU時間を渡しにくくなります。
それが自分のアプリ内だけならまだしも、システム全体の応答性を悪くすることがあります。
優先度は、薬のようなものです。
効く場面では効きます。
しかし、量を増やせばよいものではありません。
優先度を上げる前に考えること
優先度を上げる前に、まず考えたいことがあります。
| 問い | 見たいこと |
|---|---|
| その処理は本当にCPU待ちか | CPU使用率、ETW(Event Tracing for Windows。OS標準のトレース機構)、プロファイラ |
| UIスレッドを塞いでいないか | UI応答、非同期化、キュー設計 |
| 高優先度にする範囲は短いか | 一時的に上げて、終わったら戻す |
| 他アプリに迷惑をかけないか | 入力、印刷、ブラウザ、常駐ソフトへの影響 |
| 顧客環境で権限やポリシーに阻まれないか | 管理者権限、実行ユーザー、セキュリティ製品 |
Windowsアプリで重要なのは、常に最高優先度で動くことではなく、必要な処理を、必要なタイミングで、必要な範囲だけ優先することです。
3. アフィニティは「どのCPUで実行してよいか」
優先度が「いつ実行されやすいか」だとすれば、アフィニティは「どのCPUで実行してよいか」です。
Windowsでは、プロセスやスレッドに対して、実行可能な論理プロセッサの集合を指定できます。
Win32 APIでは、プロセスに対して SetProcessAffinityMask、スレッドに対して SetThreadAffinityMask があります。
PowerShellでプロセスのアフィニティを見るなら、たとえばこうです。
Get-Process -Id $PID | Select-Object Id, ProcessName, ProcessorAffinity
検証目的で、現在のPowerShellプロセスを最初の4論理プロセッサに絞るなら、こう書けます。
$p = Get-Process -Id $PID
$p.ProcessorAffinity = [IntPtr]0xF
0xF は2進数で 1111 です。
つまり、論理プロセッサ0〜3を許可する、という意味になります。
ただし、これはあくまで検証用の例です。
アフィニティを絞ると、CPUキャッシュの局所性がよくなったり、周期処理の揺れが減ったりする場合があります。
一方で、Windowsが本来なら空いているCPUへ逃がせたはずのスレッドを、狭い場所に閉じ込めることにもなります。
特に、こういう環境では注意が必要です。
- Pコア/Eコアが混在している
- SMT/Hyper-Threading(Simultaneous Multi-Threading。1つの物理コアを複数の論理プロセッサとして見せる仕組み)で、論理プロセッサと物理コアの対応が直感的でない
- NUMA(Non-Uniform Memory Access。CPUから見たメモリまでの距離が一様でない構成)になっている
- 64論理プロセッサを超えてProcessor Groupが関係する
- Core Parkingが有効
- OEMやBIOSの電源制御が強い
- 仮想環境上で動いている
アフィニティは、システムに「このCPUだけで動かして」と強めに制約をかける設定です。
制約は、安定化の道具にもなります。
しかし、間違えると逃げ道を潰します。
いまはCPU Setsもある
古典的なアフィニティマスクは、かなり強い制約です。
一方、Windowsには CPU Sets という仕組みもあります。
CPU Setsは、アプリケーション側がCPUの希望をより柔らかく伝えるためのAPIです。
Microsoftの説明では、CPU SetsはOSの電源管理と互換性のある「soft」なアフィニティ指定として位置づけられています。
厳密にCPUを固定したいのか。
OSの電源管理やスケジューリングと協調しながら、だいたいの実行場所を寄せたいのか。
この違いは重要です。
昔ながらの SetProcessAffinityMask で全部解決しようとするのではなく、現代のWindowsアプリでは、CPU SetsやQoSも含めて考える必要があります。
4. Pコア/Eコアは「CPUにも性格がある」という話
最近のCPUでは、すべてのコアが同じ性能・同じ消費電力とは限りません。代表的なのがPコアとEコアで、ざっくり言えば、Pコアは性能寄り、Eコアは効率寄りのコアです。
| 種類 | 得意なこと |
|---|---|
| Pコア | 低遅延、高い単体性能、重い前景処理 |
| Eコア | 省電力、バックグラウンド処理、並列処理の受け皿 |
ただし、開発者が安易に「CPU 0〜7がPコアで、8〜15がEコア」と決め打ちするのは危険です。CPU番号の並びは、CPU、BIOS、Windowsバージョン、ファームウェア、OEM設定、仮想化環境によって変わる可能性があります。
Windows側では、CPU Setsの情報に EfficiencyClass という考え方があります。
これは、異種プロセッサを持つシステムで、そのCPU Setの効率特性を示す値です。Microsoftのドキュメントでは、この値が高いCPU Setほど、より高速だが電力効率は低いプロセッサを持つ、と説明されています。
Pコア/Eコアを扱いたいなら、単にCPU番号を見るだけでは足りません。本当に詰めるなら、こうした観察が必要です。
- CPU Sets APIで
EfficiencyClassを見る - Windows Performance Recorder / Analyzer(WPR / WPA。ETWトレースを採取して解析する標準ツール)でスレッドの実行CPUを見る
- タスクマネージャーの論理プロセッサ表示で傾向を見る
- 実機ごとに処理時間を測る
- AC電源時とバッテリー時で比較する
- 電源モードを変えて比較する
Pコア/Eコアは単なるハードウェア仕様ではなく、Windowsのスケジューラ、QoS、省電力設定と組み合わさって、実際の実行場所が決まります。
5. 省電力設定は「CPUをどれだけ本気で回すか」に関わる
ここが、現場ではかなり大事です。
優先度は「どのスレッドを先に動かすか」。
アフィニティは「どのCPUで動かしてよいか」。
Pコア/Eコアは「そのCPUが性能寄りか、省電力寄りか」。
そして省電力設定は、
そもそもCPUをどのくらいの周波数・電力状態で動かすか
に関わります。
つまり、こういうことが起きます。
優先度を上げた。
Pコアに寄せた。
でも電源設定が省電力寄りなら、
CPUは本気で回らないかもしれない。
これは、かなりWindowsらしい話です。
Windows 11では、設定アプリの「システム > 電源とバッテリー」から電源モードを選べます。
表記は環境やバージョンで変わりますが、方向性はだいたいこうです。
| 電源モード | 方向性 |
|---|---|
| 最も高い電力効率 / Best power efficiency | バッテリーや省電力を優先 |
| バランス / Balanced | 性能と電力のバランス |
| 最も高いパフォーマンス / Best performance | 性能を優先 |
さらに従来からある電源プランには、Power Saver、Balanced、High Performance があります。
Balanced は需要に応じて性能と消費電力を調整し、High Performance は消費電力と引き換えに最大性能を出しやすくする方向の設定です。
ユーザーから見える設定はシンプルでも、裏側にはProcessor Power Management、PPM、の設定があります。
6. P-state、C-state、ブースト、EPP
CPUは、常に最大周波数で動いているわけではなく、消費電力を下げるための状態を持っています。
| 用語 | ざっくり言うと |
|---|---|
| P-state | CPUの周波数や電圧を変える性能状態 |
| C-state | アイドル時にCPUの一部機能を止める省電力状態 |
| ブースト | 条件が合うと定格以上の高い性能状態に入る仕組み |
| EPP | Energy Performance Preference。性能重視か省電力重視かの好み |
P-stateは、CPUの周波数や電圧を変えて消費電力を下げる仕組みです。
C-stateは、CPUがアイドル時に一部機能を止めて、より深い省電力状態に入る仕組みです。
Windowsの電源管理は、こうした仕組みを使って、性能と消費電力のバランスを取ります。
そのため、「高優先度なのに遅い」という現象はあり得ます。
スレッドは優先的に実行されている。
しかし、CPU周波数が低い。
ブーストに入りにくい。
EPPが省電力寄り。
Core Parkingで使えるコアが制限されている。
バッテリー駆動でOS全体が省電力寄りに動いている。
こういう場合、優先度だけを見ても原因にたどり着けません。
特に、周期処理、画像処理、測定器制御、動画処理、音声処理、USBカメラ取り込み、シリアル通信などでは、平均処理時間だけでなく「たまに遅れる」ことが問題になります。
平均は速い。
でも、1000回に1回だけ遅れる。
その1回でバッファが詰まる。
UIが固まる。
装置とのタイミングがずれる。
こういう現象では、CPU使用率の平均だけでは不十分です。
処理時間の分布、最大値、外れ値、電源状態、実行CPU、バックグラウンド負荷を一緒に見る必要があります。
7. Core Parkingは「使えるコアの数」に関わる
Windowsには、Core Parkingという仕組みがあります。これは、使っていない論理プロセッサを休ませる方向の制御です。
使用率が低いときに一部のコアを低電力状態へ寄せることで、消費電力を抑えます。
Microsoftの資料では、CPMinCores は任意の時点で最低何%の論理プロセッサをun-parked、つまり使用可能状態にしておくかを指定する設定として説明されています。
値を100%にすると、Core Parkingアルゴリズムは無効になります。
ここで問題になるのが、アフィニティとの組み合わせです。
アフィニティで「このCPU群で動いてほしい」と制限した。
しかし、電源管理上、そのあたりのコアがどう扱われているかは別問題です。
Windows Server向けの資料でも、アクティブなスレッドがNUMAノード内のCPUの一部に強くアフィニティ指定されている場合、Core Parkingの判断と噛み合わない場面があると説明されています。
これはクライアントPCでも、考え方としては参考になります。
アフィニティは、スケジューラに制約をかけます。
Core Parkingは、電源管理が「どのコアを使える状態にするか」に関わります。
この2つを別々に考えると、原因を見誤ります。
8. EcoQoS / Efficiency Modeは「この処理は省電力でよい」と伝える仕組み
昔は、Windowsアプリの性能調整というと、優先度を上げる・下げる、アフィニティを変える、といった話が中心でした。しかし、現代のWindowsにはもう少し別の考え方があります。それがQoSです。
QoS、Quality of Service、は、スレッドに対して「この処理はどのくらい性能重視か、どのくらい省電力重視か」を示す考え方です。
Microsoftのドキュメントでは、スケジューリング優先度は次にどのスレッドを実行するかを決める主要な指標であり続ける一方、QoSはコア選択やプロセッサ電源管理に影響し得ると説明されています。
特に EcoQoS は、性能が最重要ではない処理を省電力寄りに扱うための仕組みです。
C++では、たとえば SetThreadInformation と ThreadPowerThrottling を使って、現在のスレッドをEcoQoSにできます。
#include <windows.h>
void EnableEcoQoSForCurrentThread()
{
THREAD_POWER_THROTTLING_STATE powerThrottling = {};
powerThrottling.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
powerThrottling.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
powerThrottling.StateMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
SetThreadInformation(
GetCurrentThread(),
ThreadPowerThrottling,
&powerThrottling,
sizeof(powerThrottling));
}
逆に、性能重視に戻す場合は、同じ制御対象に対して StateMask を0にします。
void DisableEcoQoSForCurrentThread()
{
THREAD_POWER_THROTTLING_STATE powerThrottling = {};
powerThrottling.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
powerThrottling.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
powerThrottling.StateMask = 0;
SetThreadInformation(
GetCurrentThread(),
ThreadPowerThrottling,
&powerThrottling,
sizeof(powerThrottling));
}
C#から使う場合、.NETのクラスライブラリにはEcoQoSを設定する専用の型がありません。SetThreadInformation をP/Invokeで呼びます。次は.NET 8想定の書き方です。
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
internal static class EcoQos
{
// THREAD_INFORMATION_CLASS の ThreadPowerThrottling
private const int ThreadPowerThrottling = 3;
// THREAD_POWER_THROTTLING_CURRENT_VERSION
private const uint CurrentVersion = 1;
// THREAD_POWER_THROTTLING_EXECUTION_SPEED
private const uint ExecutionSpeed = 0x1;
[StructLayout(LayoutKind.Sequential)]
private struct ThreadPowerThrottlingState
{
public uint Version;
public uint ControlMask;
public uint StateMask;
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetCurrentThread();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetThreadInformation(
IntPtr hThread,
int threadInformationClass,
ref ThreadPowerThrottlingState threadInformation,
uint threadInformationSize);
/// <summary>今のスレッドを EcoQoS にする(false で性能重視へ戻す)。</summary>
public static void SetForCurrentThread(bool enabled)
{
var state = new ThreadPowerThrottlingState
{
Version = CurrentVersion,
ControlMask = ExecutionSpeed,
StateMask = enabled ? ExecutionSpeed : 0u,
};
var ok = SetThreadInformation(
GetCurrentThread(),
ThreadPowerThrottling,
ref state,
(uint)Marshal.SizeOf<ThreadPowerThrottlingState>());
if (!ok)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
呼び出し側は、次のようになります。EcoQoSはスレッド単位の設定なので、専用スレッドを立てて、その中で付けて外すのが安全です。
using System.Threading;
// RunBackgroundMaintenance は、自分で用意する「急がない処理」です
var worker = new Thread(() =>
{
EcoQos.SetForCurrentThread(true);
try
{
RunBackgroundMaintenance();
}
finally
{
// 使い終わったら必ず戻す
EcoQos.SetForCurrentThread(false);
}
})
{
IsBackground = true,
};
worker.Start();
ここで注意したいのが、Task.Run やスレッドプールのスレッドに直接付けないことです。設定される対象はOSのスレッドであって、実行中の Task ではありません。プールのスレッドにEcoQoSを付けると、そのスレッドが次に別の仕事へ回されたときも省電力寄りのままになります。async / await で継続が別スレッドへ移れば、意図した区間の外で効いたり効かなかったりもします。
EcoQoSは、何でも省電力にすればよい、という機能ではありません。向いているのは、たとえばこんな処理です。
- バックグラウンドの同期
- 低優先のインデックス作成
- 急がないログ集計
- ユーザー操作に直接関係しないキャッシュ更新
- 後で終わればよいメンテナンス処理
一方で、こういう処理には慎重になるべきです。
- UI操作に直結する処理
- カメラ取り込み
- 音声処理
- 制御周期に関わる処理
- 検査装置の判定処理
- ユーザーが待っているエクスポート処理
- リアルタイム性に近い応答が必要な処理
タスクマネージャーの Efficiency mode も、この考え方と関係しています。
MicrosoftのPerformance Diagnosticsブログでは、Efficiency modeを有効にすると、プロセスのベース優先度をLowに下げ、QoSをEcoQoSに設定すると説明されています。
つまり、Efficiency modeは単なる「省電力アイコン」ではなく、低優先度化とEcoQoSを組み合わせて、前景アプリの応答性や電力効率を守るための仕組みです。
開発者目線では、これはかなり重要です。
「この処理は速く終わってほしい」だけでなく、
「この処理はユーザーの邪魔をしない範囲で動けばよい」
とOSに伝える設計ができるからです。
9. 判断フローチャート
ここまでの話は複雑なので、Windowsアプリの性能や周期の揺れを調べるときの判断フローを置いておきます。
flowchart TD
A["症状<br/>遅い・周期が揺れる・UIが固まる・ファンがうるさい"] --> B["まず記録する<br/>処理時間 / 最大値 / CPU使用率 / 電源状態 / AC or バッテリー / 対象PC"]
B --> C{"CPUが主因か?"}
C -- "いいえ / 不明" --> D["I/O・ロック・DB・ネットワーク・GC・GPU・ドライバを確認<br/>優先度やアフィニティの前に待ち時間を潰す"]
C -- "はい" --> E{"他プロセスとのCPU競合が強いか?"}
E -- "はい" --> F["優先度を検討<br/>ただし範囲を限定する<br/>High/Realtimeの常用は避ける"]
E -- "いいえ" --> G{"特定のCPUに偏っているか?"}
G -- "はい" --> H["アフィニティ / CPU Setsを確認<br/>固定しすぎていないか<br/>P/EコアやNUMAを見ているか"]
G -- "いいえ" --> I{"周波数・電源状態が怪しいか?"}
I -- "はい" --> J["電源モード / 電源プラン / PPMを確認<br/>P-state / EPP / ブースト / Core Parkingを疑う"]
I -- "いいえ" --> K{"バックグラウンド処理が前景を邪魔しているか?"}
K -- "はい" --> L["EcoQoS / Efficiency Mode / 低優先度化を検討<br/>急がない処理を省電力側へ逃がす"]
K -- "いいえ" --> M["アルゴリズム・並列度・キュー設計・UIスレッド設計を見直す"]
F --> N["1項目ずつ変更してA/B測定"]
H --> N
J --> N
L --> N
M --> N
N --> O["平均だけでなく<br/>最大値・外れ値・発熱・他アプリへの影響を見る"]
このフローで大事なのは、最初から設定をいじらないことです。まず記録します。
- 遅いのは常時か
- たまに遅いのか
- AC電源かバッテリーか
- 電源モードは何か
- タスクマネージャーでEfficiency modeになっていないか
- CPU使用率は高いか
- 周波数は上がっているか
- どのスレッドがCPUを使っているか
- 処理時間の最大値はどのくらいか
- 他の常駐ソフトやウイルス対策ソフトが関係していないか
そのうえで、1つずつ変えます。
優先度を変える。
アフィニティを変える。
電源モードを変える。
EcoQoSを付ける。
バックグラウンド処理を分離する。
キューを入れる。
UIスレッドから外す。
複数を同時に変えると、何が効いたのか分からなくなります。
10. 現場での確認コマンド
Windowsアプリの動作が環境によって違うとき、まずは状態を取れるようにしておくと便利です。
CPU情報を見る
Get-CimInstance Win32_Processor |
Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed
アクティブな電源プランを見る
powercfg /getactivescheme
プロセッサ電源管理の設定を見る
powercfg /q SCHEME_CURRENT SUB_PROCESSOR
出力はかなり長いですが、最小・最大プロセッサ状態、EPP、ブースト、Core Parkingに関係する設定を確認できます。
環境によって見える項目は異なります。
電源効率の診断レポートを作る
管理者権限のターミナルで実行します。
powercfg /energy
一定時間の計測後、HTMLレポートが出力されます。
ドライバ、デバイス、タイマー解像度、USBの省電力、スリープ阻害などを見る入口になります。
対象プロセスの優先度とアフィニティを見る
Get-Process -Name MyApp |
Select-Object Id, ProcessName, PriorityClass, ProcessorAffinity
タスクマネージャーで確認する
顧客先の端末では、PowerShellを開けないこともあります。GUIだけで確認したいときは、次の場所を見ます。表記はWindowsのバージョンで変わるため、括弧内の英語名も併記します。
| 見たいもの | 操作 |
|---|---|
| プロセスの優先度 | タスクマネージャー >「詳細」ページ(Details) > 列見出しを右クリック >「列の選択」>「基本優先度」(Base priority)にチェック |
| 優先度を変える | 「詳細」ページでプロセスを右クリック >「優先度の設定」(Set priority) |
| アフィニティを見る・変える | 「詳細」ページでプロセスを右クリック >「関係の設定」(Set affinity) |
| 効率モードの状態 | 「プロセス」ページ(Processes)の「状態」列(Status)に「効率モード」と表示される。子プロセスを効率モードにしている親プロセスには葉のアイコンが付く |
| 効率モードを付ける | 「プロセス」ページでプロセスを選び、コマンドバーの「効率モード」を押す。または右クリックメニューから選ぶ |
| 論理プロセッサごとの負荷 | 「パフォーマンス」ページ >「CPU」> グラフを右クリック >「グラフの変更」>「論理プロセッサ」 |
効率モードは、Windowsの中核プロセスには適用できません。止めるとシステムの安定性に関わるためです。
また、効率モードは表示上のラベルではありません。MicrosoftのPerformance Diagnosticsブログでは、効率モードがプロセスのベース優先度をLowに下げ、QoSをEcoQoSに設定すると説明されています。つまり、8章で書いた2つの設定をGUIから一括で掛けているだけです。
なお、タスクマネージャーの「詳細」ページで見えるのは、あくまでプロセス単位の優先度です。スレッド単位の相対優先度や、スレッドごとのQoSは表示されません。そこまで見るなら、WPR / WPAでETWを取ります。
CPUの性能状態を見る
環境によって利用できるカウンター名は変わりますが、このあたりのPerformance Counterが参考になります。
Get-Counter '\Processor Information(_Total)\% Processor Performance'
Get-Counter '\Processor Information(_Total)\% Processor Utility'
本格的に見るなら、Windows Performance Recorder(WPR)とWindows Performance Analyzer(WPA)でETWを取る方が確実です。
周期の揺れ、コンテキストスイッチ、実行CPU、DPC/ISR(Deferred Procedure Call / Interrupt Service Routine。ドライバの割り込み処理と、その後回し実行)、ディスクI/O、CPU周波数の変化をまとめて追えます。
11. ソフトリアルタイム処理では、全部まとめて考える
Windowsは、一般的な意味でのリアルタイムOSではありません。しかし、現場のWindowsアプリでは、リアルタイムに近い性質を求められることがあります。
- USBカメラから一定周期で画像を取る
- シリアル通信で装置とやり取りする
- 測定器からデータを読む
- PLCや外部機器と同期する
- 音声や動画を処理する
- 検査結果を一定時間内に返す
- UIを固めずに重い計算を走らせる
こういう処理では、コードの速さだけでは足りません。
スレッド設計が必要です。
キュー設計が必要です。
ログが必要です。
タイムアウトが必要です。
バックプレッシャー(下流が処理しきれないときに、上流の流量を意図的に落とす仕組み)が必要です。
遅れたときに捨てるのか、待つのか、再試行するのかを決める必要があります。
そして、そのうえで、優先度、アフィニティ、Pコア/Eコア、省電力設定を見る必要があります。
たとえば、カメラ取り込みスレッドを考えます。
このスレッドは、ユーザー操作に近く、周期性もあります。
だから、EcoQoSは付けるべきではありません。
優先度は、取り込み区間に限って1段だけ上げる価値があります。ただし、HIGH_PRIORITY_CLASS 以上を常用してはいけません。UIや他の処理に悪影響が出ます。
アフィニティは、実測で周期の揺れとCPU移動の相関が見えたときだけ固定します。固定するなら、CPU番号ではなく EfficiencyClass を見てPコア側を選ぶべきです。Eコア側へ固定すると逆効果になります。
電源モードが省電力寄りなら、AC電源では出なかった遅れがバッテリー時に出ます。だから、性能試験にはバッテリー条件を必ず入れてください。
一方で、古いログの圧縮処理ならどうでしょうか。
これは、ユーザーが待っていない処理の代表です。
それなら、優先度を下げる。
EcoQoSにする。
アイドル時に動かす。
AC電源時だけ動かす。
こういう設計の方が、アプリ全体として親切です。
すべての処理を「速く」するのではなく、速くするべき処理と、邪魔をしないべき処理を分けます。
12. 設計の基本方針
Windowsアプリで、このあたりを扱うときの基本方針は 6 つあります。
1. 最初から固定しない
最初から優先度やアフィニティを強く固定しない方がよいです。
Windowsのスケジューラは、多くの場合、かなりよくやってくれます。
アプリ側が余計な制約をかけることで、かえって悪くなることがあります。
まずは普通に作る。
測る。
問題が出たら、仮説を立てる。
小さく変える。
また測る。
この順番です。
2. 優先度は一時的に使う
高い優先度は、必要な区間だけにします。
ずっと高優先度にするより、
重要な処理の直前に上げる
終わったら戻す
の方が安全です。
3. アフィニティは最後の方で検討する
アフィニティは強い設定です。
周期の揺れやCPU移動の影響が疑われるときには有効なことがあります。
しかし、最初に触る設定ではありません。
特に、顧客PCが複数種類あるなら、CPU番号の決め打ちは危険です。
4. Pコア/Eコアは「決め打ち」ではなく「観察」する
Pコア/Eコアを使い分けたいなら、まず実機で観察します。
CPU番号で決め打ちするのではなく、CPU Sets、EfficiencyClass、ETW、実測で見ます。
5. 省電力設定を前提にテストする
開発機がAC電源・高性能設定だから速い、というだけでは不十分です。
顧客環境では、こういう状態が普通にあります。
- ノートPCのバッテリー駆動
- Best power efficiency
- Energy saver
- OEM独自の省電力ユーティリティ
- 企業ポリシーで固定された電源設定
- 熱で性能が落ちる薄型PC
- 常駐ソフトが多い業務端末
性能試験では、少なくともこのくらいの条件は分けて見たいです。
| 条件 | 見ること |
|---|---|
| AC + Best performance | 最大性能に近い状態 |
| AC + Balanced | 標準的な業務利用 |
| Battery + Balanced | ノートPCの現実 |
| Battery + Best power efficiency | 省電力寄りの下限 |
| 長時間連続実行 | 熱、ファン、サーマルスロットリング |
| 他アプリ同時利用 | ブラウザ、Teams、Excel、ウイルス対策ソフトとの共存 |
6. バックグラウンド処理にはEcoQoSを検討する
ユーザー体験に直結しない処理は、性能を取りに行くより、邪魔をしないことを優先した方がよい場合があります。
すべてを高性能で動かすアプリは、速いかもしれません。
しかし、他の作業を邪魔します。
ノートPCのファンを回します。
バッテリーを消費します。
結果として、使いにくいアプリになります。
Windowsアプリでは、速さだけでなく「共存のうまさ」も品質です。
13. ありがちな誤解
優先度を上げれば速くなる
必ずしも速くなりません。
CPU競合が原因なら効くことがあります。
しかし、I/O待ち、ロック待ち、省電力による低周波数、Eコア側での実行などが原因なら、優先度だけでは不十分です。
Pコアに固定すれば常に正解
常に正解ではありません。
Pコアは高性能ですが、発熱や消費電力も増えます。
また、他の重要な前景処理と競合する可能性もあります。
Pコアに寄せるべき処理と、Eコアで十分な処理を分ける方が大事です。
省電力設定はユーザーの好みで、アプリには関係ない
関係あります。
同じアプリでも、電源モード、電源プラン、EPP、ブースト、Core Parkingによって、実行され方が変わります。
特にノートPCでは、AC電源とバッテリーで挙動が変わります。
Efficiency modeは単に遅くするだけ
単に遅くするだけではありません。
低優先度化とEcoQoSによって、前景アプリの応答性や電力効率を守るための仕組みです。
急がないバックグラウンド処理には、むしろ積極的に考える価値があります。
Windowsだから安定しない
これも雑な見方です。
WindowsはリアルタイムOSではありません。
しかし、スケジューリング、電源管理、QoS、測定、ログ、スレッド設計を理解すれば、かなり現場向けに安定させられます。
問題は「Windowsだから無理」ではなく、どの層で何が起きているかを見ていないことです。
14. 実装より先に、ログを設計する
この手の問題は、顧客環境でだけ起きることがあります。だから、アプリ側で最低限の診断情報を残せるようにしておくと助かります。
たとえば、診断ログにはこういう情報を出します。
- アプリバージョン
- Windowsバージョン
- CPU名
- 論理プロセッサ数
- AC電源かバッテリーか
- 処理時間の平均・最大・パーセンタイル
- 処理周期の遅延回数
- 対象スレッドの優先度
- プロセス優先度
- アフィニティ設定の有無
- EcoQoS設定の有無
- 起動時の電源プラン
- 対象処理がタイムアウトした回数
顧客先で「たまに遅い」と言われたとき、何も記録がないと推測の勝負になります。ログがあれば、仮説を立てられます。
バッテリー時だけ遅い
特定PCだけ遅い
起動直後だけ遅い
30分後から遅い
他アプリ起動中だけ遅い
一定周期で外れ値が出る
こういう差分が見えれば、優先度なのか、アフィニティなのか、電源設定なのか、熱なのか、別の待ちなのか、切り分けやすくなります。
15. 小村ソフト的な見方
Windowsアプリの開発では、きれいな理屈だけでは足りません。
お客様のPCで動くこと。
現場の端末で動くこと。
古い周辺機器と共存すること。
ウイルス対策ソフトやプリンタや社内ポリシーのある環境で動くこと。
ノートPCの省電力設定でも破綻しないこと。
性能が必要なところでは、きちんと性能を出すこと。
急がない処理では、ユーザーの邪魔をしないこと。
そのためには、Windowsを単なる「黒箱のOS」として見ない方がよいです。
Windowsは、スレッドをどう動かすかを考えています。
どのCPUで動かすかを考えています。
電力と性能のバランスを取っています。
Pコア/Eコアのような異種コアを扱っています。
前景アプリとバックグラウンド処理を区別しようとしています。
開発者は、それに逆らうのではなく、必要なところで意図を伝えるべきです。
この処理はユーザーを待たせている。
この処理は多少遅くてもよい。
この処理は周期が重要。
この処理はバックグラウンドで静かに動けばよい。
この処理は他の処理を邪魔してはいけない。
そういう設計を、優先度、アフィニティ、QoS、省電力設定の理解に落とし込みます。
次に読む
この記事で扱ったのは、CPU側の設定です。「遅い」の原因がCPUの外にあるときは、次の記事のほうが近道です。
| 症状 | 次に読む記事 |
|---|---|
| どこで時間を使っているかが分からない | PerfViewとdotnet-traceで「遅い」を特定する ── .NETパフォーマンス調査の実務入門 |
| 顧客環境の記録を残す仕組みから作りたい | Windowsイベントログ・ETW入門 ── 業務アプリのログをOS標準の仕組みに乗せる |
| I/O待ちやスレッドプールの詰まりが疑わしい | Windows I/Oの深層(第3回) ── I/O完了ポート(IOCP)と.NETスレッドプール |
| メモリが増え続けて遅くなる | .NETでGC待ちとメモリリークを見分ける |
| UIだけが固まる | WPF/WinFormsのasyncとUIスレッドを一枚で整理 |
16. まとめ
Windowsアプリの性能は、コードだけでは決まりません。
優先度を上げても、アフィニティでEコア側に寄っていれば期待通りには動きません。
Pコアに寄せても、電源モードが省電力寄りならCPUは最大性能を出しません。
CPU SetsやQoSを考えずに強いアフィニティをかければ、Windowsの電源管理やスケジューラの逃げ道を潰します。
すべてを高性能側に振れば、発熱、ファンノイズ、バッテリー消費、他アプリへの影響が増えます。
Windowsアプリ開発では、速さだけでなく、応答性、安定性、消費電力、発熱、他プロセスとの共存まで含めて設計する必要があります。
そのために見るべきものは、やはりこの4つです。
優先度
アフィニティ
Pコア / Eコア
省電力設定
そして現代のWindowsでは、そこにEcoQoSやEfficiency modeも加わります。
心配することは多いです。
しかし、心配をそのまま不安にしておく必要はありません。
心配を、計測に変える。
心配を、ログに変える。
心配を、設計に変える。
心配を、テスト条件に変える。
そうすれば、Windowsは単なる気まぐれな実行環境ではなく、かなり観察しがいのある、現場向けのプラットフォームになります。
WindowsはリアルタイムOSではありません。
それでも、実行環境を理解して設計すれば、現場で十分に頼れるアプリを作ることはできます。
そして、その難しさこそが、Windowsアプリ開発の面白さでもあります。
参考リンク
- この記事のサンプルコード一式(C# / PowerShell / ユニットテスト) - komurasoft-blog-samples (GitHub)
- Scheduling Priorities - Microsoft Learn
- SetProcessAffinityMask function - Microsoft Learn
- SetThreadAffinityMask function - Microsoft Learn
- CPU Sets - Microsoft Learn
- SYSTEM_CPU_SET_INFORMATION structure - Microsoft Learn
- Change the power mode for your Windows PC - Microsoft Support
- Power Policy Settings - Microsoft Learn
- P-states and C-States - Microsoft Learn
- Processor power management options - Microsoft Learn
- CPMinCores - Microsoft Learn
- Quality of Service - Microsoft Learn
- SetThreadInformation function - Microsoft Learn
- Reduce Process Interference with Task Manager Efficiency Mode - Microsoft DevBlogs
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Windowsアプリ 外注・受託開発を依頼する前に整理したいこと
Windowsアプリの外注・受託開発を依頼する前に、既存ソフト改修、装置連携、COM/ActiveX、配布・更新、保守の整理ポイントを解説します。
Windows効率モードとは - 緑の葉アイコンとオフにする方法
Windows 11 のタスク マネージャーに出る効率モードの意味、緑の葉アイコンの見方、オフにする手順、レジストリや電源モードで全プロセスの効率モードを極力走らせなくする方法、オフにできないケース、Microsoft Edge の省エネ機能との違いを整理します。
Windowsのプロセッサのスケジュール設定 - バックグラウンドサービスとP/Eコア
Windows の「バックグラウンド サービス」設定で何が変わるかを、量子時間、foreground優遇、音切れ、Pコア/Eコア時代のQoSから整理します。
マルチスレッドの実務ベストプラクティス C言語編 ── Win32 APIの流儀で安全に書く
C言語×Win32のマルチスレッドは、_beginthreadexでのスレッド作成、SRWロックと条件変数、Interlocked、停止イベント+WaitForMultipleObjectsの停止設計が定石。TerminateThreadの危険とDllMainの制約まで整理...
マルチスレッドの実務ベストプラクティス C++編 ── RAIIとjthreadで事故を構造から消す
C++のマルチスレッドはデータ競合が未定義動作になる世界。std::threadのデストラクタの罠、jthreadとstop_tokenによる停止設計、scoped_lockのデッドロック回避、atomicの正しい位置づけ、Win32同期APIとの使い分けまで整理します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
Windowsアプリ開発
業務アプリ、装置連携、通信ツールなどの Windows ソフト開発を支援します。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- CPU優先度はどうやって設定しますか?
- 優先度は、プロセスの優先度クラスとスレッドの相対優先度の2段階で決まります。Win32 API ではプロセス側に SetPriorityClass、スレッド側に SetThreadPriority があります。PowerShell なら Get-Process で取得したオブジェクトの PriorityClass プロパティに "AboveNormal" などを設定でき、C# では Process.GetCurrentProcess() の PriorityClass に ProcessPriorityClass.AboveNormal を代入します。ただし HIGH_PRIORITY_CLASS や REALTIME_PRIORITY_CLASS の常用はシステム全体の応答性を悪化させるため避けるべきです。
- 優先度を上げたのにアプリが速くならないのはなぜですか?
- 優先度は「競合があるときにどのスレッドを先に動かすか」を決める設定であって、CPU を速くする設定ではないからです。遅さの原因がディスク I/O 待ち、ネットワーク待ち、ロック競合、GC 停止、UI スレッドのブロック、ウイルス対策ソフトのスキャン、あるいは省電力設定で CPU 周波数が抑えられていることなら、優先度を上げても本質的には解決しません。まず処理時間の分布や電源状態、実行 CPU を記録して原因を切り分けることが大事です。
- SetThreadInformationでEcoQoSを設定すると何が起きますか?
- SetThreadInformation に ThreadPowerThrottling と THREAD_POWER_THROTTLING_EXECUTION_SPEED を指定すると、そのスレッドを EcoQoS(省電力寄りの QoS)として扱うよう OS に伝えられます。QoS はコア選択やプロセッサ電源管理に影響し得るため、バックグラウンド同期や急がないログ集計のような処理を、前景アプリの邪魔をしない省電力側へ逃がせます。一方、UI 操作に直結する処理やカメラ取り込み、制御周期に関わる処理には慎重になるべきです。StateMask を 0 にすれば性能重視に戻せます。
- powercfgのCPMinCoresとは何ですか?
- CPMinCores は Windows の Core Parking に関する電源設定で、任意の時点で最低何%の論理プロセッサを un-parked(使用可能状態)にしておくかを指定します。値を100%にすると Core Parking アルゴリズムは無効になります。現在の設定は管理者権限で powercfg /q SCHEME_CURRENT SUB_PROCESSOR を実行すると確認でき、アフィニティで CPU を絞っている場合は Core Parking の判断と噛み合わない場面があるため、両方を合わせて見る必要があります。