ISSUE-018-multi-unit-alpha-oscillation
ISSUE-018: PAXI LG 调度周期共振导致通信延迟随消息大小非单调
发现日期:2026-05-12
修复日期:2026-05-13
状态:已修复
类型:调度策略 bug
根因:chip-level round-robin counter 在 frames/unit % num_link_groups == 0 时与 CDMA 注入门控阶梯 earliest_ready 发生 phase lock
修复:PAXI LG 分配改用 hash-based ITLV(对齐硬件 spec SG2262 C2C方案.docx §"ITLV (Hash)"),spec 同步升级 G5-RC-Link v1.4.0
影响范围:所有通过 PAXI 的通信场景,N=2 单步 Ring 通信最显著(M=512/576/1024 等 case 出现反物理 M 增大延迟减小)
诊断历史:本 issue 经过 5 轮诊断。前 4 轮对根因的解读(ACK MERGE drain / housekeeping 污染 / CHS Transfer last_done_ns 太早 / multi-LG 仿真器内部同步爆发)均不完整或不准确。第 5 轮通过 RcFrameDone / SUBMIT / AckArrived debug trace 定位到 PAXI
lg_rr_counter与frames/unit的整除共振。前 4 轮诊断保留作推理过程记录。
问题现象
固定 N=2 Ring AllGather,扫描 M ∈ [32KB, 4MB],提取等效 α(= G5 总延迟 - β·wire),观察到 α 在 0.27~0.73 μs 之间非单调震荡:
| M (KB) | β·wire (μs) | G5 total (μs) | α_eff (μs) |
|---|---|---|---|
| 32 | 0.046 | 0.413 | 0.367 |
| 64 | 0.091 | 0.456 | 0.365 |
| 128 | 0.182 | 0.586 | 0.404 |
| 256 | 0.364 | 0.872 | 0.508 |
| 512 | 0.728 | 1.457 | 0.729 ← 局部峰 |
| 768 | 1.092 | 1.536 | 0.444 |
| 896 | 1.274 | 1.599 | 0.325 |
| 960 | 1.365 | 1.659 | 0.294 ← 局部谷 |
| 1000 | 1.422 | 2.057 | 0.635 ← 跳起 0.34 |
| 1024 | 1.456 | 2.121 | 0.665 ← 局部峰 |
| 1100 | 1.564 | 1.873 | 0.309 |
| 1152 | 1.638 | 1.943 | 0.305 |
| 1180 | 1.678 | 1.955 | 0.277 |
| 1500 | 2.133 | 2.721 | 0.588 ← 又跳起 |
| 2048 | 2.913 | 3.449 | 0.536 |
| 4096 | 5.825 | 6.105 | 0.280 |
α_eff 极差 0.46 μs,远超合理误差范围(spec v1.1.2 α_start = 0.30 μs,理论上应该是常量)。
物理上 α 与消息大小无关(startup latency 不应随 M 变化),所以这种震荡是建模产生的人工现象,不是真实硬件行为。
实际影响:TPS186 baseline doc 中 MLA AG(M=1.125 MB → α_eff=0.305)和 MLA RS(M=1.0 MB → α_eff=0.665)恰好踩到曲线相邻的"谷"和"峰",造成 G5/Math 比值看起来差异巨大(0.83x vs 1.15x),实际是 M 选择的偶然,与 AG/RS 本身建模无关。
调查过程
-
[实验]
scripts/debug_g5_rs_vs_ag.py:对同一 M 分别跑 AG 和 RS,G5 输出完全一致(total_sim_time 字节对齐)。→ 排除"RS 特有的 reduce-after-receive 路径"假设。AG 和 RS 在 G5 expand 路径完全等价,都调用helpers::expand_ring_steps。 -
[查代码]
perfmodel/evaluation/g5/src/collective/allgather.rs:12-17和reducescatter.rs:11-16都调用同一个 helper,CDMACommand 生成结构相同。 -
[查代码]
perfmodel/evaluation/g5/src/collective/expand.rs:104-109multi-channel 分包逻辑:let c = total_cdma_units.max(1);
if c > 1 {
op.data_bytes = (op.data_bytes as f64 / c as f64).ceil() as u64;
}total_cdma_units = dies_per_chip × cdmas_per_die = 2 × 4 = 8(configs/chips/SG2262.yaml:61,79),所以 SG2262 默认每条 CommOp 平分到 8 个 Unit 并发。 -
[假设 1] 震荡来自 PAXI rsp 通道的 ack tail。 → 排除。trace 验证:tail 区间事件 = link_busy/idle on
link_cX_cY_rsp,长度 ~500ns,但在所有 M 下都存在,不能解释震荡的高低差异。 -
[假设 2] 震荡来自 RS 特有的 reduce 计算延迟。 → 排除。同 M 下 AG=RS 完全相等。
-
[假设 3] 震荡来自 multi-CDMA Unit 分包与 PAXI 包边界的耦合。 → 确认根因。
scripts/debug_g5_units_isolation.py测试:强制cdmas_per_die=1, dies_per_chip=1(total_cdma_units=1)后,α_eff 在所有 M 下稳定在 0.35~0.37 μs,没有震荡。仅当 total_cdma_units=8(默认)时出现震荡。M (KB) α @ 1 unit α @ 8 units 256 0.364 0.508 512 0.364 0.729 960 0.364 0.294 1024 0.364 0.665 1180 0.355 0.277 1500 0.354 0.588
根因分析
multi-Unit 并发场景下,每个 CDMA Unit 拿到 data_bytes / 8 字节,独立按 PAXI max_payload = 1344 B 分包:
configs/chips/SG2262.yaml:99: max_payload: 1344(每 RC Link 包最大数据字节)
每 Unit 包数 = ceil(unit_bytes / 1344),最后一包字节数 = unit_bytes - (N_pkt - 1) × 1344。
8 个 Unit 同时发送时,critical path 由"最慢完成的 Unit"决定。当不同 Unit 的"最后小包"在时间上错落分布到 link 上时,会产生追加占用尾巴——尾巴长度与最后小包大小、相邻 Unit 完成时刻的相对位置有关,呈非单调变化。
具体表现为 α_eff 在 M 跨过某些"分包边界 + Unit 同步边界"组合时跳跃。简单的 M / 8 / 1344 余数分析不足以完全预测峰谷位置——还涉及 PAXI line_rate(tx.line_rate_per_lg_bytes_per_ns: 50)、credit_size、datapath_bytes、Unit 启动错位等多个因素的耦合。
Spec / 文档依据
项目 Spec(docs/specs/互联通信延迟数学建模.md v1.1.2):
§3.3 α_start = ddr_r + ddr_w + 2·(noc + stack) = 0.30 μs,与消息大小 M 无关。
互联通信文档(docs/interconnect/04-集合通信/07-all-gather.md line 100):
$T_{\text{Ring-AG}} = (N-1)\alpha + \frac{(N-1)M}{N\beta}$
理论模型中 α 是 per-step 常量,不应随 M 震荡。G5 的 α_eff 震荡是仿真器多通道分包行为产生的,模型层无对应建模。
推断:
- multi-Unit 分包是 SG2262 实际硬件行为(8 个 CDMA Unit 物理独立),所以"Unit 分包导致 packet 不对齐"在硬件上也确实存在
- 但硬件上 PAXI line_rate / credit pool / scheduler 可能比 G5 当前建模更平滑(如硬件 datapath 流水线掩盖小包尾巴),G5 当前的离散事件建模可能放大了这种边界效应
未解决:
- 真实 SG2262 硬件实测在 M=512KB / 1024KB 是否也出现类似的 α 跳变?需要硬件测量数据对照
- 哪个具体边界(分包余数、credit 同步、Unit dep chain)权重最大?需要进一步 trace 拆解
业界对比(暂缺)
NCCL 不暴露 per-CDMA-Unit 延迟分布,业界对 multi-engine 分包是否产生 α-jitter 没有公开实测数据。后续若获得 SG2262 实测,再补对照。
解决方案
备选方案
| 方案 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| A | 维持现状,doc 注明震荡现象,建议工程用多个 M 平均或 M > 2MB(α 占比小)做对比 | 不改代码,工作量最小 | doc 表读起来 G5/Math 比值仍然有锯齿 |
| B | 在 expand.rs 改 multi-Unit 分包逻辑:用 round-robin packet 级分配代替"先切大块再各自分包" | 平滑 α | 需要重写 multi-channel 逻辑,可能影响其他场景(hierarchical / AllToAll) |
| C | 修 PAXI 模型让最后一包不产生 tail(如包大小 padding 到 max_payload 整数倍) | 物理上更接近硬件 datapath 流水线行为 | 改 PAXI 层,影响所有原语;padding 也会改变 wire bytes 统计 |
| D | 给 baseline 测试加 multi-Unit 调度的"平均化"层(同 M 跑多次,固定 jitter 平均),仅用于 baseline 报告 | 不动 G5 核心 | 需要新模块,且不解决问题本质 |
采用方案
采用 A(先记录,不修代码):
理由:
- 震荡只发生在 N=2 单步通信场景,N≥4 多步 pipeline 会摊薄
- SG2262 实际硬件是否有同样的边界效应未知,盲改 G5 可能脱离真实硬件
- doc 表只是 baseline 对比,加一段说明即可
后续触发条件升级到 B/C:
- 拿到 SG2262 实测发现硬件没有这种震荡 → 走 C(修 PAXI 模型让仿真匹配实测)
- 拿到 SG2262 实测发现硬件有但幅度不同 → 走 B(调整分包策略)
- 业务需要 N=2 单步通信的 G5 稳定结果 → 走 B 或 D
涉及文件(A 方案):
| 文件 | 改动说明 |
|---|---|
docs/validation/通信原语评估-TPS186/comm-primitive.md | §差异分析 增加一段,说明 N=2 单步通信下 G5 α_eff 随 M 震荡是 multi-Unit 分包效应,不是 RS/AG 建模差异 |
验证
复现命令:
# 完整 M 扫描(确认震荡)
python scripts/debug_g5_m_sweep.py
# 1 unit vs 8 unit 对比(确认 multi-Unit 是根因)
python scripts/debug_g5_units_isolation.py
# AG vs RS 在同 M 下完全等价(确认不是 RS 特有建模问题)
python scripts/debug_g5_rs_vs_ag.py
预期:
- M 扫描:α_eff 在 0.27~0.73 μs 之间锯齿震荡
- 1 unit:α_eff 在所有 M 下稳定 ~0.36 μs(无震荡)
- AG vs RS:同 M 下 total_sim_time 完全相等
遗留问题
- 拿到 SG2262 真实硬件实测后,对比 α_eff 是否同样震荡。决定是否升级到方案 B/C
- 进一步用 trace 拆解到具体 PAXI 事件级别,找哪个边界(分包余数 / credit / scheduler)权重最大
- 评估对 N≥4 多步通信的影响(推测被 pipeline 摊薄到不可见,但需要扫一次验证)
后续诊断(2026-05-12)
为决定是否走 frame-level 重构(方案 C),跑了一组参数扫描诊断(scripts/debug_jitter_root_cause.py),对照 4 个变体在 M ∈ {512KB, 960KB, 1024KB, 1180KB} 下的 α_eff:
| 变体 | α 范围 (μs) | α 极差 | 解读 |
|---|---|---|---|
| baseline (8 LG, poll=4ns) | 0.28 ~ 0.73 | 0.45 | 已知 jitter |
| variant_A (1 LG) | 4.93 ~ 10.9 | 5.98 | 单 LG 整体拖 8×,无法横向对比 |
| variant_B (poll=0.001ns 准 per-packet ACK) | 0.27 ~ 0.73 | 0.45 | 与 baseline 完全相同 |
| variant_C (poll=1000ns 大窗口 ACK 聚合) | 0.63 ~ 1.67 | 1.04 | 比 baseline 还大 2.3× |
@tbl-issue-018-diag jitter 根因参数扫描对照
关键结论
- ACK MERGE 聚合粒度不是 jitter 来源 —— variant_B (poll=0.001ns,准 per-packet ACK) 与 baseline (poll=4ns) 极差完全相同。这证伪了 "frame-level ACK 聚合可消除 jitter" 的假设
- variant_C 反向:ACK 窗口越大反而恶化 jitter,说明现 baseline 的 poll=4ns 已经接近最优 ACK 聚合度
- multi-LG 路径影响无法独立验证:单 LG 整体拖慢 8×,无法分离"调度散布"与"带宽损失"的贡献
真因剩余假设
- 推测:multi-LG 间 packet 到达时刻随 M 跳跃,max(LG_completion) 在某些 M 处触发"最后一 LG 拖尾",造成 α_eff 震荡
- 真因可能与 frame-level 重构正交,frame-level 后 jitter 可能仍存在
- 需要进一步 trace 拆解 multi-LG 完成时刻分布才能确认
对方案 C 的影响
ISSUE-018 当前继续走方案 C(frame-level 重构),但重新校准目标:
- 主要目标:spec 对齐(PAXI SUE2.0 UserGuide §2.1.1 Flit Rules 契约)
- 次要目标:jitter 缓解(不再作为验收 KPI,因为诊断显示 frame-level 未必能消除)
frame-level 重构进度跟踪在 docs/plans/2026-05-12-g5-frame-level-event-refactor.md,plan §目标已同步去除 "α_eff 稳定到 0.30±0.05" 兜底要求。
jitter 残余问题在 frame-level 重构完成后单独追踪(必要时开新 ISSUE-020 跟 multi-LG 调度散布)。
第三轮诊断(2026-05-12,frame-level 重构后)
frame-level 重构 (commit bc2a716) 完成后重跑 scripts/debug_g5_m_sweep.py:α_eff 在 0.27~0.73 μs 之间继续震荡,与重构前的范围完全相同。验证了第二轮诊断的预判:frame-level 重构对齐了 PAXI spec,但未消除 jitter。
多变量诊断矩阵
跑 scripts/debug_jitter_v2.py 同时控制 num_link_groups 和 cdma_units:
| 配置 | α 范围 (μs) | α 极差 | 解读 |
|---|---|---|---|
| 1 unit, 1 LG | -0.84 ~ +0.09 | 0.93 | 单调(带宽不足占主导,无 jitter) |
| 1 unit, 8 LG | 2.05 ~ 8.12 | 6.07 | 单调(仍无 jitter,但单 unit 提交 OST 限制) |
| 8 unit, 1 LG | -0.84 ~ +0.09 | 0.93 | 完全消除 jitter(与 1u-1LG 几乎相同) |
| 8 unit, 8 LG | 0.28 ~ 0.73 | 0.45 | baseline,已知 jitter |
@tbl-issue-018-diag2 多变量诊断矩阵
关键证伪:1 LG 配置下 jitter 完全消失,8 LG 才表现 jitter。multi-Unit 本身不是根因,真因在 multi-LG 路径。
Wire 带宽不是 jitter 主因
scripts/debug_inf_bw.py 把 C2C bandwidth 从 400 GB/s 调到 40 000 GB/s(100×):
| M (KB) | G5@400 (μs) | G5@40000 (μs) | Δ (ns) |
|---|---|---|---|
| 512 | 1.457 | 1.454 | 3 |
| 576 | 1.143 | 1.089 | 54 |
| 1024 | 2.121 | 2.118 | 3 |
| 1180 | 1.955 | 1.874 | 81 |
@tbl-issue-018-diag-bw c2c 带宽 100x 对照
100× 带宽后 jitter 极差从 1.249 → 1.252,几乎不变。证伪 "PhyLink 串行化是 jitter 主因" 的假设。
真因定位:仿真器内部 ACK drain 时间
scripts/debug_recv_path.py 比较 multi_chip.kernel.total_sim_time_ns(仿真结束)vs max(cdma_unit.last_done_ns)(所有 unit transaction 完成最大值):
| M (KB) | frames/unit | mod 8 | total_sim (ns) | cdma_max (ns) | gap (ns) |
|---|---|---|---|---|---|
| 256 | 4 | 4 | 872 | 551 | 321 |
| 480 | 8 | 0 | 1393 | 775 | 618 |
| 512 | 8 | 0 | 1457 | 807 | 650 |
| 576 | 9 | 1 | 1143 | 871 | 272 |
| 640 | 10 | 2 | 1256 | 935 | 321 |
| 960 | 15 | 7 | 1659 | 1255 | 404 |
| 1000 | 16 | 0 | 2057 | 1295 | 762 |
| 1024 | 16 | 0 | 2121 | 1319 | 802 |
| 1100 | 18 | 2 | 1873 | 1395 | 478 |
| 1180 | 19 | 3 | 1955 | 1475 | 480 |
| 1500 | 24 | 0 | 2721 | 1795 | 926 |
@tbl-issue-018-diag-gap total_sim vs cdma_max 时间差
强规律:
- cdma_max 单调递增(无 jitter)—— 反映真实 critical path
- gap 跳变:
frames_per_unit % 8 == 0时 gap 跳高 200~500 ns- frames=8 → 9 时 gap 跳跃 -378 ns
- frames=16 → 15 时 gap 跳跃 -398 ns
- frames=24 → 19 时 gap 跳跃 -446 ns
每次 frames/unit 跨过 8 的倍数边界,gap 跳变约 400 ns。
根因机制(已定位)
comm_primitive_pipeline.py:70 用 multi_chip.kernel.total_sim_time_ns 作为通信延迟:
sim_time_ns = stats.get("multi_chip.kernel.total_sim_time_ns")
但 total_sim_time_ns 不是"通信完成时刻",是最后一个 simulation event 的时间戳(包括 CDMA transaction 完成之后的 ACK MERGE poll 残余事件、credit 返还事件、retry timer arm 等)。
当 8 LG 在最后一帧完全同步完成(frames/unit % 8 == 0)时:
- 8 LG 同时进入 RX 端 ACK MERGE poll 队列
- ACK 返回 chip0 后通过 RSP VC 串行化(轻微贡献)
- 8 个 retry_timer 同步 arm(500ms timeout)
- credit 返还事件链 + tcredit 反向通信路径
- 这些事件全部串行 drain 到 simulation queue 末尾,造成 ~400 ns 额外尾巴
而 frames/unit % 8 != 0 时,LG 间最后一帧错开完成,drain 事件分散,每个事件单独 drain 不集中,gap 小。
修复方向(备选)
| 方案 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| P1 | comm_primitive_pipeline._build_comm_only_result 改用 max(cdma_unit.last_done_ns) 代替 total_sim_time_ns 作为通信延迟 | 改一行 Python,jitter 立即消失,与真实硬件 critical path 对齐 | 改变 G5 通信延迟语义,需所有 caller 复测;ACK drain 不计入是否符合实际硬件行为有待 spec 确认 |
| P2 | 仿真 kernel 增加"通信完成事件"标记,total_sim 只到该事件为止;drain 残余事件不算 | 语义清晰 | 需要修改 Rust SimKernel,涉及面大 |
| P3 | 接收侧 ACK MERGE / credit return / retry timer 事件不进 kernel 总时间统计 | 治本 | 需要分类事件,区分"应用层"和"内部" |
| P4 | 维持现状,仅文档说明(已采用方案 A) | 不改代码 | 用户/前端看到的延迟仍有 jitter |
推荐方案
推荐 P1:
- cdma_unit.last_done_ns 是 PAXI
on_remote_done时间戳,对应 CHS 模式下应用层认为 transaction 完成的时刻(与 SG2262 spec 中 CHS = post-write early-resp 语义一致) - ACK MERGE poll 残余事件是仿真器内部 drain,不影响真实芯片的应用进度
- 改动局部(
comm_primitive_pipeline.py一处),易于回滚 - 对 N≥4 多步通信场景:cdma_max 仍然是 max across all units,反映 critical path(不影响 pipeline)
验证:用 P1 后重跑 sweep,预期 α_eff 极差 < 0.05 μs,cdma_max 与 Math 模型偏差稳定。
遗留问题(P1 之后)
- 多步通信场景下 cdma_max 与 total_sim 的差异行为需要验证(N=4 ring 是否仍 ACK drain 滞后)
- 对 Math 模型 alpha (startup) 的对齐:cdma_max 已不含 ACK 路径,可能 cdma_max 表示的 α 略小于 Math 的 0.30 μs
- 是否需要新开 ISSUE-020 跟踪"仿真器事件 drain 语义"作为独立改造
第四轮诊断(2026-05-13,P1 修复回滚)
实施 P1 后跑 TPS186 baseline 发现 G5 延迟显著低于 Math(new/Math ≈ 0.5x0.7x,且数值约等于 CDMA datapath 释放时刻),与 ISSUE-016 时观察到的 0.881.25x 范围(基于 total_sim_time)完全不一致。复核 spec 后定位 P1 的根本误解:
spec 依据:docs/specs/G5仿真建模/G5-CDMA建模设计规格.md v1.2.0 §sync_id 更新时机 表:
| 指令 | CHS 模式完成时机 | CFS 模式完成时机 |
|---|---|---|
| Transfer | CdmaLocalComplete (datapath_end + 5ns) | DataArrived (远端 ACK) |
| Send | DataArrived (远端 ACK) | DataArrived (远端 ACK) |
而 helpers.rs:5,24,40,78,136,167 显示 Ring AG/RS expand 用的是 CDMAOpType::Transfer(不是 Send)。代码链路 multi_chip.rs:193-199 CdmaLocalComplete 调 on_remote_done 设置 last_done_ns。
结论:CHS 模式 Transfer 下 last_done_ns 设置在 sender CDMA datapath 释放(数据交给 PAXI)时刻,不包含:
- wire transfer 传输时间(数据从 sender PAXI → receiver)
- RX 端 ACK MERGE 累计 + ACK 返回路径
- DataArrived 到 sender 的真正 application-visible 完成
P1 用 max(last_done_ns) 把以上时间全部当成 housekeeping 砍掉,砍掉的是真实通信传输时间,不是仿真器噪声。
数值验证:M=1.125 MB N=2 AG,per-chip wire = M/2 = 0.5625 MB,每 chip 8 unit 并行注入,每 unit ≈ 0.07 MB / 64 GB/s = 1.099 μs + CDMA startup 290 ns + 5 ns ≈ 1.394 μs。P1 后 G5 = 1.447 μs ← 完美对应 sender CdmaLocalComplete 时刻,没有计入任何 wire transfer + ACK 路径。
P1 已回滚(2026-05-13),恢复使用 kernel.total_sim_time_ns 作为通信延迟。
重新审视:震荡来源 vs application-visible 完成时刻
回滚后重新思考问题分层:
A. 震荡来源(业界证据 + 物理推导):
- α 物理来源 = DDR R/W + NoC + PHY serialize,与 M 无关。真实硬件 fixed-algorithm jitter < 50 ns(NCCL arxiv 2507.04786 "variance is very low")
- 仿真器观察到的 200~500 ns 锯齿主要是 multi-LG 在 frames/unit % 8 == 0 时仿真器内部同步推进(缺失真实硬件 PHY clock skew / router queue jitter)→ 下游事件爆发式调度
B. application-visible 完成时刻的真值:
- 既不是
last_done_ns(CHS Transfer 太早 — 只到 sender datapath 释放) - 也不一定是
total_sim_time_ns(可能太晚 — 含 sender 完成后的真正 housekeeping events) - 真值 ≈ receiver 收到全部数据的时刻(即 RX 端最后一个 packet receive_packet 完成时)
C. 当前 total_sim_time_ns 的真实组成:
- sender CdmaLocalComplete (~1.4 μs for M=1.125MB AG)
-
- wire transfer + ACK MERGE + ACK 返回 sender
-
- DataArrived event 处理
-
- 各种 housekeeping (CdmaWake/RcLinkWake/CreditReturn drain)
ISSUE-016 时基于 total_sim_time_ns 跑出 G5/Math ≈ 1.0x,说明 total_sim_time_ns 数值上接近 application-visible 真值(housekeeping 量级可能小于估计)。
第五轮诊断(2026-05-13,定位 PAXI LG 调度 Bug)
P1 回滚后重新审视 total_sim_time_ns 的语义。trace 验证它 = max(DataArrived 时刻) = sender 收到最后一个 ACK 的时刻 = application-visible 完成时刻,没有 housekeeping drain。
那么 ISSUE-018 报告的 200~500 ns 震荡到底来自哪里?
反物理证据
固定算法 N=2 Ring AG,扫描 M:
| M (KB) | per-unit packets | frames/unit | tot_sim (ns) | 现象 |
|---|---|---|---|---|
| 512 | 32 | 8 | 1457 | 16 cmd 错开 (跨度 577 ns) |
| 576 | 36 | 9 | 1143 | 16 cmd 同步 (跨度 2 ns) |
| 1024 | 64 | 16 | 2121 | 错开 (跨度 553 ns) |
M=576 比 M=512 完成早 314 ns,但 M=576 多 24 个 packet × 3.44 ns/packet = 多 82 ns 真实 wire time。物理上不可能 M 变大延迟变小——这是仿真器的反物理建模 bug。
Trace 数据:LG 分配的周期共振
scripts/debug_p1_validation.py 加 RcFrameDone / SUBMIT / AckArrived debug print,trace t=290 chip=0 8 个 unit 的 frame_seq=0 分配:
M=512 (frames/unit=8):8 个 unit 的 frame_seq=0 全部分配到 lg=0
[DBG-SUBMIT] t=290 chip=0 lg=0 txn=0 frame_seq=0 ...
[DBG-SUBMIT] t=290 chip=0 lg=0 txn=1 frame_seq=0 ...
[DBG-SUBMIT] t=290 chip=0 lg=0 txn=2 frame_seq=0 ...
... (8 个 unit 全部 lg=0)
M=576 (frames/unit=9):8 个 unit 的 frame_seq=0 分散到 lg=0~7
[DBG-SUBMIT] t=290 chip=0 lg=0 txn=0 frame_seq=0 ...
[DBG-SUBMIT] t=290 chip=0 lg=1 txn=1 frame_seq=0 ...
[DBG-SUBMIT] t=290 chip=0 lg=2 txn=2 frame_seq=0 ...
... (8 个 unit 散到 lg=0~7)
根因机制
paxi.rs::PAXIBridge 用 chip-level shared lg_rr_counter,每次 segment submit 时 counter = (counter + 1) % num_link_groups。
- 当 每 unit submit 的 frame 数 % num_link_groups == 0(M=512: 8 frames/unit, num_lg=8),每 unit 结束时 counter 恰好转回起点
- 所有 unit 的 frame_seq=k 全部落到同一 LG_k
- 叠加 CDMA 注入门控阶梯 earliest_ready_ns(每 segment 间隔
MPS / 注入带宽= 4096/64 = 64 ns) - LG_(N_LG-1) 集中接收所有 unit 的最后一帧,起跑线被推后 (N_LG-1) × 64 ns = 448 ns,单独决定 critical path
frames/unit 不整除 num_link_groups 时,counter 每轮偏移一格,frame 在 LG 上 cross-unit 打散。
完整设计原理(频谱分析、phase lock 数学模型、3 相电网类比、业界 ECMP/RSS/NVLink 对照)见 G5-RC-Link 传输层设计规格 v1.4.0 §为什么 hash 优于 round-robin。
硬件 spec 依据
SG2262 C2C 方案明确:
"多个 c2c Link 之间做 ITLV (Hash),从而分散带宽" "同 src macid + 同 dst macid + 同 hash value 时,交换机路径相同且严格保序"
硬件 spec 要求 hash-based ITLV,G5 实现的 chip-level round-robin counter 违反 spec。这是 ISSUE-016 修复 LG 不均衡时引入的副作用(当时为绕开 pick_least_busy_lg + try_arbitrate 互相干扰的 bug 改成纯 RR,但没对齐硬件 spec)。
解决方案(已采用,已修复)
修复:PAXI LG 分配从 chip-level round-robin counter 改为 hash(txn_id, frame_seq, qp_id) % num_link_groups,使用 splitmix64 雪崩 hash。
Spec 改动
| 文件 | 改动 |
|---|---|
docs/specs/G5仿真建模/G5-RC-Link传输层设计规格.md v1.3.1 → v1.4.0 | §ITLV 分发机制 默认策略改为 hash_itlv,新增子段 §为什么 hash 优于 round-robin(设计原理)含频谱分析 + phase lock 数学模型 + 业界对照 |
代码改动
| 文件 | 改动 |
|---|---|
perfmodel/evaluation/g5/src/tier6/paxi.rs | 加 mix64 + hash_lg splitmix64 实现;submit 和 feed_rc_link 中的 closure 用 hash 替代 lg_rr_counter;移除 lg_rr_counter 字段 |
perfmodel/evaluation/g5/src/tier6/paxi_core.rs | submit / drain_pending / create_segments 的 closure 签名 FnMut() -> u8 → FnMut(u64, u32, u16) -> u8,传入 (txn_id, frame_seq, qp_id) |
perfmodel/evaluation/g5/src/tier6/paxi.rs tests | test_least_busy_lg_dispatch 改名 test_hash_lg_dispatch,断言反映 hash 非完全均匀但应打散到 ≥2 LGs |
修复效果
反物理消失(M sweep tot_sim 完全单调):
| M (KB) | 旧 round-robin | 新 hash |
|---|---|---|
| 256 | 872 | 1034 |
| 480 | 1393 | 1393 |
| 512 | 1457 | 1434 |
| 576 | 1143 ← 反物理跌 | 1502 ✓ |
| 640 | 1256 | 1638 |
| 1000 | 2057 | 2114 |
| 1024 | 2121 | 2114 |
| 1100 | 1873 ← 反物理跌 | 2132 ✓ |
| 1180 | 1955 | 2250 |
@tbl-issue-018-fix-monotonic ISSUE-018 hash-based ITLV 修复前后 M sweep 对比
TPS186 baseline 周期共振虚高/虚低消除:
| Case | 旧 G5 | 新 G5 | 变化 |
|---|---|---|---|
| MLA RS @ 1 MB | 2.471 μs | 2.114 μs | -357 ns(之前周期共振虚高) |
| DSA topk_AG @ 32 KB | 0.763 μs | 0.483 μs | -280 ns(同样虚高) |
| MLA AG @ 1.125 MB | 2.293 μs | 2.250 μs | -43 ns |
| Dense AR @ 0.4375 MB | 1.923 μs | 2.054 μs | +131 ns |
测试:Rust 212 passed / Python 23 passed
遗留问题(已不在本 ISSUE 范围)
- CHS Transfer 完成时刻语义:CHS 模式 Transfer 的
last_done_ns设在 CdmaLocalComplete(sender datapath 释放),不含 wire transfer + ACK 路径。若需测量"receiver 看到全部数据"的精确时刻,需在 Rust kernel RX 端加 stats 字段。当前kernel.total_sim_time_ns在 N=2 Ring AG 场景下 = max(DataArrived) ≈ sender 收到 ACK 时刻 ≈ application-visible 完成时刻,作为代理可用。视后续需求决定是否开新 ISSUE。 - LLM pipeline
adapter.py:144用total_sim_time_ns兜底 SimRecord(ISSUE-010 修复路径),未受 LG 调度 bug 影响。 - 微抖动(< 50 ns):hash 后仍存在的统计性碰撞导致的微小 jitter,与真实硬件 PHY jitter 量级相当,作为已知 limitation 接受。