验证原则
在这个仓库里,性能结论只有在通过相应的 preset、测试与测量路径之后才会被接受。默认姿态很简单:先证明正确,再做测量,最后解释为什么这个数字可信。
默认契约
维护者处理常规改动时,应首先走这条基线路径:
bash
cmake --preset=debug
cmake --build build/debug
ctest --preset=debug这条路径只回答一个狭窄但关键的问题:仓库是否仍能在可调试配置下顺利构建,并通过标准检查?
证据阶梯
| 问题 | 最低证据 | 常见升级路径 |
|---|---|---|
| 改动是否保持了既有行为? | debug 构建与 ctest --preset=debug | 如果契约此前是隐含的,就补一条聚焦的单元测试或性质测试 |
| 优化路径是否仍具代表性? | release 或 relwithdebinfo 构建加一次基准运行 | 如果加速不明显或不稳定,就收集 profiler 输出 |
| 低层代码是否仍然内存安全? | asan 构建与测试 | 在同时改动多个变量之前,先缩小到最小失败工作负载 |
| 并发代码是否遵守同步契约? | 单元或性质测试加 tsan | 缩减工作负载,并确认存在一条可复现的竞态路径 |
| 算术、布局或转换假设是否成立? | ubsan | 如果假设是刻意且与特定架构相关,应在文档中写明 |
Preset 阶梯
仓库把 preset 当作文档、CI 与本地维护的公共验证语言,这使命令在不同表面之间保持稳定。
| Preset | 使用时机 | 存在意义 |
|---|---|---|
debug | 建立基线 | 反馈快、失败可读、测试覆盖完整 |
release | 比较性能敏感变体 | 优化后二进制才能暴露真实代码生成结果 |
relwithdebinfo | 分析你准备长期保留的工作负载 | 同时保留优化与符号,是 perf 的合适默认值 |
asan | 调查生命周期或越界问题 | 这类缺陷未必会在功能层面立即暴露 |
tsan | 修改原子、队列或 OpenMP 代码时 | 竞态检测与原始吞吐量是不同维度的问题 |
ubsan | 验证低层算术、位移或转换 | 未定义行为往往会直接使基准结论失效 |
基准原则
基准是证据阶梯的一部分,而不是替代品。在本仓库中,只有当周边条件明确时,基准结果才具有说服力:
- 可执行文件被准确标识。 优先给出精确路径,例如
./build/release/examples/02-memory-cache/aos_vs_soa_bench。 - 构建 preset 被明确命名。
release与relwithdebinfo用于诊断时含义不同。 - 比较只隔离一个变量。 算法、数据布局或同步策略应一次只变动一个。
- 硬件背景被理解。 缓存、ISA 与核心数决定结果能否推广。
- 数字可复现。 如果你无法在本地重新跑出它,它还只是一个观察,不是原则。
性能分析原则
当你需要因果解释,而不只是前后对比数字时,再使用 profiler。
- 用
perf stat做快速计数器级检查。 - 当需要调用路径解释时,使用
perf record -g --call-graph dwarf或 FlameGraph 工具链。 - 当符号化分析比峰值纯净度更重要时,使用
relwithdebinfo。 - 把 profiler 输出视为下一次实验的指引,而不是独立成证的终点。
何时可以发布一个结论
当你能回答以下全部问题时,一个性能结论才适合进入文档、注释或评审:
- 生成该二进制用了哪个 preset?
- 运行的是哪个可执行文件或测试目标?
- 被改变的变量是什么?
- 如果这个结论是错的,哪一个验证表面会推翻它?
- 一个持怀疑态度的读者下一步该看哪一页或哪个源文件?