Windowsアプリ開発者のためのCPU設定入門:優先度・アフィニティ・Pコア/Eコア

· · Windows, Windowsアプリ, CPU, パフォーマンス, 優先度, アフィニティ, Pコア, Eコア, 省電力, EcoQoS

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コア、省電力設定の関係を整理します。

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アプリの性能調整は、単純な「速くするボタン探し」ではありません。

どの処理を速くしたいのか。
どの処理は遅くてもよいのか。
ユーザー操作の応答性を優先するのか。
バックグラウンド処理の完了時間を優先するのか。
バッテリーや発熱をどこまで許容するのか。

その設計の話になります。

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_CLASSREALTIME_PRIORITY_CLASS は安易に使うものではありません。

高い優先度で長時間動き続けるスレッドは、他のスレッドにCPU時間を渡しにくくなります。
それが自分のアプリ内だけならまだしも、システム全体の応答性を悪くすることがあります。

優先度は、薬のようなものです。

効く場面では効きます。
しかし、量を増やせばよいものではありません。

優先度を上げる前に考えること

優先度を上げる前に、まず考えたいのは次のことです。

問い 見たいこと
その処理は本当にCPU待ちか CPU使用率、ETW、プロファイラ
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で論理プロセッサと物理コアの対応が直感的でない
  • NUMA構成になっている
  • 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でスレッドの実行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 SaverBalancedHigh Performance があります。
Balanced は需要に応じて性能と消費電力を調整し、High Performance は消費電力と引き換えに最大性能を出しやすくする方向の設定です。

ユーザーから見える設定はシンプルでも、裏側にはProcessor Power Management、PPM、の設定があります。

6. P-state、C-state、ブースト、EPP

CPUは、常に最大周波数で動いているわけではありません。

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++では、たとえば SetThreadInformationThreadPowerThrottling を使って、現在のスレッドを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));
}

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

CPUの性能状態を見る

環境によって利用できるカウンター名は変わりますが、次のようなPerformance Counterが参考になります。

Get-Counter '\Processor Information(_Total)\% Processor Performance'
Get-Counter '\Processor Information(_Total)\% Processor Utility'

本格的に見るなら、Windows Performance RecorderとWindows Performance AnalyzerでETWを取る方が確実です。
周期の揺れ、コンテキストスイッチ、実行CPU、DPC/ISR、ディスクI/O、CPU周波数の変化をまとめて追えます。

11. ソフトリアルタイム処理では、全部まとめて考える

Windowsは、一般的な意味でのリアルタイムOSではありません。

しかし、現場のWindowsアプリでは、リアルタイムに近い性質を求められることがあります。

  • USBカメラから一定周期で画像を取る
  • シリアル通信で装置とやり取りする
  • 測定器からデータを読む
  • PLCや外部機器と同期する
  • 音声や動画を処理する
  • 検査結果を一定時間内に返す
  • UIを固めずに重い計算を走らせる

こういう処理では、コードの速さだけでは足りません。

スレッド設計が必要です。
キュー設計が必要です。
ログが必要です。
タイムアウトが必要です。
バックプレッシャーが必要です。
遅れたときに捨てるのか、待つのか、再試行するのかを決める必要があります。

そして、そのうえで、優先度、アフィニティ、Pコア/Eコア、省電力設定を見る必要があります。

たとえば、カメラ取り込みスレッドを考えます。

このスレッドは、ユーザー操作に近く、周期性もあります。
EcoQoSにするべきではないかもしれません。
優先度を少し上げる価値はあるかもしれません。
ただし、上げすぎるとUIや他の処理に悪影響が出ます。
アフィニティを固定すると安定するかもしれません。
しかし、固定先がEコア寄りなら逆効果かもしれません。
電源モードが省電力寄りなら、バッテリー時だけ遅れるかもしれません。

一方で、古いログの圧縮処理ならどうでしょうか。

これはユーザーが待っていない処理かもしれません。
それなら、優先度を下げる。
EcoQoSにする。
アイドル時に動かす。
AC電源時だけ動かす。
こういう設計の方が、アプリ全体として親切です。

すべての処理を「速く」するのではありません。

速くするべき処理と、邪魔をしないべき処理を分けます。

12. 設計の基本方針

Windowsアプリで、このあたりを扱うときの基本方針は次の通りです。

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、省電力設定の理解に落とし込みます。

16. まとめ

Windowsアプリの性能は、コードだけでは決まりません。

優先度を上げても、アフィニティでEコア側に寄っていれば期待通りに動かないかもしれません。
Pコアに寄せても、電源モードが省電力寄りならCPUは最大性能を出さないかもしれません。
CPU SetsやQoSを考えずに強いアフィニティをかけると、Windowsの電源管理やスケジューラの逃げ道を潰すかもしれません。
すべてを高性能側に振れば、発熱、ファンノイズ、バッテリー消費、他アプリへの影響が増えます。

Windowsアプリ開発では、速さだけでなく、応答性、安定性、消費電力、発熱、他プロセスとの共存まで含めて設計する必要があります。

そのために見るべきものは、次の4つです。

優先度
アフィニティ
Pコア / Eコア
省電力設定

そして現代のWindowsでは、そこにEcoQoSやEfficiency modeも加わります。

心配することは多いです。

しかし、心配をそのまま不安にしておく必要はありません。

心配を、計測に変える。
心配を、ログに変える。
心配を、設計に変える。
心配を、テスト条件に変える。

そうすれば、Windowsは単なる気まぐれな実行環境ではなく、かなり観察しがいのある、現場向けのプラットフォームになります。

WindowsはリアルタイムOSではありません。
それでも、実行環境を理解して設計すれば、現場で十分に頼れるアプリを作ることはできます。

そして、その難しさこそが、Windowsアプリ開発の面白さでもあります。

参考リンク

関連する記事

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

関連トピック

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

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

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

ブログ一覧に戻る