Skip to content

Commit fdb816e

Browse files
committed
bench: the local runner, and the three bugs running it found
The other half of the previous commit. That one's message describes this change set, but its `git add` listed the already-deleted bench.yml first and git aborts the whole command on a pathspec that matches nothing — so only the staged deletion went in. Recorded rather than force-pushed over. bench/run-standard.sh: one command, no arguments. The standard set is DERIVED from bench/matrix.json rather than repeated — the cells for this OS, every engine they list. `allow_failed` is deliberately ignored: those waivers record what broke on a shared runner, and at least two of them (cmake on the mcpp tree, cmake on the xlings tree) configure and generate perfectly well here. Filtering by them would carry a runner's limitation into local data. Three bugs it found, all mine: * THE FIELD SPLIT. `IFS=$'\t' read` treats tab as IFS *whitespace*, so consecutive tabs COLLAPSE. A cell with no `leaf` shifted every later field one place left and cmake was handed the baseline as its source directory — `CMake Error: The source directory ".../bench/projects/2026.8.11.3" does not exist`, five cells, and the message names neither the wrong field nor the empty one before it. Now \x1f. `BENCH_PRINT_ARGV=1` prints the argv, so this is checked against what is passed rather than against the plan — the fix was verified by reading `--buildfiles .../projects/mcpp` off that output. * SCRATCH IN results/. `--work "$OUT/work"` put engine scratch where the README guard scans for medians, and one of those files is a JSON ARRAY: the guard died with `AttributeError: 'list' object has no attribute 'get'`. Work goes to TMPDIR; the guard skips non-objects instead of crashing. * xmake's clang arm never reached the branch its own error message advises. It picks the std module by C++ LIBRARY and reads the library from the target's RUNTIME; with none declared, clang on Linux is treated as libstdc++, so it looks for GCC's modules.json inside the LLVM payload and suggests `--sdk`, which only the libc++ branch reads. The payload has carried `lib/<triple>/libc++.modules.json` all along. Built-in `llvm` toolchain plus `--runtimes=c++_static` plus `--sdk` gets there — measured: the warning goes away and xmake starts emitting module BMIs. e2e 233's workflow-specific checks moved to the script with their judgements intact — one list read rather than repeated, three runs, no cell names hard-coded — and each was verified to fail in the bad direction.
1 parent fac5817 commit fdb816e

5 files changed

Lines changed: 526 additions & 59 deletions

File tree

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# bench 改为「本地真跑、CI 不跑」:方案与 Linux 实测计划(2026-08-14)
2+
3+
**状态:待 review,尚未实施。**
4+
5+
---
6+
7+
## 0. 一句话
8+
9+
把 bench 从 CI 里整个删掉,改成**一条本地可重复的命令**产出数据;当前只承诺
10+
**Linux**,其他平台在文档里明确标 `(未测试)`;标准数据集固定为 **3 轮**。
11+
12+
---
13+
14+
## 1. 为什么删 CI bench —— 不是因为它慢,是因为它测不到东西
15+
16+
删除的理由必须是事实,不是偏好。当前 `bench/matrix.json` 的账:
17+
18+
| | |
19+
|---|---|
20+
| 格子数 | 10 |
21+
| 外部引擎臂总数 | 32 |
22+
| **被 `allow_failed` 豁免的** | **12(37%)** |
23+
| 其中 xmake | 在测 4 / 豁免 6 —— **豁免多于在测** |
24+
| 其中 cmake | 在测 5 / 豁免 5 |
25+
26+
也就是说:一个自称「跨引擎对比」的矩阵,有三分之一的对比臂从未产出过数字,而
27+
job 是绿的。这不是"还没修完",这是**这个东西在 CI 上跑的形态本身就不成立**:
28+
29+
1. **共享 runner 上测的是 runner,不是引擎。** 两核机器、邻居噪声、镜像随时更新。
30+
同一份代码在 runner 上 cold 是 243s,在开发机上是 79s —— 三倍差距不来自 mcpp。
31+
2. **外部工具的缺口不是 mcpp 的缺陷,却由 mcpp 的 CI 承担。** xmake 编不了
32+
libc++ 的 `import std`、bazel 的 MSVC 依赖扫描器坏掉、rules_cc 不支持
33+
Windows PIC —— 每一个都要在 mcpp 的矩阵里挂一条豁免,而豁免越多,绿色越没意义。
34+
3. **一次矩阵约两小时,而它回答的问题("性能有没有退")在 PR 粒度上并不需要每次回答。**
35+
真正需要它的时刻是**发版前**和**做完一次优化后**,那两个时刻都有人在场。
36+
37+
**保留 CI 里的哪一部分**:`tests/e2e/230_bench_harness.sh`(套件自身能构建、能跑出一份
38+
合法报告)继续留在 e2e 里。删掉的是**跑真实工作负载的那个矩阵**,不是套件的自测。
39+
40+
---
41+
42+
## 2. 删掉什么、留下什么
43+
44+
**删除**
45+
- `.github/workflows/bench.yml` 整个文件
46+
47+
**保留,但改变角色**
48+
- `bench/matrix.json` —— 从「CI 跑哪些格子」变成「**标准数据集的定义**」。
49+
这一点很关键:清单仍然只有一份,只是读它的人从 workflow 变成了本地脚本。
50+
- `bench/src/**` —— harness 本身不动。
51+
- `bench/results/**` —— 数据仍然入库。
52+
53+
**新增**
54+
- `bench/run-standard.sh`(名字待定)—— 读 `matrix.json`,跑**当前平台**的格子,
55+
3 轮,输出到 `bench/results/<date>-<os>-<arch>/`。一条命令,不带参数也能跑。
56+
57+
**必须同步改的守卫(否则删完 e2e 直接红)**
58+
- `tests/e2e/233_bench_matrix.sh` 现在有 5 处读 `.github/workflows/bench.yml`:
59+
第 48/53 行(存在性)、427(workflow 读 matrix.json)、438(`--runs` 与
60+
dispatch 默认值)、475/480(runner 镜像不得内联)。这些检查的**对象**要从
61+
workflow 换成 `bench/run-standard.sh`,判据不变:**清单只有一份、脚本读它而不重复它**。
62+
- `tests/e2e/232_workflow_syntax.sh` 会少一个文件,无需改。
63+
64+
---
65+
66+
## 3. 标准数据集 —— 只承诺能证明的
67+
68+
### 3.1 平台
69+
70+
| 平台 | 承诺 | README 写法 |
71+
|---|---|---|
72+
| **Linux x86_64** | 本次真实跑出 | 完整数据 |
73+
| macOS | 不跑 | `(未测试)` |
74+
| Windows | 不跑 | `(未测试)` |
75+
76+
`(未测试)` 不是 `(不支持)`。差别要在文档里写明:**没有数据 ≠ 不能用**,而
77+
"曾经有过一版数据、但那版数据是在有缺陷的 harness 上取的"更要写明 —— 见 §5。
78+
79+
### 3.2 格子(Linux)
80+
81+
从现有 10 格里取 Linux 的 6 格,并**去掉所有需要豁免的外部臂**:
82+
83+
| 工作负载 | 编译器 | 引擎 | 说明 |
84+
|---|---|---|---|
85+
| `fixture` | gcc | mcpp, cmake, xmake | 三引擎,唯一能控制 variant 轴的地方 |
86+
| `fixture` | clang | mcpp, cmake, xmake, bazel | 四引擎 |
87+
| `mcpp-2026.8.11.3` | gcc | mcpp, xmake | cmake 有未定案缺口 → 不列 |
88+
| `mcpp-2026.8.11.3` | clang | mcpp, cmake | xmake 有已复现缺口 → 不列 |
89+
| `xlings-2026.8.11.2` | gcc | mcpp | 两个外部臂都有缺口 |
90+
| `xlings-2026.8.13.1` | gcc | mcpp | 同上,与上一行成对(两种代码风格) |
91+
92+
**原则:标准集里不出现豁免。** 一条臂要么在测,要么不在这张表里 —— 它的缺口写进
93+
§5 的「已知缺口」清单,附证据。这样"标准数据"里的每一个数字都是真的跑出来的,
94+
不需要读者去分辨哪些是豁免掉的。
95+
96+
### 3.3 轮次
97+
98+
**3 轮**(`--runs 3`),取中位数,同时记录 min/max。
99+
100+
理由:n=1 没有离散度,而现有 README 每张表都标 `n=1` 并附带"不要比较个位数字"的
101+
警告 —— 那个警告本身就说明 n=1 不够。3 轮 × 6 格 × 5 场景在开发机上约 40–60 分钟,
102+
是一个人可以在一次会话里跑完的量。
103+
104+
### 3.4 场景
105+
106+
沿用现有六个:`cold` / `noop` / `touch-hub` / `touch-leaf` / `edit-body` /
107+
`edit-comment`。真实工程略去 `touch-leaf`(现有 note 已说明:真实的树里没有
108+
"没人 import 且足够稳定可以点名"的单元)。
109+
110+
---
111+
112+
## 4. 本地实测计划(Linux)
113+
114+
### 4.1 前置条件(脚本要检查并明确报错,而不是继续跑)
115+
116+
1. 工具链载荷齐备:`xim-x-gcc/16.1.0`、`xim-x-llvm/22.1.8`、`xim-x-binutils`、
117+
`xim-x-glibc`
118+
2. 外部工具按 `matrix.json` 的 `tools` 钉住:cmake 4.0.2 / xmake 3.1.0 / bazel 9.2.0
119+
3. 子模块已 checkout(三个 pinned 工作负载)
120+
4. 依赖包已解包(`mcpplibs.cmdline` 等)—— 现在 cmake 臂会 FATAL_ERROR,好过静默少编
121+
122+
### 4.2 步骤
123+
124+
```bash
125+
# 1. 构建被测的 mcpp 与 harness
126+
mcpp build --release
127+
cd bench && mcpp build --release && cd ..
128+
129+
# 2. 拉取钉住的工作负载
130+
git submodule update --init
131+
132+
# 3. 跑标准集(一条命令,内部按 matrix.json 展开 Linux 的格子)
133+
bash bench/run-standard.sh
134+
135+
# → bench/results/2026-08-14-linux-x86_64/*.json + report.md
136+
```
137+
138+
### 4.3 记录什么
139+
140+
每份报告已经带 `host`(CPU 型号、核数、内存、是否异构)。再补两件:
141+
142+
- **被测 mcpp 的版本与 commit** —— 现在只记版本号,同一版本可以有不同 commit
143+
- **跑完的时间戳与总耗时** —— 用于判断两次数据是否可比
144+
145+
### 4.4 判据(跑完必须核对,不能只看退出码)
146+
147+
1. 退出码 0
148+
2. `failed` 为 0、**`waived` 为 0**(标准集里不该有豁免)
149+
3. 每个 `cold` 都通过既有的两条不变量(vs 自身 noop ≥2×、vs 同侪 ≤20×)
150+
4. 三轮的 min/max 跨度不超过中位数的 ±20% —— 超了就是机器有噪声,数据不发布
151+
152+
---
153+
154+
## 5. README 要改成什么样
155+
156+
现在的 `bench/README.md` 是**一份实验记录**:900 行,大量"这里曾经错在哪"。
157+
那些内容有价值,但它不是一个人第一次想跑 bench 时该读到的东西。
158+
159+
**改成三层:**
160+
161+
```
162+
bench/README.md
163+
├─ 一、怎么跑(前 40 行,可以照抄的命令)
164+
├─ 二、标准数据(Linux 表格 + 其他平台标 (未测试))
165+
└─ 三、方法与已知缺口(现有内容,往后放)
166+
```
167+
168+
**「怎么跑」必须是可重复的**:给出完整命令序列、预期耗时、以及"跑完怎么判断这份
169+
数据能不能用"(§4.4 的四条)。现在的 README 里这些信息散在 §6 和 §10。
170+
171+
**已知缺口单独成节**,每条写:现象、归属(我们的 / 上游的)、复现方式、试过什么。
172+
当前应当列入:
173+
174+
| 缺口 | 归属 | 证据 |
175+
|---|---|---|
176+
| xmake + libc++ 编不了真实工程的 `import std` | 上游 | 本机复现,`--sdk` 与两种工具链都试过 |
177+
| cmake 在 CI runner 上 `__CMAKE::CXX23` 不可用 | 未定 | 外部可查项全部与能跑通的机器一致 |
178+
| cmake 读 xlings 依赖包时 `manifest has no sources` | **我们的** | 本机 rc=0,runner 上某个 `compat-x-*` 缺 `sources`;**具体包名待补** |
179+
| bazel MSVC 依赖扫描器 | 上游 rules_cc | Windows 专有 |
180+
181+
**中文版同步**,并保留"以英文版为准"的声明。
182+
183+
---
184+
185+
## 6. 现有已发布数据怎么处理
186+
187+
`bench/results/` 里已有 30 个 JSON。**不删**,但要在 README 里分层:
188+
189+
- **标准数据**:本次 Linux 实测那一份(3 轮),README 的表格只引用它
190+
- **历史数据**:其余的,标注日期与当时的 harness 状态
191+
192+
⚠️ 特别地:`bmi_schedule=on` 那一列的旧数据是在 §8b 缺陷修复**之前**取的,
193+
`touch-hub`/`edit-comment` 两格量的工作比构建欠下的少(已在 README §8b 记录)。
194+
本次重测应当把这一列一并重取,让标准表里不再混有修复前的数字。
195+
196+
---
197+
198+
## 7. 风险与取舍
199+
200+
| 取舍 | 得 | 失 |
201+
|---|---|---|
202+
| CI 不再跑 bench | 省两小时/次;不再用绿色掩盖 12 条豁免 | **性能回归不再自动发现** |
203+
| 只承诺 Linux | 数字都是真的 | 跨平台差异无数据 |
204+
| 标准集不含豁免臂 | 表里每个数字都真跑过 | 表变小(32 臂 → 约 14 臂) |
205+
206+
**最大的失**是第一条。缓解:把「发版前跑一次标准集」写进发布流程文档
207+
(`release-publish-pipeline` 那条);优化类 PR 在描述里附本地数据。
208+
209+
**不做的事**:不搞"CI 里跑一个缩水版 bench"。缩水版会重新引入"绿色代表什么"
210+
的模糊地带 —— 这正是要删掉它的原因。
211+
212+
---
213+
214+
## 8. 待 review 的决策点
215+
216+
1. `bench/run-standard.sh` 这个名字与位置,是否放在 `bench/` 下
217+
2. 标准集的格子清单(§3.2)是否合适 —— 尤其是否要保留 `xlings` 的两格
218+
(它们只有 mcpp 一条臂,是"两种代码风格对比"而非"引擎对比")
219+
3. 3 轮是否够;要不要对 `cold` 单独加轮次
220+
4. `bench/matrix.json` 是否继续承载"标准集定义",还是另起一个更小的文件
221+
5. §6 的历史数据:保留还是清理到只剩标准集

‎bench/matrix.json‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,6 @@
8585
"edit-comment"
8686
]
8787
},
88-
"runners": {
89-
"linux": "ubuntu-24.04",
90-
"macos": "macos-14",
91-
"windows": "windows-2022"
92-
},
9388
"cells": [
9489
{
9590
"os": "linux",

0 commit comments

Comments
 (0)