perf_runner 设计

设计目标

moon bench 报告的是汇总结果;而仓库的报告流水线需要单个用例的逐样本原始计时,自行控制重复次数和预热,还需要一种重放用例并检查其结果的方式。perf_runner 就是这样的单用例工具。

数学背景

一个样本是 repeat 次连续调用的平均时间,t=1repeat∑j=1repeatTjt = \tfrac1{\text{repeat}} \sum_{j=1}^{\text{repeat}} T_j。在样本内部取平均,可以把每次测量固定的计时器分辨率误差减小为原来的 1/repeat,并把独立的单次调用噪声减小为 1/repeat1/\sqrt{\text{repeat}}。在样本之间,流水线使用顺序统计量(中位数、p90)和 MAD,它们对调度造成的离群值是稳健的。预热轮次会丢弃最初几次调用的瞬态(缓存、分支预测器、惰性初始化)。

设计决策

在计时器之外制作 scratch 副本

对于会修改输入的用例,repeat 份副本会在 monotonic_clock_start 之前准备好,因此复制不计入测量,且每次调用看到的输入相同。

每种模式都有校验和

每次调用的校验和都被折叠进一个累计值,使工作保持可观察,诊断模式也能据此验证结果逐位相同。

纯 JSON 行

输出是每次运行一个 JSON 对象,通过字符串拼接组装,这使 runner 无需序列化依赖,也便于用 Python 解析。

正确性与不变量

  • 在诊断模式下,打印的校验和等于各次调用校验和的 FNV 风格折叠,这由该包的白盒测试验证。
  • 为 scratch 用例制作的副本从不与已准备的输入互为别名。

被否决的方案

  • 为每次调用单独计时。 亚微秒级的调用低于时钟分辨率;repeat 将其分摊。

边界

runner 每个进程只测量一个用例,自身不计算统计量,并且只支持在 perf_support 中注册的用例。