外观
实验:从正确结果到可解释的性能数据
本章给出已经执行的 CPU 实验与可继续开展的任务。所有时间都绑定具体程序、输入、编译器和测量口径,不把某个加速比推广为课程的固定答案。先确认结果,再讨论时间。
复现环境与命令
已执行环境为 2026-10-04 的 macOS arm64,Apple clang 21.0.0,C++17、-O3 -g -Wall -Wextra -Wpedantic -Werror -pthread。hardware_concurrency() 返回 10,仅作为线程库提示;沙箱拒绝读取硬件型号和缓存行信息,因此不宣称具体芯片或物理核心数。
sh
make -C labs/parallel clean
make -C labs/parallel test
./labs/parallel/benchmark 200000 4 64 5
./labs/parallel/false_sharing 4 1000000 5每个策略先热身一次,再计时 5 次,报告 min/median/max。计时包含线程创建与 join,不含输入初始化和正确性比较。没有设置线程亲和性或锁定频率,系统后台负载未隔离,策略采用固定顺序运行。结果说明本次观察,不是严格硬件基准。
实验一:工作量不均衡
work(i,N) 在 i 位于前 1/4 时执行 512 步确定性整数混合,其余执行 16 步。所有算术使用无符号整数,模回绕是预期语义。三种策略必须逐元素得到相同结果,不只比较一个总和;checksum 用于报告可读性,不能替代逐元素检查。
| 方案 | 线程/粒度 | min ms | median ms | max ms |
|---|---|---|---|---|
| serial | 1 | 66.810 | 67.217 | 72.753 |
| static 连续分块 | 4 | 65.201 | 65.370 | 65.688 |
| dynamic 领取分块 | 4 / 64 | 17.767 | 17.845 | 18.098 |
三者 checksum 都为 11481154863628559085,所有正式轮次逐元素比较通过。本次动态相对串行中位数约 3.77 倍,静态约 1.03 倍。这个差异符合“连续静态区间把重工作集中给一名 worker”的代码结构解释;没有采集每线程运行时间或硬件事件,不能进一步精确归因每毫秒。
原始摘要保存于 labs/parallel/results-scheduling.csv,编译器提示和正确性消息在相应 .log。这些文件由真实程序输出生成,没有手工填造。
实验二:相同原子操作,不同布局
4 个线程各增加自己的计数器 1000000 次,最终每槽都必须为 1000000,总计 4000000。packed 的槽位间距为 8 字节;padded128 的间距和对齐为 128 字节。
| 布局 | min ms | median ms | max ms |
|---|---|---|---|
| packed | 64.992 | 65.656 | 66.206 |
| padded128 | 2.093 | 2.141 | 2.212 |
本次差距明显,与降低共享缓存行竞争的解释一致,但该实验同时受原子实现、核心调度、缓存层次和处理器配置影响。没有硬件性能计数器证据,不能仅凭时间断定具体一致性事务数;也不能得出“所有计数器都要补齐 128 字节”的结论。若本来就由一个线程使用,padding 可能只浪费空间。
原始数据在 labs/parallel/results-sharing.csv,完整说明在 labs/parallel/VALIDATION.md。
正确性与性能测试分开
sh
make -C labs/parallel sanitizeASan/UBSan 功能测试已通过:N=1003、grain=17 验证非整除尾部;N=3、线程=8 验证空区间与任务少于线程;布局程序验证所有计数器。检测构建的时间不用于上述表格。若要重新测量,先 clean 再按普通优化构建,否则 Make 可能保留检测版本。
没有运行 ThreadSanitizer、官方 CS149 评分器、ISPC、Trainium2 或 GPU kernel。本机数据不能证明这些尚未运行部分通过。
三组下一步实验
粒度扫描。 固定 200000 项、4 线程,对 grain=1、16、64、256、4096 各测至少 5 次。预测曲线可能先下降后上升:太小受领取开销影响,太大受尾部失衡影响。实际曲线若不同,检查输入分布、计时精度和系统噪声,不强迫数据符合预测。
输入分布改变。 把重任务分散到每四项一个,保留每项计算不变;再做全部等重。比较静态与动态,区分“调度器快”与“调度器恰好适合该分布”。正确性参考必须同步修改,不可沿用旧结果。
线程数与规模。 用 P=1、2、4、8,分别测试小 N 与大 N。当前每轮都会创建线程,小 N 很可能更慢,这是包含启动成本的真实答案。若升级线程池,要明确基线是否也改变,分别报告一次性端到端与持续任务服务的时间。
实验报告模板
写清一个假设和一个可证伪预测,例如“重任务集中导致静态方案失衡;重排后静态应改善”。然后附程序版本、编译命令、机器已知信息、输入、重复次数、正确性判据、时间分布、解释和局限。不要只贴一个 speedup 数字。
自测:为何不在 timed region 里逐元素检查正确性?
检查本身会增加另一轮内存访问与计算,混入要研究的策略成本。可以在计时外严格检查每轮输出,既保证结果正确,也让口径清楚。若研究端到端带验证服务,应另立包含验证的指标。
自测:测到 dynamic 更快,能否声称已经证明瓶颈就是原子操作?
不能。dynamic 增加了原子领取,却改善了负载。对瓶颈的判断要用对照实验或 profiler 进一步拆分;总时间只是现象。
下一章:复习与迁移。