在 Windows 上正确比较不同版本程序运行速度的方法

· 更新日期: · · Windows, Benchmark, Performance, Profiling, Power Management

更新记录(2 条,最后更新 2026年09月03日)

本文的修改记录。已保存的更新前版本,可通过带有 DOI 的永久链接阅读。

本文此前是日文原文的节译,缺少大量章节、表格、Mermaid 图、图题、脚注与 FAQ。现已改写为日文原文的完整译文,技术主张与日文版一致,并补上了此前缺失的图与表,同时统一了全篇的术语译法。 查看更新前的版本 (DOI: 10.5281/zenodo.22276814)
补充了日文原文中已有的咨询引导(consultation_services)。正文内容没有改动。 查看更新前的版本 (DOI: 10.5281/zenodo.21615473)
首次发布
引用本文(DOI: 10.5281/zenodo.21615472)

本文保存于 Zenodo。以下同时提供始终指向最新版本的 DOI,以及固定于您正在阅读版本的 DOI。

小村 豪(2026)。《在 Windows 上正确比较不同版本程序运行速度的方法》。小村软件有限公司。https://doi.org/10.5281/zenodo.21615472 https://comcomponent.com/zh-CN/blog/2026/03/16/002-windows-benchmark-comparing-program-versions/

DOI(最新版本)
10.5281/zenodo.21615472
DOI(此版本)
10.5281/zenodo.22281989

想在 Windows 上比较程序的版本 A 与 B。 这时最不该做的,就是在同一台机器上各跑一次,然后说“看起来 B 快 8%”。

这 8% 也许真的来自代码差异。 但实际情况往往是,它其实来自 Power mode(电源模式)、Power plan(电源计划)、散热、后台更新、搜索索引、病毒扫描、亲和性、执行顺序、缓存状态 当中的某一项——这在 Windows 基准测试中是很常见的事。接下来要做的,是把这些条件一个一个排除掉的枯燥工作。

“看起来快 8%”的真实来源各跑一次得到的差异也许来自代码,但更常见的是来自电源、散热、后台或缓存中的某一项,需要把条件一个一个排除掉的示意图。也许是常见的真实来源各跑一次进行比较得出“看起来快 8%”真的来自代码差异电源、散热、噪声、缓存把条件一个一个排除掉

图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 条。

  1. 先决定“想比较什么” 想看的是代码差异,还是真实用户的体感,需要对齐的环境是不一样的。

  2. 把 Power mode(电源模式)与 Power plan(电源计划)当成两件事分别记录 在 Windows 上如果这里处理得马虎,比较很容易变成在比较 OS 的省电策略。

  3. 把冷机状态下的第一次与热机之后的稳定状态分开 只有第一次快、或者只有后半段慢,都不是罕见现象。

  4. 按 A→B→A→B 的方式交替执行 先把 A 全部跑完再跑 B,就会把散热与后台状态的偏差全吃进去。

  5. 不只看平均值,还要看中位数与离散程度 只要有一个离群值,整体图景就会被大幅扭曲。平均值比想象中脆弱。

  6. 差异较小时,用 ETW / WPR 深挖到原因为止 只凭体感争论,双方的主张都没有依据,只会各说各话。

图中实线表示始终成立的关系,虚线表示带条件的关系(成立条件写在详情页各关系的说明中)。关系的完整列表(共 25 条,附依据与确信度)以及主要概念的定义,汇总在知识地图详情页(日文)。数据:JSON-LD / Turtle

先决定你到底要比较什么

“速度比较”听起来只是一件事,其实分成两种。

1. 想看代码差异的比较

因为算法变更、数据结构变更、编译器优化、运行时更新等原因,想知道 实现本身是否变快 的比较。

这种情况下要尽量削减环境噪声。 使用基准测试专用的会话、固定 Power mode(电源模式)、关闭通知、抑制搜索索引与同步,必要时甚至做 clean boot。

2. 想看真实用户体感的比较

想知道发布之后,用户在日常的 Windows 环境中体感到的速度。

这种情况下 不能把现实中存在的噪声全部消除。 在包含 OneDrive 同步、Defender、通知、普通电源配置在内的“接近日常”的环境下比较,结果才会更贴近现实。

把这两者混在一起,结论就会拧巴。 “在实验室里快 12%,在现实中却只是误差”“现实中更快,但 CPU 时间没变”这类情况很常见。

不要把两种比较混在一起想看代码差异的比较要尽量削减环境噪声,想看真实用户体感的比较要保留现实噪声、在日常环境下测量,两者混在一起会让结论拧巴的示意图。代码差异真实用户体感混在一起想比较什么削减噪声的实验室环境保留噪声的日常环境结论会拧巴

图2:目的不同,该对齐的环境正好相反,所以要先决定做的是哪一种比较。

Windows 上导致结果波动的主要因素

先把会让结果晃动的因素粗略列一遍。

层面 波动因素 典型例子
硬件 CPU / GPU、内存、SSD、散热 笔记本机身薄、有没有散热支架
固件 BIOS / UEFI、OEM 控制 省电策略、风扇控制
OS Windows build、驱动程序、更新状态 同一台电脑更新之后行为就变了
电源 AC / DC、Power mode(电源模式)、Power plan(电源计划) 换成电池供电就是另一个世界
散热 室温、风扇、之前的负载 只有第一次跑到 turbo,后半段失速
后台 Update、Defender、同步、通知 运行过程中正好赶上扫描或同步
调度 优先级、亲和性、NUMA 机器不同,CPU 的分配也不同
数据 / 缓存 OS 缓存、应用缓存 只有第一次慢,第二次以后才快
构建条件 Debug / Release、PGO、有没有日志 本来比较的就是两个不同的东西

一句话,即使是“同一台 Windows 机器”,只要条件没有对齐,就是另一次实验。

条件不对齐就是另一次实验即使在同一台 Windows 机器上测量,只要从硬件到电源、散热、后台、构建条件这些多层条件没有对齐,实质上就是另一次实验,只有固定并记录条件才算得上比较的示意图。条件没有对齐固定并记录多层条件在同一台 Windows 机器上测量实质上是另一次实验这才算得上比较

图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(电源模式)这一层覆盖(overlay) 和 Power plan(电源计划)。 把两者的关系画成图,就是下面这样。

下层:Power plan - 电源计划BalancedHigh performancecustom plan上层:Power mode - 覆盖层Best power efficiencyBalancedBest performance设置应用Power and battery 中的 Power mode用 powercfg /setactive 切换实际生效的电源配置PPM 与图形的子组接 AC 供电还是电池频率上限 / 核心停放 / 性能调度

图4:Power mode(覆盖层)与 Power plan 这两层再加上 AC/DC,共同决定了实际生效的电源配置。

只看上层或只看下层,都决定不了实际行为。所以基准测试结果至少要记录下面这些内容。

  • 接的是 AC 还是电池
  • Power mode 是什么
  • Active power plan 是什么

没有写下这 3 项的基准测试结果,事后回看时无法还原当时的条件。

至少要记录的 3 项接的是 AC 还是电池、Power mode 是什么、Active power plan 是什么这 3 项,如果不随结果一起记录下来,事后回看时就无法还原条件的示意图。接的是 AC 还是电池与结果一起记录下来Power modeActive power plan事后可以还原条件

图5:电源相关的这 3 项是最低限度的记录,不写下来结果就整个无法复现。

首先应该固定的电源条件

  1. 笔记本电脑务必接 AC 电源再比较 靠电池运行时容易被加上意料之外的限制。

  2. 固定 Power mode 如果是做基准测试,可以先试 Best performance。

  3. 记录 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 而不是名称,因为同样叫“平衡”的,也可能是复制或自定义出来的另一个计划。

  1. 必要时切换到 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”,并在每次执行前在界面上确认一遍。

powercfg 能做什么、不能做什么powercfg 支持读写当前生效的覆盖层的配置值,但没有可以改变选择哪一个覆盖层的选项,因此现实的做法是在设置应用里人工固定 Power mode 并把值记录下来的示意图。能做到做不到powercfg读取并调整覆盖层的内容改变选择哪一个覆盖层在设置应用里人工固定并记录

图6:覆盖层的选择无法用命令改变,只能人工固定并记录下来。

“找不到 High performance”是很常见的情况

这里也是一个坑。 Microsoft 的文档说明,在支持 Modern Standby 的设备上,只允许使用 Balanced 或由 Balanced 派生出来的计划。 所以与其想“找不到 High performance,是不是坏了?”,更可能是 这台机型在设计上本来就是这样。

另外,Microsoft 还提示:“如果 Power mode 无法更改,可能是选中了 custom power plan,可以先试着选 Balanced。”当 Power mode 的界面动不了时,先怀疑这一点会更快。

找不到 High performance 时怎么看支持 Modern Standby 的设备只允许 Balanced 或其派生计划,所以看不到 High performance 属于设计使然;当 Power mode 的界面无法更改时,要先怀疑是否选中了 custom plan 的示意图。找不到 High performance支持 Modern Standby 就是设计如此Power mode 的界面动不了怀疑选中了 custom plan先试着选 Balanced

图7:计划不出现、界面动不了未必是故障,先看机型的设计和当前选中的计划。

压制后台噪声

即使我们想安静地测量,Windows 也会在后台跑更新、建索引、做扫描。首先要把这些活动的量降下来。

先重启,然后等它安静下来

修改配置之后先重启一次,登录后不要马上开跑,等上几分钟。 刚启动时,更新、索引、同步、Defender 以及各种常驻程序都还很活跃。

重启后等它安静下来修改配置后先重启一次,由于刚启动时更新、索引、同步、Defender 等还在运行,所以登录后不要马上开跑,等几分钟再开始测量的步骤示意图。修改配置重启一次登录后等几分钟然后再开始测量刚启动时常驻程序还很活跃

图8:要等重启后后台活动安静下来,再开始测量。

严格的比较就用 clean boot

Microsoft 提供了通过 clean boot 把启动配置降到最小的操作步骤。 做法是用 msconfig 停用 Microsoft 以外的服务,再用 Task Manager 停用 Startup apps。

这种方式在 降噪 上相当有效。 不过它离日常使用环境比较远,更适合用在“为了观察代码差异而做的实验室式比较”中。

让通知安静下来

Windows 的通知横幅看起来无关紧要,实际上相当碍事。 它不只是视觉上的干扰,还可能改变执行时机、焦点以及后台应用的活动。

手动开启 Do not disturb,或者至少在基准测试期间关闭通知。

抑制搜索索引与同步

如果基准测试对象属于会读取大量文件、写出大量生成物、反复重建源码树的类型,搜索索引与云同步就会在不显眼处产生影响。

  • 把基准测试用的目录排除在搜索范围之外
  • 停止 OneDrive / Dropbox / Google Drive 之类的同步
  • 关闭浏览器、Teams、Discord、Slack

这些操作看起来不起眼,但起作用的时候相当明显。

会影响文件操作密集型基准测试的噪声对于会大量读写文件的基准测试,搜索索引、云同步和常驻应用都会影响测量结果,因此要通过排除设置或停止这些程序来减少噪声的示意图。搜索索引影响文件操作密集的基准测试云同步常驻应用用排除与停止来减少噪声

图9:读写文件越多的基准测试,停掉索引与同步的效果越明显。

不对齐散热条件的比较,基本上是在比较散热

CPU 和 GPU 在冷机状态与热机之后,运行频率是不同的。也就是说,即使是同一份代码,每次执行的条件都在变。 笔记本电脑、超薄迷你主机、小型台式机尤其明显。

散热改变条件的机制CPU 和 GPU 在冷机状态与热机之后运行频率不同,因此即使是同一份代码,每次执行的条件都会变化,不对齐散热条件的比较基本上就是在比较散热的机制示意图。在冷机状态下执行热起来后频率会变每次执行条件都在变不对齐就是在比较散热

图10:频率会随温度变化,不对齐散热条件就变成在比较散热而不是代码。

需要遵守的规则

  • 尽量对齐室温
  • 固定笔记本电脑的摆放方式
  • 固定 AC 适配器、扩展坞、外接显示器的配置
  • 基准测试之前不要做重负载的工作
  • 把首次执行与稳定状态分开测量

执行顺序要交替

避免先把 A 跑 10 次,再把 B 跑 10 次。 因为散热、缓存、后台活动的偏差会压在一边。

推荐下面几种做法中的任意一种。

  • A B A B A B ...
  • A B B A A B B A ...
  • 事先生成随机顺序,再按该顺序执行
执行顺序决定偏差落在哪里先把 A 全部跑完再跑 B,会让散热、缓存和后台活动的偏差只压在其中一方身上,而交替或随机顺序执行可以把偏差分散到两边的示意图。先跑完全部 A → 再跑全部 B偏差只压在一边A B A B 交替或随机顺序偏差分散到两边可以把顺序的影响从差异中剔除

图11:把顺序集中起来就会连偏差一起比较,所以要交替或随机执行。

测量什么,决定了“快”的含义

把“快”压缩成一个数字,多半会出事。 在 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 不同。 特别是想确认“等待时间没变,但计算部分是否变轻了”的时候很有用。

3 个指标各自观察的侧面wall-clock time 是用户等待的时间,CPU time 是进程实际使用 CPU 的时间,cycle count 是 CPU 周期数,三者展示不同侧面,组合起来才能读懂“快”的内容的示意图。读懂“快”的内容wall-clock:等待的时间CPU time:使用的 CPU 时间cycle:计算部分的轻重用组合来推测原因

图12:不要压缩成一个数字,要用 3 个指标的组合去读懂“快”的含义。

priority、affinity、NUMA 是最后的手段

这些手段有时确实有效。 但正因为有效,一开始就去动它们,容易制造出另一种现象。

先按常规方式测量

如果默认状态下就出现差异,这个差异本身就有价值。 一上来就加 /high 或 /affinity,就等于引入了 “真实 Windows 环境中不会出现的条件”。

priority 与 affinity 是最后的手段先在默认状态下测量,出现差异时这个差异本身就有价值;一上来就固定优先级或亲和性,等于引入真实 Windows 环境中不会出现的条件,所以要在明确目的之后作为最后手段使用的示意图。确有需要时,先定好目的先在默认状态下测量出现差异则差异本身有价值一上来就用 /high 或 /affinity引入现实中不会出现的条件作为最后手段进行固定

图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 虽然可以用,但 最好不要用。 它往往不是在降噪,而是在制造另一种问题。

推荐的测量步骤

把前面的内容整理成一套便于实际执行的步骤。

偏实验室的比较步骤

  1. 固定比较对象
    • commit hash / build number
    • compiler / runtime version
    • Debug / Release
    • 有没有日志、assert、trace
  2. 固定机器条件
    • Windows build
    • BIOS / UEFI version
    • driver version
    • 接 AC 电源
    • 室温、摆放方式
  3. 固定电源条件
    • 决定 Power mode
    • 记录 Active power plan
  4. 重启
  5. 测试前等几分钟
  6. 必要时做 clean boot
  7. 加入 warm-up
  8. A / B 交替执行
  9. 保证执行次数
  10. 保留中位数、最小值、最大值、p95
  11. 保存 raw data
  12. 差异较小时采集 ETW / WPR

要跑多少次

第 9 条“保证执行次数”也要定个大致标准。下面不是统计学上的严格解,而是实务中的折中方案。

想看什么 每个版本的执行次数参考
只看中位数,确认较大的差异(1 成以上) 10 次
想主张百分之几的差异,同时也想看离散程度 30 次
想读到 p95 30 次以上。20 次时 p95 就等于最靠上的那 1~2 个值本身,会直接受到离群值的影响

所需时间可以按 单次执行时间 × 次数 × 版本数 + warm-up 来估算。如果单次处理需要 30 秒,A / B 各跑 30 次,加上 warm-up 大约是 35 分钟左右。当这个时间不现实时,与其削减次数,不如 把测量对象切小(只截取耗时较重的工序),这样思路更正。

如果不知道该在哪里收手,一个清晰的做法是 一边增加次数一边观察中位数的变化,等到再增加也不动了就停下来。

执行次数的收手点一边增加次数一边观察中位数的变化,等到再增加中位数也不再变动时就停下来,这是决定执行次数的实务做法的示意图。还在变增加也不再变增加次数继续跑观察中位数的变化就在这里停下

图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 基准测试中很容易被破坏。 只要有一次 Defender 介入、弹出一次通知、别的进程敲了一下 SSD,平均值就会被带偏。

平均值会被离群值带偏只要有一次 Defender 扫描、通知或其他进程的 I/O 混进来,平均值就会被带偏,因此要以中位数为主,结合 p95、p99 和 min/max 从分布上来看的示意图。只混进一次噪声平均值被带偏以中位数为主同时看 p95 / p99 与 min / max得到对离群值更稳健的读法

图15:平均值会被一次噪声破坏,所以要结合中位数与分布来读。

推荐下面这个组合。

  • 中位数:先看这个
  • p95 / p99:看尾部有没有恶化
  • min / max:看离群的程度
  • 箱线图或散点图:差异较小时很有用

出现差异时怎么读

结果的解读,组合起来看会更清楚。

只有 wall-clock 变快

可能是 I/O、等待时间、缓存或调度上的改善。

CPU time 和 cycle 都下降了

很可能是实现本身变轻了。

只有第一次慢 / 快

这是 cold / warm 的差异。要怀疑启动、初始化、缓存生成、JIT。

跑得越多越慢

要怀疑散热、降频、内存压力、后台活动。

从差异的形态读出原因只有 wall-clock 变快就看等待与 I/O,CPU time 和 cycle 都下降就是实现变轻,只有第一次不同就是 cold 与 warm 的差异,跑得越多越慢就怀疑散热与后台活动的读法示意图。只有实际耗时CPU 时间也减少只有第一次后半段变慢观察差异是怎么出现的等待、I/O 相关实现变轻了cold / warm散热、后台活动

图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 时间、等待时间以及上下文切换的原因。锁等待和 I/O 等待的差异会在这里体现
是不是驱动程序导致的卡顿 DPC/ISR 按模块查看耗时。如果这里很大,那差异本来就不在应用一侧
磁盘是不是在起作用 Disk Usage 看 I/O 的次数、大小和服务时间

做比较时,基本做法是 在同一场景下为 A 和 B 各采集一份跟踪,把同样的图并排来看。只看一份,是判断不出“这算慢吗”的。

走到这一步之后,就不再是 “B 快 3%”, 而是能说出 “B 的锁等待减少了,ready time 下降了” “A 的 file open 变多了,cold start 更慢” 这种带原因的结论。

从只有数字的差异走向带原因的差异当差异较小、读不出原因时,用 WPR 在同一场景下为 A 和 B 各采集一份跟踪,再用 WPA 把同样的图并排比较,就能从“快百分之几”变成带原因的结论的流程示意图。差异较小、读不出原因用 WPR 采集 A 与 B 的跟踪用 WPA 把同样的图并排来看能说出带原因的结论只看一份判断不出是否算慢

图17:挖到 ETW 这一层,“快百分之几”就变成了“为什么更快”。

一页纸的检查清单

最后整理成可以直接贴进操作手册的形式。

固定

  • 已固定比较对象(commit hash / build number / Debug 还是 Release / PGO 等构建条件 / 有没有日志和 assert)
  • 笔记本电脑已接 AC 电源
  • 已在设置应用里固定 Power mode
  • 已用 powercfg /getactivescheme 确认 Active power plan
  • 已关闭通知。已停止搜索索引与云同步
  • 必要时已切换为 clean boot
  • 已重启,并等了几分钟再开始

执行

  • 已加入 warm-up
  • 已把 cold(首次)与 warm(稳定)分开测量
  • 已按交替或随机顺序执行 A / B
  • 已定好次数再执行(参考上面的表)

记录

  • 已保留每次执行一行的 raw data(elapsed_ms / user_ms / kernel_ms / cycles)
  • 已保留接的是 AC 还是 DC、Power mode(电源模式)、Power plan(电源计划)的 GUID、Windows build、driver version
  • 已保留室温与摆放状态
  • 没有固定的条件也写下来了

解读

  • 已看中位数,没有只凭平均值下判断
  • 已用 p95 / p99 看尾部
  • 已用 min / max 确认离群值
  • 已用 wall-clock / CPU time / cycle 的组合推测原因
  • 差异较小时已挖到 ETW / WPR

总结

在 Windows 上比较不同版本的程序时,真正管用的不是花哨的偏门技巧。 重要的是下面这些 朴素但能提升可复现性的做法。

  • 固定并记录 AC / Power mode(电源模式)/ Power plan(电源计划)
  • 把 cold 与 warm 分开
  • A / B 交替执行
  • 看中位数与分布
  • 必要时做 clean boot
  • 差异较小时用 ETW / WPR 挖到原因

而最重要的一点是,把固定了什么、没固定什么与结果一起写下来。 基准测试既是速度的比较,同时也是实验条件的记录。

没有写明条件的提速报告,别人无法确认能不能得到同样的结果。因为留下的只有数字,没有留下复现的办法。 反过来,只要条件写得扎实,即使差异很小,那个结果也是有价值的。

条件的记录决定结果的价值没有写明条件的提速报告只留下数字、没有留下复现的办法,而条件写清楚了即使差异很小也有价值,说明基准测试同时也是实验条件的记录的示意图。不写条件的报告只留下数字别人无法确认写了条件的报告留下了复现的办法即使差异很小也有价值

图18:基准测试的价值与其说在数字,不如说在于记录了固定什么、没固定什么。

参考资料

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。

本文与以下服务页面相关联,欢迎从最接近的入口查看。

常见问题

汇总了咨询这一主题时常见的问题。

在 Windows 上做基准测试时,结果波动的主要原因有哪些?
有很多层面的因素,包括 Power mode(电源模式)、Power plan(电源计划)、散热、后台更新、搜索索引、病毒扫描、优先级与亲和性、执行顺序、缓存状态等。即使是同一台 Windows 机器,只要这些条件没有对齐,实质上就是两次不同的实验。尤其是笔记本电脑,接 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 是什么这三项。
找不到 High performance 电源计划,是设备故障吗?
很可能不是故障。Microsoft 的文档说明,在支持 Modern Standby 的设备上,只允许使用 Balanced 或由 Balanced 派生出来的计划。也就是说,这台机型在设计上就不会出现 High performance,这是很正常的情况。另外,如果 Power mode 的界面无法更改,可能是选中了 custom power plan,这时先试着切换到 Balanced 是比较快的排查方式。
比较版本 A 和 B 的速度时,应该按什么顺序执行?
应避免先把 A 全部跑完再跑 B,因为散热、缓存、后台活动的偏差会全部压在其中一方身上。建议按 A B A B 交替执行,或者按事先生成的随机顺序执行。同时还要把冷机状态下的第一次与热机之后的稳定状态分开测量,并且不只看平均值,还要看中位数、p95、最小值和最大值,这样才能避免结果被离群值带偏。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表