ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PowerShell 性能回归检测实战:用 ResultsComparer 对比 BenchmarkDotNet 基准测试结果

PowerShell 性能回归检测实战:用 ResultsComparer 对比 BenchmarkDotNet 基准测试结果 PowerShell 性能回归检测实战用 ResultsComparer 对比 BenchmarkDotNet 基准测试结果【免费下载链接】PowerShellPowerShell for every system!项目地址: https://gitcode.com/GitHub_Trending/po/PowerShellResultsComparer 是 PowerShell 仓库中专门用于对比两批 BenchmarkDotNet 基准测试结果的小型命令行工具其核心用途是在引擎代码改动前后自动识别性能回归Slower与性能提升Faster。本文以仓库内 ResultsComparer 说明文档 为主线结合 Program.cs、CommandLineOptions.cs 等源码完整讲解其命令行参数、输入数据格式、统计判定原理、输出解读以及如何通过 perf.psm1 与 benchmarks 基准测试项目 打通从跑基准到判回归的完整工作流。背景PowerShell 的微基准与回归检测需求PowerShell 仓库在test/perf/benchmarks下维护了一批针对PowerShell 引擎内部实现的微基准micro benchmarks用来度量解析器、编译器、脚本块执行、引擎内部 API 等热路径的性能。该目录的 README 明确提出性能测试的三条要求一套足够好的基准、一组配置一致的机器、以及用于回归检测的自动化手段。要判断新改动是否引入了回归仅看单次运行的数字是不够的——你至少需要两条数据改动前的基准结果baseline改动后的对比结果diff。ResultsComparer 就是为此而生的工具。仓库在test/perf/dotnet-tools/中维护了一套从 .NET 性能测试生态中移植的性能工具集工具集 README其中 ResultsComparer 负责比较不同基准测试结果从而直观展现新改动相对于基线的回归情况。工具定位与适用场景ResultsComparer 的设计目标非常聚焦输入两份基准结果输出两者之间在统计学意义上存在差异的基准项。依据 ResultsComparer README它可以被用来比较比较维度典型示例历史结果我的改动之前 vs. 我的改动之后不同操作系统Windows vs. Ubuntu不同 CPU 架构x64 vs. ARM64不同目标框架.NET Core 3.1 vs. 5.0也就是说只要输入输出格式一致无论差异来自代码、运行时还是运行环境它都能给出统一的对比结论。运行前提与目录结构ResultsComparer 是一个独立的 .NET 控制台项目位于test/perf/dotnet-tools/ResultsComparer/结构如下ResultsComparer.csproj项目文件依赖CommandLineParser、MarkdownLog、Newtonsoft.Json、BenchmarkDotNet与PerfolizerProgram.cs程序入口与全部比较逻辑CommandLineOptions.cs命令行参数定义DataTransferContracts.csBenchmarkDotNet JSON 报告的数据模型。从 ResultsComparer.csproj 可以看到项目默认以net5.0为回退目标框架可通过环境变量PERFLAB_TARGET_FRAMEWORKS覆盖编译运行需要本机具备对应版本的 .NET SDK。运行前先进入该目录cd test/perf/dotnet-tools/ResultsComparer dotnet run -c Release --base 基线结果路径 --diff 对比结果路径 --threshold 统计阈值仓库中的 perf.psm1 正是用这种方式调用它的先Push-Location到 ResultsComparer 目录再执行dotnet run -c Release见 perf.psm1。命令行参数详解依据 CommandLineOptions.cs 与 ResultsComparer README工具的全部参数如下参数是否必填说明--base是存放基线baseline结果的文件或文件夹路径--diff是存放待比较diff结果的文件或文件夹路径--threshold是显著性检验阈值例如5%、10ms、100ns、1s--top否只输出差异最大的前/后N条结果--noise否噪声阈值默认0.3ns例如1.0ns与1.1ns相差 10%但很可能只是测量噪声--csv否将结果导出到指定 CSV 文件--xml否将结果导出为 XML便于接入 CI 报告对应源码见 CommandLineOptions.cs-f/--filter否用 glob 通配符模式按名称过滤基准项可多次指定需要特别注意的是threshold 与 noise 的取值格式支持两种单位制既可以是相对比例如5%也可以是绝对时间量如10ms、100ns、1s。此外--threshold是唯一标记为必填的参数--noise则在未指定时使用默认值0.3ns见 CommandLineOptions.cs。一条完整的对比命令沿用文档中最经典的双系统对比示例Windows 上的结果作为基线、Ubuntu 上的结果作为 diff使用1%阈值只打印差异最大的 TOP 10dotnet run --base C:\results\windows --diff C:\results\ubuntu --threshold 1% --top 10如果把阈值换成绝对时间量并叠加噪声过滤可以这样写对应 CommandLineOptions.cs 中内置的用法示例dotnet run --base C:\results\win --diff C:\results\unix --threshold 5% --noise 0.5ns若只想比较名为System.Math*的基准项dotnet run --base C:\results\ubuntu16 --diff C:\results\ubuntu18 --threshold 5% -f System.Math*输入格式仅支持*full.json注意ResultsComparer 只支持 BenchmarkDotNet 导出的*full.json结果文件源码中以常量FullBdnJsonFileExtension full.json限定见 Program.cs。路径解析规则在 GetFilesToParse 中实现如果传入的是文件夹会递归查找其中所有*full.json文件如果传入的是具体文件则直接解析该文件若路径不存在且不满足上述条件抛出明确异常。值得强调的是README 中特别说明此导出器在本仓库中默认启用。这一点可以从基准测试的推荐配置得到印证RecommendedConfig.cs 在基准运行配置里主动添加了JsonExporter.Full并额外注册了PerfLabExporter因此在test/perf/benchmarks下跑出的结果天然就带full.json变体可以直接喂给 ResultsComparer。统计判定原理双重阈值 Mann-Whitney 检验比较绝非简单地算差值百分比因为微基准数据本身存在噪声。ResultsComparer 的判定逻辑位于 GetNotSameResults采用的是TOSTTwo One-Sided Tests双单侧检验等价性检验检验算法来自 Perfolizer 的MannWhitneyTest先用用户指定的--threshold显著性阈值做一次 TOST 检验如果两组数据在该阈值下被判定为Same直接跳过认为没有差异若第一层判定有差异再用--noise默认 0.3ns做第二次 TOST 检验如果该差异在噪声阈值下又被判定为Same说明差异量级落在测量噪声范围内同样被过滤掉只有同时通过两层检验的基准项才会被报告结论为Slower变慢或Faster变快。这就是为什么绝对差值极小如 1.0ns 对 1.1ns时不会误报——虽然相对变化有 10%但绝对差异不过 0.1ns会被默认0.3ns的噪声阈值吸收。检验所依赖的原始数据来自 DataTransferContracts.cs 中手工添加的GetOriginalValues()方法它从Measurements里挑出IterationStage Result的迭代逐条计算Nanoseconds / Operations即单次调用耗时得到一组真实 workload 样本——warmup、pilot 等阶段的数据一律被排除。另外ReadResults 中会跳过两端任一Statistics null的项这类通常是执行失败的基准并且只保留Statistics完整可比的条目。基准项的匹配方式比较时并不依赖测试文件名而是使用**完整基准名FullName即 namespace.type.method 加参数**做精确匹配从 diff 结果建立FullName - Benchmark字典再以 base 结果的 FullName 去查字典见 Program.cs。这意味着只对比两边同时存在的基准项名称不同哪怕只是参数不同就不会被匹配上没有差异、或没有匹配项的基准结果都不会出现在输出中。输出解读summary 与 Slower / Faster 表比较完成后程序依次输出总览 summary与两张 Markdown 表格。判定与输出流程见 Compare。summary 总览PrintSummary 会打印better变快Faster的条目数与几何平均加速比 geomeanworse变慢Slower的条目数与几何平均减速比total diff所有存在差异的条目总数。注意其中会把比值为double.PositiveInfinity的条目从 geomean 计算中剔除——这是因为当 baseline 缺少对应测试时会出现无穷大比值见 Program.cs 的注释说明。两张结果表表格依据变慢/变快分组输出见 PrintTable每列含义如下Slower 表diff/base比值 1表示 diff 相对 base 变慢Faster 表base/diff比值 1表示 diff 相对 base 变快Base Median (ns)/Diff Median (ns)两边的中位数耗时ns比值排序按中位数计算Modality分布形态提示详见下文。ResultsComparer README 中给出的示例输出如下Slowerdiff/baseBase Median (ns)Diff Median (ns)ModalityPerfLabTests.BlockCopyPerf.CallBlockCopy(numElements: 100)1.609.2214.76System.Tests.Perf_String.Trim_CharArr(s: Test, c: [ , ])1.416.188.72Fasterbase/diffBase Median (ns)Diff Median (ns)ModalitySystem.Tests.Perf_Array.ArrayCopy3D1.31372.71284.73当所有条目的结论都一致时表格仍会保留若某一方向没有任何差异则会打印类似No Slower results for the provided threshold 1% and noise filter 0.3ns.的提示若整体不存在任何差异则直接输出No differences found between the benchmark results with threshold ...。Modality分布形态的含义Modality列用来提示测量分布是否多峰——多峰通常意味着测量不稳定或存在间歇性干扰。其算法与 BenchmarkDotNet 的多峰分布分析器一致由 GetModalInfo 基于 Perfolizer 的MValueCalculator计算 M 值判定样本数N 12数据量不足不判定M 4.2标记为multimodal多峰M 3.2标记为bimodal双峰M 2.8标记为several?疑似多峰。结果过滤与导出--top、--filter、--csv、--xml--top N在排序后只取前/后 N 条。结合源码可见Slower 表按比值降序取前 NFaster 表同理见 Program.cs可配合阈值用来聚焦最严重的回归 TOP 榜。-f/--filter按完整基准名做 glob 过滤支持多个模式内部先把通配符转成大小写不敏感的正则*映射为.*、?映射为.见 WildcardToRegex再对每条基准的FullName进行匹配。--csv把存在差异的条目及其每次迭代的原始样本导出为分号分隔的 CSV。每个条目写两行格式为基准名;base|diff;结论;原始值序列见 ExportToCsv方便进一步做外部统计或绘图。--xml生成可供 CI/报告系统消费的 XML见 ExportToXml。其语义设计得很有 CI 味道变慢的条目输出为test resultFail 异常类型Regression的失败信息变快的条目输出为test resultSkip并附上was X is Y的中位数变化说明。也就是说只要把 XML 接入流水线任何Fail条目都代表一次真实的性能回归告警。在本仓库中的端到端实践工作流上面的底层细节最终落到 PowerShell 仓库的性能日常中是一条简单闭环具体流程记录在 benchmarks/README.md 的 Regression Detection 一节跑当前代码的基准Start-Benchmarking -Filter *script* -Artifacts C:\arena\tmp\BenchmarkDotNet.Artifacts\current\跑某个已发布版本如 7.1.3的基准作为基线Start-Benchmarking -Filter *script* -Artifacts C:\arena\tmp\BenchmarkDotNet.Artifacts\7.1.3 -TargetPSVersion 7.1.3用 Compare-BenchmarkResult 对比Compare-BenchmarkResult -BaseResultPath C:\arena\tmp\BenchmarkDotNet.Artifacts\7.1.3\ -DiffResultPath C:\arena\tmp\BenchmarkDotNet.Artifacts\current\ -Threshold 1%上述第三步调用的Compare-BenchmarkResult就是 ResultsComparer 的 PowerShell 封装定义于 perf.psm1它把-BaseResultPath/-DiffResultPath/-Threshold映射为--base/--diff/--threshold并透传可选的-Noise默认 0.3ns与-Top参数最终在 ResultsComparer 项目目录中执行dotnet run -c Release。该 README 记录了一次真实运行的输出示例对比 7.1.3 与当前代码阈值 1%summary: better: 4, geomean: 1.057 total diff: 4 No Slower results for the provided threshold 1% and noise filter 0.3ns. | Faster | base/diff | Base Median (ns) | Diff Median (ns) | Modality | | --- | ---: | ---: | ---: | ---: | | Engine.Scripting.InvokeMethod(Script: $fsNew-Object -ComObject scripting.files | 1.07 | 50635.77 | 47116.42 | | | Engine.Scripting.InvokeMethod(Script: $shNew-Object -ComObject Shell.Applicati | 1.07 | 1063085.23 | 991602.08 | | | Engine.Scripting.InvokeMethod(Script: String.GetType()) | 1.06 | 1329.93 | 1252.51 | | | Engine.Scripting.InvokeMethod(Script: [System.IO.Path]::HasExtension()) | 1.02 | 1322.04 | 1297.72 | |从中可以清楚看到这一轮改动对 4 个脚本类基准项均有约 1.02~1.07 倍的提升且没有任何 Slower回归。这正是 ResultsComparer 在 PowerShell 性能工程中的典型价值——把改动是否有害从主观感觉变成一行可量化的结论。小结ResultsComparer 虽小却是 PowerShell 性能回归检测流水线的关键一环它用统计检验Mann-Whitney TOST 双阈值替代朴素的百分比比较用完整基准名精确对齐两份结果用 summary Markdown 表格 CSV/XML 导出覆盖人工查看与自动化告警两种消费方式。配合 Start-Benchmarking / Compare-BenchmarkResult 两个模块函数开发者在合入改动前后即可快速得到量化的性能影响结论。【免费下载链接】PowerShellPowerShell for every system!项目地址: https://gitcode.com/GitHub_Trending/po/PowerShell创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进