更新履歴(5件・最終更新 2026年08月25日)
この記事に加えた変更の記録です。アーカイブした更新前のバージョンは、DOI付きの固定URLから読めます。
- 不自然な口語・比喩を、意味を変えずに技術文書として自然な日本語に直しました。 更新前のバージョンを見る (DOI: 10.5281/zenodo.22064165)
- 比較の種類、電源条件の記録、実行順序、指標の読み方、ETWで掘る流れなどを図でも追えるように、Mermaid図を17点追加しました(地の文500〜750字につき1図の規約に合わせたものです)。既存の電源2層の図にもキャプションを付け、図番号を通しで振りました。本文の文章は変えていません。
- 記事の冒頭に「この記事の知識マップ」節を追加しました。本文で扱っている概念とその関係を、要約・図・詳細ページへのリンクにまとめたものです。本文の主張は変えていません。
- 外部レビュー(1283件)への対応として本文を更新しました。個々の変更内容は、この下の履歴を参照してください。
- 比喩や口語で書かれていた6か所を技術的な記述に置き換えました。電源モードのオーバーレイと電源プランの2層構造を図にし、公開されている`powercfg`のオプションにオーバーレイの切り替えが無いことを明記しました。用語表、測定を何回まわすかの目安、固定する・実行する・記録する・解釈するのチェックリストを追加しました。
- 初版公開
この記事を引用する(DOI: 10.5281/zenodo.21589656)
この記事はZenodoにアーカイブされています。常に最新版へ解決されるDOIと、いま表示している版に固定されたDOIの両方を下に示します。
小村 豪(2026)「Windowsでプログラムのバージョン別速度を正しく比較する方法」合同会社小村ソフト. https://doi.org/10.5281/zenodo.21589656 https://comcomponent.com/blog/2026/03/16/002-windows-benchmark-comparing-program-versions/
- DOI(最新版)
- 10.5281/zenodo.21589656
- DOI(この版)
- 10.5281/zenodo.22093767
Windows でプログラムのバージョン A と B を比較したい。 そのとき一番やってはいけないのは、同じマシンで 1 回ずつ実行して「B のほうが 8% 速いっぽい」と言ってしまうことです。
その 8% は、本当にコード差かもしれません。 でも実際には、Power mode(電源モード)、Power plan(電源プラン)、熱、バックグラウンド更新、検索インデックス、ウイルススキャン、アフィニティ、実行順序、キャッシュ状態のどれかだった、というのが Windows ベンチマークでよくある話です。条件を 1 つずつ潰していく地味な作業になります。
flowchart TB
accTitle: 「8%速いっぽい」の正体
accDescr: 1回ずつ実行して出た差はコード差かもしれないが、電源や熱、バックグラウンドやキャッシュのどれかだったということがよくあり、条件を1つずつ潰していく作業が必要になることを示す図。
one1["1 回ずつ実行して比べる"] --> dif2["8% 速いっぽい、が出る"]
dif2 -->|"かもしれない"| code1["本当にコード差"]
dif2 -->|"よくある正体"| env1["電源・熱・ノイズ・キャッシュ"]
env1 --> crush1["条件を 1 つずつ潰していく"]
図1: 1回きりの差はコード差とは限らず、条件を潰してからでないと言い切れない。
この記事では、Windows 上で異なるバージョンのプログラムの実行速度を、できるだけコード差に近い形で比較する方法をまとめます。
対象は主に Windows 11 を想定していますが、powercfg や start などの大半は Windows 10 でも同様に使えます。
先に押さえておく用語
本文には英語のまま出てくる用語があります。初出でつまずかないよう、先にまとめておきます。
| 用語 | 意味 |
|---|---|
| ETW | Event Tracing for Windows。Windows に標準で入っているトレース基盤です。OS やドライバー、アプリが出すイベントをまとめて記録できます |
| WPR / WPA | Windows Performance Recorder と Windows Performance Analyzer。ETW のトレースを記録するツールと、それを開いて分析するツールで、どちらも Windows ADK に含まれます |
| clean boot | Microsoft 以外のサービスとスタートアップ アプリを止めて、最小構成で起動する手順です。常駐アプリのノイズを減らす目的で使います |
| PGO | Profile-Guided Optimization。一度実行して集めた分岐や呼び出しの統計を、次のビルドの最適化判断に使う仕組みです。ビルド条件が変わるので、比較対象がそろっているかの確認項目になります |
| p95 / p99 | パーセンタイル。全 run を速い順に並べたとき、下から 95% / 99% の位置にある値です。「20 回に 1 回はこれより遅い」が p95 にあたります |
| NUMA | Non-Uniform Memory Access。CPU から見てメモリまでの距離が均一でない構成です。どのノードで実行されるかで、メモリアクセスの速度が変わります |
| コア駐車(core parking) | 負荷が低いときに、使わない論理プロセッサを寝かせておく電力管理の仕組みです |
まず結論
再現性を上げるコツは、突き詰めると次の 6 つです。
-
「何を比較したいか」を先に決める コード差を見たいのか、実ユーザー体験を見たいのかで、揃えるべき環境が変わります。
-
Power mode(電源モード)と Power plan(電源プラン)を別物として記録する Windows ではここを雑に扱うと、比較が OS の省電力方針比較になりがちです。
-
冷えた 1 回目と、温まった後の定常状態を分ける 初回だけ速い・後半だけ遅い、は珍しくありません。
-
A→B→A→B のように交互に回す A を先に全部回してから B を回すと、熱やバックグラウンド状態の偏りを食らいます。
-
平均だけでなく中央値とばらつきを見る 外れ値が1つあるだけで、全体像が大きく歪みます。平均は思ったより脆いです。
-
差が小さいなら ETW / WPR で原因まで掘る 体感だけで議論すると、どちらの主張も裏づけがないまま平行線になります。
この記事の知識マップ
この記事は、Windows上でプログラムのバージョン間の速度を比較する際の再現性をテーマに、Power mode(オーバーレイ)とPower planという2層の電源設定を固定・記録することが前提になることを示す。Power modeの違いはコア駐車などPPMの挙動を左右し、Modern Standby対応デバイスではPower planの選択肢自体がBalanced系に制限される。検索インデックスやDefenderのスキャン、通知といったバックグラウンド処理は測定値のブレの原因になり得るため、除外設定やクリーンブート、通知の抑制で軽減する。Wall-clock time・CPU time・Cycle countをQueryPerformanceCounter・GetProcessTimes・QueryProcessCycleTimeでそれぞれ計測し、差が小さく理由が読めない場合はWPRでETWトレースを取りWPAで比較することが推奨される。
flowchart LR
accTitle: Windowsベンチマーク比較の再現性の知識マップ
accDescr: Power modeとPower planという2層の電源設定を固定・記録することがベンチマークの再現性の前提になること、検索インデックスやDefenderスキャンなどのバックグラウンド処理が測定値のブレを生み除外設定やクリーンブートで軽減できること、Wall-clock time・CPU time・Cycle countをそれぞれ専用APIで計測すること、差が小さいときにWPRとWPAで原因を掘り下げる流れを示す図
benchmark_reproducibility["ベンチマークの再現性"]
power_mode["電源モード/電源プラン(PPM)"]
power_plan["Power plan(電源プラン)"]
powercfg["powercfg"]
modern_standby["Modern Standby対応デバイス"]
core_parking["Core Parking"]
search_indexing["検索インデックス作成"]
benchmark_result_variance["ベンチマーク結果のばらつき(ブレ)"]
exclusion_setting["除外設定(フォルダー除外)"]
clean_boot["クリーンブート(clean boot)"]
do_not_disturb["Do not disturb(通知の抑制)"]
wpr["Windows Performance Recorder(WPR)"]
etw["ETW(Event Tracing for Windows)"]
wpa["Windows Performance Analyzer(WPA)"]
inconclusive_benchmark_result["差が小さい・理由が読めないベンチマーク結果"]
wall_clock_time["Wall-clock time(実時間)"]
queryperformancecounter["QueryPerformanceCounter(QPC)"]
cpu_time["CPU time(ユーザー+カーネル時間)"]
getprocesstimes["GetProcessTimes"]
cycle_count["Cycle count(CPUサイクル数)"]
queryprocesscycletime["QueryProcessCycleTime"]
process_priority["優先度クラス(priority class)"]
start_command["startコマンド"]
processor_affinity["プロセッサアフィニティ(affinity mask)"]
processor_groups["プロセッサグループ(Processor Groups)"]
benchmark_reproducibility -->|"前提とする"| power_mode
benchmark_reproducibility -->|"前提とする"| power_plan
power_plan -->|"で構成できる"| powercfg
modern_standby -.->|"両立しない"| power_plan
core_parking -.->|"で構成できる"| power_mode
search_indexing -.->|"原因になり得る"| benchmark_result_variance
exclusion_setting -.->|"軽減する"| benchmark_result_variance
clean_boot -->|"軽減する"| benchmark_result_variance
do_not_disturb -.->|"軽減する"| benchmark_result_variance
wpr -->|"利用する"| etw
wpa -->|"利用する"| etw
wpa -.->|"前提とする"| wpr
wpr -->|"推奨される対応"| inconclusive_benchmark_result
wall_clock_time -->|"で確認できる"| queryperformancecounter
cpu_time -->|"で確認できる"| getprocesstimes
cycle_count -->|"で確認できる"| queryprocesscycletime
process_priority -->|"で構成できる"| start_command
processor_affinity -->|"で構成できる"| start_command
processor_affinity -.->|"前提とする"| processor_groups
benchmark_reproducibility -.->|"利用する"| wall_clock_time
benchmark_reproducibility -.->|"利用する"| cpu_time
benchmark_reproducibility -.->|"利用する"| cycle_count
benchmark_result_variance -->|"両立しない"| benchmark_reproducibility
benchmark_reproducibility -.->|"原因になり得る"| inconclusive_benchmark_result
benchmark_reproducibility -.->|"利用する"| start_command
図の実線は常に成り立つ関係、破線は条件付きの関係です(成立条件は詳細ページの各関係の説明に記載)。関係すべての一覧(全25件、根拠・確度つき)と主要概念の定義は知識マップ詳細ページにまとめています。データ: JSON-LD / Turtle
何を比較したいのかを最初に決める
「速度比較」と一口に言っても、実は 2 種類あります。
1. コード差を見たい比較
アルゴリズム変更、データ構造変更、コンパイラ最適化、ランタイム更新などによって、実装そのものが速くなったかを知りたい比較です。
この場合は、環境ノイズをできるだけ削ります。 ベンチ専用セッション、Power mode(電源モード)固定、通知停止、検索インデックスや同期の抑制、必要なら clean boot までやります。
2. 実ユーザー体験を見たい比較
配布後にユーザーが普段の Windows 上で体感する速さを知りたい比較です。
この場合は、現実に存在するノイズを全部消してはいけません。 OneDrive 同期、Defender、通知、通常の電源設定などを含んだ「それっぽい日常環境」で比較したほうが、現実に近い結果になります。
この 2 つを混ぜると、結論がねじれます。 「ラボでは 12% 速いのに、現実では誤差」「現実では速いのに、CPU 時間では変わらない」みたいなことが普通に起きます。
flowchart TB
accTitle: 2種類の比較を混ぜない
accDescr: コード差を見たい比較では環境ノイズをできるだけ削り、実ユーザー体験を見たい比較では現実のノイズを残した日常環境で測るべきで、2つを混ぜると結論がねじれることを示す図。
q5["何を比較したいか"] -->|"コード差"| lab1["ノイズを削ったラボ環境"]
q5 -->|"実ユーザー体験"| real1["ノイズを残した日常環境"]
q5 -.->|"混ぜると"| twist2["結論がねじれる"]
図2: 目的で揃えるべき環境が逆になるので、最初にどちらの比較かを決める。
Windows で結果がぶれる主因
まず、何が結果をぶらすのかを雑に一覧化しておきます。
| 層 | ぶれ要因 | 典型例 |
|---|---|---|
| ハードウェア | CPU / GPU、メモリ、SSD、冷却 | ノート PC の薄さ、冷却台の有無 |
| ファームウェア | BIOS / UEFI、OEM 制御 | 省電力ポリシー、ファン制御 |
| OS | Windows build、ドライバ、更新状態 | 同じ PC でも更新後に挙動が変わる |
| 電源 | AC / DC、Power mode(電源モード)、Power plan(電源プラン) | バッテリー駆動だと別世界 |
| 熱 | 室温、ファン、直前の負荷 | 1 回目だけターボ、後半で失速 |
| バックグラウンド | Update、Defender、同期、通知 | 実行中にスキャンや同期が走る |
| スケジューリング | 優先度、アフィニティ、NUMA | マシン次第で CPU 配置が変わる |
| データ / キャッシュ | OS キャッシュ、アプリキャッシュ | 初回だけ遅い、2 回目以降だけ速い |
| ビルド条件 | Debug / Release、PGO、ログ有無 | そもそも別物を比べている |
要するに、「同じ Windows マシン」でも、条件が揃っていなければ別の実験です。
flowchart TB
accTitle: 条件が揃わなければ別の実験
accDescr: 同じWindowsマシンで測っていても、ハードから電源、熱、バックグラウンド、ビルド条件までの多層の条件が揃っていなければ実質別の実験であり、条件を固定して初めて比較になることを示す図。
same1["同じ Windows マシンで測る"] -.->|"条件が揃っていない"| oth1["実質、別の実験"]
same1 -->|"多層の条件を固定・記録"| cmp1["初めて比較になる"]
図3: マシンが同じでも、層をまたぐ条件が揃っていなければ比較として成立しない。
Power mode(電源モード)と Power plan(電源プラン)は分けて考える
ここはかなり重要です。
Windows には、設定アプリの Power mode(電源モード) と、従来の Power plan(電源プラン)(powercfg で見える電源プラン)があります。
見た目が似ているので一緒くたにされがちですが、雑に扱うと比較条件が曖昧になり、結果の再現性が失われます。
Windows の設定アプリでは、Settings > System > Power & battery から Power mode を選べます。
Microsoft のドキュメントでは、Plugged in / On Battery ごとに Best power efficiency、Balanced、Best performance を切り替えられるとされています。さらに、Power mode は変わると背後の電源関連設定や PPM(Processor Power Management)の挙動にも影響します。つまり、ここが違うだけでコア駐車や性能スケーリングの方針が変わり得ます。
一方、Power plan は Balanced、High performance などの伝統的な電源プランです。
powercfg /list や powercfg /getactivescheme で確認できます。
ここでややこしいのは、Windows には Power mode(電源モード)のオーバーレイとPower plan(電源プラン)の両方があることです。 関係を図にすると、こうなります。
flowchart TB
subgraph upper["上の層: Power mode - オーバーレイ"]
direction LR
M1["Best power efficiency"]
M2["Balanced"]
M3["Best performance"]
end
subgraph lower["下の層: Power plan - 電源プラン"]
direction LR
P1["Balanced"]
P2["High performance"]
P3["custom plan"]
end
UI["設定アプリ<br/>Power and battery の Power mode"] --> upper
CLI["powercfg /setactive で切り替え"] --> lower
upper --> PPM["実際に効く電源設定<br/>PPM とグラフィックスのサブグループ"]
lower --> PPM
AC["AC 給電かバッテリーか"] --> PPM
PPM --> RESULT["周波数の上限 / コア駐車 / 性能スケーリング"]
図4: Power mode(オーバーレイ)とPower planの2層とAC/DCが合わさって、実際に効く電源設定が決まる。
上の層だけ、下の層だけを見ても実際の挙動は決まりません。だからベンチ結果には、最低でも次を記録してください。
- AC かバッテリーか
- Power mode が何か
- Active power plan が何か
この 3 つを書いていないベンチ結果は、後から見返したときに条件を復元できません。
flowchart TB
accTitle: 最低限記録する3点
accDescr: ACかバッテリーか、Power modeが何か、Active power planが何かの3点を結果に記録しておかないと、後から見返したときに条件を復元できないことを示す図。
r1["AC かバッテリーか"] --> rec2["結果と一緒に記録する"]
r2["Power mode"] --> rec2
r3["Active power plan"] --> rec2
rec2 --> rst1["後から条件を復元できる"]
図5: 電源まわりの3点は、書いていなければ結果ごと再現不能になる最低限の記録。
まず固定すべき電源条件
-
ノート PC は必ず AC 接続で比較する バッテリー運用は、意図しない制限が入りやすいです。
-
Power mode を固定する ベンチ用途なら、まず
Best performanceを試します。 -
Active power plan を記録する
powercfgで現在値を残します。
powercfg /list
powercfg /getactivescheme
powercfg /list の出力は、Microsoft のドキュメントでは次の形で示されています。アクティブなプランの行末に * が付きます。日本語環境では、見出しとプラン名が日本語で出ます。
Existing Power Schemes (* Active)
-----------------------------------
Power Scheme GUID: {guidPlan1} (Balanced) *
Power Scheme GUID: {guidPlan2} (Power saver)
ここで出た GUID を、そのまま結果ファイルの power_plan 欄へ書き写します。名前ではなく GUID を残すのがポイントです。同じ「バランス」でも、複製やカスタマイズをした別プランのことがあるからです。
- 必要なら High performance に切り替える
# Balanced
powercfg /setactive 381b4222-f694-41f0-9685-ff5bb260df2e
# High performance
powercfg /setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
Power mode はコマンドで切り替えられるのか
ここは詰まりやすいところです。powercfg の公開されているコマンドライン オプション一覧に、Power mode(オーバーレイ)そのものを選び直すオプションはありません。切り替えは設定アプリの Settings > System > Power & battery から行うのが正規の手順です。
一方で、powercfg はオーバーレイ スキームの 設定値の読み書き には対応しています。ドキュメントには次の記述があります。
powercfg /qにオーバーレイのエイリアスとサブグループを渡すと、オーバーレイ側の設定を読めるpowercfg /setacvalueindexと/setdcvalueindexはオーバーレイ スキームにも使える- スキームを指定しなかった場合は、現在アクティブなオーバーレイ(オーバーレイが無ければ現在の電源プラン)が対象になる
- エイリアスの一覧は
powercfg /aliasesで確認できる
つまりコマンドでできるのは「いま効いているオーバーレイの中身を読む・調整する」ことで、「どのオーバーレイを選ぶか」を変えることではありません。ベンチの再現手順としては、設定アプリで Power mode を人手で固定し、その値を結果に書き残すのが現実的です。手順書には「Power mode = Best performance に設定した」と明記し、実行のたびに画面で確認します。
flowchart TB
accTitle: powercfgでできることとできないこと
accDescr: powercfgはいま効いているオーバーレイの設定値の読み書きには対応しているが、どのオーバーレイを選ぶかを変えるオプションは無いため、Power modeは設定アプリで人手で固定して値を書き残すのが現実的であることを示す図。
pc1["powercfg"] -->|"できる"| rw1["オーバーレイの中身を読む・調整する"]
pc1 -.->|"できない"| sel1["どのオーバーレイを選ぶかの変更"]
sel1 --> hand1["設定アプリで人手で固定し記録"]
図6: オーバーレイの選択はコマンドでは変えられないので、人手で固定して記録する。
「High performance が出ない」は普通にある
ここもハマりどころです。 Microsoft のドキュメントでは、Modern Standby 対応デバイスでは Balanced、または Balanced から派生したプランしか許可されないとされています。 なので、「High performance が見当たらない、壊れた?」ではなく、その機種の設計上そうなっている可能性があります。
また、Microsoft は「Power mode が変更できない場合、custom power plan が選ばれているかもしれないので、まず Balanced を選んでみる」と案内しています。Power mode の UI が動かないときは、ここを疑うのが早いです。
flowchart TB
accTitle: High performanceが出ないときの見方
accDescr: Modern Standby対応デバイスではBalancedかその派生プランしか許可されないためHigh performanceが出ないのは設計であり、Power modeのUIが変更できないときはcustom planが選ばれている可能性をまず疑うことを示す図。
nohp1["High performance が見当たらない"] --> ms1["Modern Standby 対応なら設計どおり"]
nomv1["Power mode の UI が動かない"] --> cst1["custom plan の可能性を疑う"]
cst1 --> bl1["まず Balanced を選んでみる"]
図7: プランが出ない・UIが動かないのは故障とは限らず、機種の設計とプラン選択をまず見る。
バックグラウンドノイズを潰す
Windows は、こちらが静かに測りたいときでも、裏で更新やインデックス作成やスキャンを動かします。まずはその量を減らします。
まずは再起動して、落ち着くまで待つ
設定変更後は一度再起動し、ログイン後すぐには走らせず、数分待ちます。 起動直後は、更新、インデックス、同期、Defender、各種常駐がまだ暴れています。
flowchart TB
accTitle: 再起動して落ち着くまで待つ
accDescr: 設定変更後は一度再起動し、起動直後は更新やインデックス、同期、Defenderなどが動いているため、ログイン後すぐには走らせず数分待ってから測るという手順を示す図。
chg1["設定を変更する"] --> rb1["一度再起動する"]
rb1 --> wt1["ログイン後、数分待つ"]
wt1 --> ms2["それから測り始める"]
rb1 -.-> nzz1["起動直後は常駐がまだ暴れている"]
図8: 測り始めるのは、再起動後に裏の活動が落ち着いてから。
厳密な比較なら clean boot を使う
Microsoft は、clean boot によって最小限のスタートアップ構成にできる手順を案内しています。
msconfig で Microsoft 以外のサービスを止め、Task Manager で Startup apps を無効化する方法です。
これはノイズを減らすには強力です。 ただし、日常利用環境とは離れるので、「コード差を見るためのラボ比較」で使うのが向いています。
通知を黙らせる
Windows の通知バナーは、軽く見えて意外と邪魔です。 視覚的に邪魔なだけでなく、実行タイミングやフォーカス、裏のアプリ活動を変えることがあります。
Do not disturb を手動で有効にするか、少なくともベンチ中は通知を切ります。
検索インデックスと同期を抑える
ベンチ対象が大量ファイルを読む、生成物を大量に書く、ソースツリーを何度も作り直す、というタイプなら、検索インデックスやクラウド同期が地味に刺さります。
- ベンチ用のディレクトリを検索対象から外す
- OneDrive / Dropbox / Google Drive などの同期を止める
- ブラウザ、Teams、Discord、Slack を閉じる
このへんは派手さはないですが、効くときはかなり効きます。
flowchart TB
accTitle: ファイルを多く触るベンチに刺さるノイズ
accDescr: 大量のファイルを読み書きするタイプのベンチでは、検索インデックスとクラウド同期と常駐アプリが測定に刺さるため、除外設定や停止でノイズを減らすことを示す図。
ix1["検索インデックス"] --> hit1["ファイルを多く触るベンチに刺さる"]
sy1["クラウド同期"] --> hit1
ap2["常駐アプリ"] --> hit1
hit1 --> cutn1["除外・停止でノイズを減らす"]
図9: ファイルを大量に読み書きするベンチほど、インデックスと同期の停止が効く。
熱を揃えない比較は、だいたい熱を比べている
CPU や GPU は、冷えているときと温まった後で動作クロックが変わります。同じコードでも、実行のたびに条件が変わるということです。 特にノート PC、薄型ミニ PC、小型デスクトップは顕著です。
flowchart TB
accTitle: 熱で条件が変わる仕組み
accDescr: CPUやGPUは冷えているときと温まった後で動作クロックが変わるため、同じコードでも実行のたびに条件が変わり、熱を揃えない比較はだいたい熱を比べていることになるという仕組みを示す図。
cold1["冷えている状態で実行"] --> hot1["温まるとクロックが変わる"]
hot1 --> vary1["実行のたびに条件が変わる"]
vary1 -.-> heatc1["揃えない比較は熱を比べている"]
図10: クロックは熱で動くので、熱条件を揃えないとコードではなく冷却を比較してしまう。
守るべきルール
- 室温をできるだけ揃える
- ノート PC の置き方を固定する
- AC アダプタ、ドック、外部ディスプレイ構成を固定する
- ベンチの前に重い作業をしない
- 初回実行と定常状態を分けて測る
実行順序は交互にする
A を 10 回やってから B を 10 回、は避けます。 熱、キャッシュ、バックグラウンド活動の偏りが載るからです。
おすすめは次のどれかです。
A B A B A B ...A B B A A B B A ...- ランダム順序を事前生成して、その順に回す
flowchart TB
accTitle: 実行順序で偏りの載り方が変わる
accDescr: Aを先に全部回してからBを回すと熱やキャッシュ、バックグラウンド活動の偏りが片方だけに載るため、交互またはランダム順で回して偏りを両方に分散させることを示す図。
seq1["A を全部 → B を全部"] --> bias1["偏りが片方だけに載る"]
alt1["A B A B と交互・ランダム順"] --> even1["偏りが両方に分散する"]
even1 --> fair1["順序の影響を差から取り除ける"]
図11: 順序をまとめると偏りごと比べてしまうので、交互かランダムで回す。
何を測るかで「速い」の意味は変わる
「速い」を 1 個の数に押し込むと、だいたい事故ります。 Windows で見るべき代表的な指標は次の 3 つです。
1. Wall-clock time(実時間)
ユーザーが待つ時間です。 エンドツーエンドの体感に一番近いので、まず最初に見る値はこれです。
Windows では QueryPerformanceCounter (QPC) が高分解能の時刻取得に使えます。
managed code なら Stopwatch 系を使うのが基本です。
DateTime.Now でミリ秒を眺めるのは、さすがにちょっと無防備です。
2. CPU time(ユーザー + カーネル時間)
GetProcessTimes で取得できる、プロセスが実際に CPU を使った時間です。
これは計算効率を見るのに便利です。 たとえば wall-clock では速くなったのに CPU time が変わらないなら、キャッシュ、I/O、待ち時間、スケジューリングが効いている可能性があります。
3. Cycle count(CPU サイクル数)
QueryProcessCycleTime で、プロセス全体の CPU サイクル数を取れます。
これも CPU work を見る指標ですが、wall-clock とは別の面を見せてくれます。 特に「待ち時間は同じだが、計算部分は軽くなっているのか」を見たいときに便利です。
flowchart TB
accTitle: 3つの指標が見る面の違い
accDescr: wall-clock timeはユーザーが待つ時間、CPU timeはプロセスが実際にCPUを使った時間、cycle countはCPUサイクル数という別の面を見せ、組み合わせて初めて速さの中身が読めることを示す図。
spd1["「速い」の中身を見る"] --> w1["wall-clock: 待つ時間"]
spd1 --> u1["CPU time: 使った CPU 時間"]
spd1 --> cy1["cycle: 計算部分の重さ"]
w1 -.-> mixr1["組み合わせで理由を推測する"]
図12: 1つの数字に押し込まず、3指標の組み合わせで速さの意味を読む。
priority、affinity、NUMA は最後の手段
このあたりは効くことがあります。 でも、効くからといって最初から触ると、別の現象を作りやすいです。
まずは普通に測る
デフォルト状態で差が出るなら、その差自体に価値があります。
いきなり /high や /affinity を入れると、「実際の Windows では起きない条件」を持ち込むことになります。
flowchart TB
accTitle: priorityとaffinityは最後の手段
accDescr: まずデフォルト状態で測り、差が出るならその差自体に価値があり、いきなり優先度やアフィニティを固定すると実際のWindowsでは起きない条件を持ち込むことになるため、使うなら目的を明確にして最後に使うことを示す図。
def1["まずデフォルト状態で測る"] --> val1["差が出ればその差に価値がある"]
early1["いきなり /high や /affinity"] -.-> art1["現実には起きない条件を持ち込む"]
val1 -->|"必要になったら目的を決めて"| lastr1["最後の手段として固定する"]
図13: 優先度とアフィニティは、デフォルトでの測定を済ませたあとに目的を持って使う。
使うなら、目的を明確にする
- /high: 他のプロセスの邪魔を受けにくくしたい
- /affinity: CPU 配置を固定して比較したい
- NUMA 制御: 大規模マシンでメモリ局所性まで揃えたい
Windows の start コマンドは、priority class や affinity mask を付けて起動できます。
start "" /high /wait myapp.exe --bench case1.json
start "" /affinity F /high /wait myapp.exe --bench case1.json
ただし /realtime はやめる
/realtime は使えますが、使わないほうがいいです。
ノイズ除去ではなく、別の事故を作る方向に働きがちです。
測定手順のおすすめ
ここまでを踏まえた、実運用しやすい手順をまとめます。
ラボ寄りの比較手順
- 比較対象を固定する
- commit hash / build number
- compiler / runtime version
- Debug / Release
- ログ、assert、トレース有無
- マシン条件を固定する
- Windows build
- BIOS / UEFI version
- driver version
- AC 接続
- 室温、設置方法
- 電源条件を固定する
- Power mode を決める
- Active power plan を記録する
- 再起動する
- ベンチ前に数分待つ
- 必要なら clean boot
- warm-up を入れる
- A / B を交互に回す
- 回数を確保する
- 中央値・最小・最大・p95 を残す
- raw data を保存する
- 差が小さければ ETW / WPR を取る
何回まわすか
9 番の「回数を確保する」の目安も決めておきます。以下は統計的な厳密解ではなく、実務での落としどころです。
| 見たいもの | 1 バージョンあたりの実行回数の目安 |
|---|---|
| 中央値だけ見て、大きめの差(1 割以上)を確認したい | 10 回 |
| 数 % の差を主張したい。ばらつきも見たい | 30 回 |
| p95 まで読みたい | 30 回以上。20 回では p95 が上位 1〜2 個の値そのものになり、外れ値の影響を直接受けます |
所要時間は 1 回の実行時間 × 回数 × バージョン数 + warm-up で見積もれます。1 回 30 秒の処理を A / B それぞれ 30 回なら、warm-up を入れて 35 分前後という計算です。これが現実的でないときは、回数を削るより 測る対象を小さく切る(重い工程だけを切り出す)ほうが筋がよいです。
止めどころに迷うなら、回数を増やしながら中央値の推移を見て、増やしても動かなくなったところで止めるのが分かりやすいやり方です。
flowchart TB
accTitle: 回数の止めどころ
accDescr: 回数を増やしながら中央値の推移を見て、増やしても中央値が動かなくなったところで止めるという、実行回数の実務的な決め方を示す図。
add2["回数を増やして回す"] --> mdz1["中央値の推移を見る"]
mdz1 -->|"まだ動く"| add2
mdz1 -->|"増やしても動かない"| stop1["そこで止める"]
図14: 回数は先に固定しきれなくても、中央値が落ち着いた点を止めどころにできる。
記録しておくと後で助かる項目
ベンチの CSV や JSON には、少なくとも次を残しておくと強いです。
timestamp,version,scenario,elapsed_ms,user_ms,kernel_ms,cycles,power_mode,power_plan,ac_or_dc,room_temp_c,notes
可能なら、さらに次もあると便利です。
cpu_package_temp_start_c,cpu_package_temp_end_c,affinity_mask,priority_class,windows_build,driver_version
ベンチは、測ることより後で解釈できることのほうが大事だったりします。
平均だけでなく、中央値と分布を見る
平均は便利ですが、Windows ベンチでは簡単に壊れます。 1 回だけ Defender が入った、通知が出た、別プロセスが SSD を叩いた、というだけで平均が持っていかれます。
flowchart TB
accTitle: 平均は外れ値に持っていかれる
accDescr: 1回だけDefenderのスキャンや通知や別プロセスのI/Oが入るだけで平均は持っていかれるため、中央値を軸にp95やp99、min/maxを組み合わせて分布で見ることを示す図。
once3["1 回だけノイズが入る"] --> avg1["平均が持っていかれる"]
med2["中央値を軸にする"] --> dist1["p95 / p99 と min / max も見る"]
dist1 --> robust1["外れ値に強い読み方になる"]
図15: 平均は1回のノイズで壊れるので、中央値と分布の組み合わせで読む。
おすすめはこの組み合わせです。
- 中央値: まずこれを見る
- p95 / p99: tail が悪化していないかを見る
- min / max: 外れ方を見る
- 箱ひげ図や散布図: 差が小さいときに役立つ
差が出たときの読み方
結果の解釈は、組み合わせで見ると分かりやすいです。
wall-clock だけ速い
I/O、待ち時間、キャッシュ、スケジューリングの改善かもしれません。
CPU time も cycle も下がっている
実装そのものが軽くなっている可能性が高いです。
1 回目だけ遅い / 速い
cold / warm の差です。起動・初期化・キャッシュ生成・JIT を疑います。
回を重ねるほど遅くなる
熱、スロットリング、メモリ圧迫、バックグラウンド活動を疑います。
flowchart TB
accTitle: 差のパターンから原因を読む
accDescr: wall-clockだけ速いなら待ちやI/O系、CPU timeもcycleも下がるなら実装が軽い、1回目だけ違うならcoldとwarmの差、回を重ねて遅くなるなら熱やバックグラウンドを疑うという読み方を示す図。
pt2["差の出かたを見る"] -->|"実時間だけ"| c1a["待ち・I/O 系"]
pt2 -->|"CPU 時間も減"| c2a["実装が軽い"]
pt2 -->|"初回だけ"| c3a["cold / warm"]
pt2 -->|"後半で遅い"| c4a["熱・裏の活動"]
図16: 差そのものより差の出かたのパターンが、原因の当たりを教えてくれる。
ETW / WPR で「なぜ速いか」まで掘る
差が小さい、あるいは理由が読めないときは、Windows の ETW(Event Tracing for Windows)系ツールに進むのが王道です。
Microsoft の Windows Performance Recorder (WPR) は ETW ベースの記録ツールで、Windows ADK に含まれています。
CPU、I/O、context switch、ページフォールトなどをまとめて取れます。
最低限ならこんな感じです。
wpr -start CPU -filemode
REM ここでベンチを実行する
wpr -stop trace.etl
WPA で開いたあと、まず見るグラフはだいたい決まっています。
| 見たいこと | 開くグラフ | 読み方 |
|---|---|---|
| どの関数で CPU を使っているか | CPU Usage (Sampled) | Weight で並べ替え、A と B でスタックを比べる。サンプリングなので、DPC / ISR のような短い処理は写りにくい |
| なぜ待っているのか | CPU Usage (Precise) | Ready 時間、待ち時間、コンテキストスイッチの理由を見る。lock 待ちや I/O 待ちの差はここに出る |
| ドライバー起因で詰まっていないか | DPC/ISR | モジュール別の時間を見る。ここが大きいなら、そもそもアプリ側の差ではない |
| ディスクが効いているか | Disk Usage | I/O の回数とサイズ、サービス時間を見る |
比較のときは、同じシナリオで A と B のトレースを 1 本ずつ取り、同じグラフを並べて見るのが基本です。1 本だけ見ても「これは遅いのか」を判断できません。
この段階まで来ると、 「B のほうが 3% 速い」ではなく、 「B は lock 待ちが減って ready time が下がっている」 「A は file open が増えて cold start が遅い」 のように、理由付きで話せるようになります。
flowchart TB
accTitle: 数字だけの差から理由付きの差へ
accDescr: 差が小さい・理由が読めないときは、WPRで同じシナリオのトレースをAとBで1本ずつ取り、WPAで同じグラフを並べて比べることで、何%速いではなく理由付きで話せるようになる流れを示す図。
small2["差が小さい・理由が読めない"] --> tr1["WPR で A と B のトレースを取る"]
tr1 --> cmp2["WPA で同じグラフを並べて見る"]
cmp2 --> rsn1["理由付きで話せるようになる"]
cmp2 -.-> onen1["1 本だけでは遅いか判断できない"]
図17: ETWまで掘ると、「何%速い」が「なぜ速い」に変わる。
1 枚でまとめたチェックリスト
最後に、そのまま手順書へ貼れる形にしておきます。
固定する
- 比較対象を固定した(commit hash / build number / Debug か Release / PGO などのビルド条件 / ログや assert の有無)
- ノート PC は AC 接続にした
- Power mode を設定アプリで固定した
- Active power plan を
powercfg /getactiveschemeで確認した - 通知を止めた。検索インデックスとクラウド同期を止めた
- 必要なら clean boot にした
- 再起動し、数分待ってから始めた
実行する
- warm-up を入れた
- cold(初回)と warm(定常)を分けて測った
- A / B を交互、またはランダム順で回した
- 回数を決めて回した(目安は上の表)
記録する
- 1 実行 1 行の raw data を残した(
elapsed_ms/user_ms/kernel_ms/cycles) - AC か DC か、Power mode(電源モード)、Power plan(電源プラン)の GUID、Windows build、driver version を残した
- 室温と設置状態を残した
- 固定しなかった条件も書き残した
解釈する
- 中央値を見た。平均だけで判断していない
- p95 / p99 で tail を見た
- min / max で外れ値を確認した
- wall-clock / CPU time / cycle の組み合わせで理由を推測した
- 差が小さいときは ETW / WPR まで掘った
まとめ
Windows でバージョン違いのプログラムを比較するときに、本当に効くのは派手な裏技ではありません。 大事なのは、次のような地味だけど再現性に効く作法です。
- AC / Power mode(電源モード) / Power plan(電源プラン)を固定して記録する
- cold と warm を分ける
- A / B を交互に回す
- 中央値と分布を見る
- 必要なら clean boot
- 差が小さければ ETW / WPR で理由まで掘る
そして一番大事なのは、何を固定して、何を固定しなかったかを結果と一緒に書くことです。 ベンチは速さの比較であると同時に、実験条件の記録でもあります。
条件の書かれていない高速化報告は、同じ結果を出せるかどうかを他人が確かめられません。数字だけが残り、再現の手立てが残らないからです。 逆に条件がきちんと書かれていれば、たとえ差が小さくても、その結果にはちゃんと価値があります。
flowchart TB
accTitle: 条件の記録が結果の価値を決める
accDescr: 条件の書かれていない高速化報告は数字だけが残って再現の手立てが残らず、条件がきちんと書かれていれば差が小さくても価値があるという、ベンチが実験条件の記録でもあることを示す図。
norec1["条件を書かない報告"] --> onlyn1["数字だけが残る"]
onlyn1 --> norep1["他人が確かめられない"]
rec3["条件を書いた報告"] --> rep1["再現の手立てが残る"]
rep1 --> worth1["差が小さくても価値がある"]
図18: ベンチの価値は数字よりも、何を固定して何を固定しなかったかの記録に宿る。
参考資料
- Microsoft Support: Change the power mode for your Windows PC
- Microsoft Learn: Power Policy Settings
- Microsoft Learn: Customize the Windows performance power slider
- Microsoft Learn: Powercfg command-line options
- Microsoft Support: How to perform a clean boot in Windows
- Microsoft Support: Notifications and Do Not Disturb in Windows
- Microsoft Support: Search indexing in Windows
- Microsoft Learn: Configure custom exclusions for Microsoft Defender Antivirus
- Microsoft Support: Device Security in the Windows Security App
- Microsoft Learn: QueryPerformanceCounter function
- Microsoft Learn: Acquiring high-resolution time stamps
- Microsoft Learn: GetProcessTimes function
- Microsoft Learn: QueryProcessCycleTime function
- Microsoft Learn: start command
- Microsoft Learn: SetPriorityClass function
- Microsoft Learn: SetProcessAffinityMask function
- Microsoft Learn: Processor Groups
- Microsoft Learn: Windows Performance Recorder
- Microsoft Learn: WPR Command-Line Options
- Microsoft Learn: CPU Analysis in Windows Performance Analyzer - どのグラフで何を見るか。
- Microsoft Learn: Set the Default Power Plan -
powercfg -LISTの出力例。
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
C#/C++/Java/Goの実行速度を公平に比較する方法
C#、C++、Java、Go の実行速度を公平に比較する方法を、測定設計、warm-up、環境固定、統計の見方、具体的なベンチ項目まで整理します。
WinRTはCOMである ── IInspectable・.winmd・言語プロジェクション、そしてWinUIが今もバイナリ契約の上に乗っている理由
WinRTはマネージドランタイムではなく、COMにメタデータ(.winmd)と言語プロジェクションを足したABIです。IUnknownとIInspectableの関係から、デスクトップアプリでのHWND初期化やpackage identityの詰まりどころまで解説します。
親が落ちたあとに何が残るか ── Job Objectで子プロセスを飼う
UIを強制終了してもSDKのヘルパーが残り、カメラやCOMポートを握ったままになるのはなぜか。Job Objectでプロセスツリーを一つの単位にし、KillOnJobCloseと完了ポートで子プロセスの寿命を設計する方法を計測アプリ目線で解説します。
OLEオブジェクトとは何か ── 埋め込み・リンクの仕組みと業務文書の落とし穴
WordにExcelの表を埋め込む機能の正体がOLEオブジェクトです。埋め込みとリンクの違い、複合ファイルと構造化ストレージ、In-Place Activationの仕組みから、リンク切れ・肥大化・セキュリティ対策まで実務目線で解説します。
「同じPC」は同じ実行環境ではない ── AppData・HKCU・DPAPI・資格情報を隔てるユーザー境界
手元で動くアプリがタスク化・サービス化した途端に壊れるのはなぜか。AppData・HKCU・DPAPI・ブラウザプロファイル・資格情報を隔てるWindowsのユーザー境界を仕組みから解説します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。
このテーマがつながるサービス
この記事は次のサービスページにつながります。近い入口からご覧ください。
技術相談・設計レビュー
性能比較の設計、計測条件の揃え方、ETW / WPR での掘り下げまで含めて、技術相談・設計レビューと相性がよいテーマです。
不具合調査・原因解析
バージョン間で速い・遅いの差が出たときに、電源条件、熱、バックグラウンドノイズ、実装差のどこが原因か切り分ける流れは、不具合調査・原因解析として進めやすいです。
よくある質問
この記事のテーマについて、相談時によくある質問をまとめています。
- Windowsでベンチマーク結果がぶれる主な原因は何ですか?
- Power mode(電源モード)やPower plan(電源プラン)、熱、バックグラウンド更新、検索インデックス、ウイルススキャン、優先度・アフィニティ、実行順序、キャッシュ状態など多層の要因があります。同じWindowsマシンでも、これらの条件が揃っていなければ実質的に別の実験になります。特にノートPCではAC接続かバッテリー駆動かで挙動が大きく変わるため、必ずAC接続で比較し、条件を記録することが重要です。
- Power mode(電源モード)とPower plan(電源プラン)は何が違いますか?
- Power modeは設定アプリのPower & batteryで選ぶBest power efficiency・Balanced・Best performanceの切り替えで、背後の電源関連設定やPPM(Processor Power Management)の挙動に影響します。Power planはpowercfgで確認できるBalancedやHigh performanceなどの伝統的な電源プランです。Windowsには両方が存在するため、ベンチ結果にはACかバッテリーか・Power mode・Active power planの3つを最低限記録しておく必要があります。
- High performanceの電源プランが表示されないのは故障ですか?
- 故障ではない可能性が高いです。Microsoftのドキュメントでは、Modern Standby対応デバイスではBalanced、またはBalancedから派生したプランしか許可されないとされています。つまりその機種の設計上High performanceが出ないことは普通にあります。またPower modeのUIが変更できない場合は、custom power planが選ばれている可能性があるため、まずBalancedを選んでみるのが早いです。
- バージョンAとBの速度比較はどんな順序で実行すべきですか?
- Aを先に全部回してからBを回すのは避けるべきです。熱・キャッシュ・バックグラウンド活動の偏りが片方だけに載るからです。A B A B と交互に回すか、事前に生成したランダム順序で実行します。また冷えた1回目と温まった後の定常状態を分けて測り、平均だけでなく中央値・p95・最小・最大を見ることで、外れ値に結果を持っていかれるのを防げます。