ISSUE-034 — G5 alltoallv 仿真帧静默丢失 + 提前终止 (busbw 超物理上限的真因)
| 字段 | 值 |
|---|---|
| 日期 | 2026-06-04 |
| 状态 | 已修复 (per-LG 容量取货 + 守恒护栏 + 守恒/单测;全量 v5 重跑 0 超限;code-review APPROVED) |
| 类型 | 仿真正确性 bug — segment 在 CDMA→PAXI→LG 流转中静默丢失,导致仿真在数据传完前终止 |
| 影响范围 | G5 feed_rc_link / take_segments / dispatch_segments 全局 vs per-LG 容量口径错配;topo-routing-comm-eval 全部数据集 (v2/v3/v4) 作废,所有 latency/busbw 基于残缺传输 |
| 关联 | ISSUE-031 (push-driven 重构引入本回归);ISSUE-033 (busbw 超 400 是本 issue 的症状,per-LG SerDes cap 物理正确予以保留) |
结论速览
根因:feed_rc_link 用全局 slot 容量 (8 LG free_slot 之和) 决定 take_segments 取多少 segment; segment 按 hash_lg 投到特定 LG;该 LG slot 满时 submit_frame 返回 false + diag_slot_full++,但 dispatch_segments 忽略返回值 → segment 既出队 (seg_total--) 又没进 LG → 永久丢失 → 丢失帧不再产生事件 → 事件队列提前空 → 仿真提前终止 → total_time 偏小 → busbw = C.sum/(N×total_time) 虚高破物理上限 400。
铁证:diag_slot_full = 70339 与守恒护栏 unsent_frames = 70339 逐一相等 (single-switch S1 ep32 tok256)。
因果链:
帧在 CDMA→PAXI→LG 流转中静默丢失 (root cause)
→ 仿真在 45-71% 数据传完时事件队列空、提前终止
→ total_time 系统性偏小
→ busbw = 全量 C.sum / 偏小 total_time > 400 (ISSUE-033 观察到的"症状")
问题现象
topo-routing-comm-eval 数据集 per-chip busbw 超物理上限 400 GB/s (SG2262 8 LG × 50)。v4 (exp38) 全量 2779 cell 中 203 个 bus_bw_gb_per_s > 400, max 439.9。最初定性为"chip 出向超速" (ISSUE-033),实为表象。
调查过程
- [查 busbw 公式]
busbw = total_bytes/(N×total_time)(run_alltoallv.py:539),分子C.sum()对角线为 0,公式与口径均正确,非公式 artifact。 - [Rust 探针] dump 所有 chip 每帧 (chip lg now done wire):单 LG 严格 50.0 GB/s (overlap=0),每 chip 物理出向 ≤396 < 400。物理串行完全正确,per-LG cap (ISSUE-033) 生效,非超速。
- [守恒护栏]
multi_chip.rs::collect_results加incomplete_txns/unsent_framesstat:仿真结束所有 64 chip 各 31 txn 未完成 (acked < total_frames),而waitack=0 / seg_q=0 / free_slots=4096(PAXI 全空) → 帧"凭空消失"。 - [对比 switch 转发] single-switch 所有数据必过 s0,应转发 100%;实测 ep32 仅 45%、ep64 仅 71% (CDMA 发起均 100%) → 数据未传完仿真即结束。busbw 越虚高、转发越不完整 (负相关)。
- [diag 计数器] 聚合 RC_TX diag:
slot_full=70339 == unsent_frames=70339→ 丢帧点 =submit_frameslot 满。 - [查 dispatch_segments]
paxi.rsfor seg in segments { self.tx_lgs[..].submit_frame(..) }忽略 bool 返回值 → 丢帧静默。 - [git 考古] push-driven SegmentDispatch 重构 (ISSUE-031, commit 3899aff) 把 per-LG 容量判断写成全局求和
rc_link_available_capacity。
根因分析
| 口径错配 | 问题 |
|---|---|
| 全局容量 vs per-LG 分配 | feed_rc_link 按"8 LG 总空位"取货,但货按 hash_lg 分到指定 LG;单 LG 装不下 (即使全局够) |
dispatch_segments 忽略返回值 | submit_frame 返回 false (丢帧信号) 被丢弃,segment 既不退回 segment_queue 也不重试 |
ep=32 流量集中 → hash_lg 分布更偏 → 单 LG 过载更频繁 → 丢更多帧 → 传更少 → busbw 更高,解释 ep32(45%) 比 ep64(71%) 严重。
Spec / 文档依据
| 来源 | 内容 | 与本 issue |
|---|---|---|
| G5-RC-Link 传输层设计规格 line 228 | Slot 池 per-LG 独立 (每 LG TYPE1_OST) | 容量判断应 per-LG,实现用了全局求和 |
| 同 line 240 | "Slot 耗尽时新包入 segment_queue 排队,ACK 释放 Slot 后由 feed_rc_link 自动 drain" | slot 满留队列不丢帧 — 实现违反 (take 出又丢) |
spec 设计正确,实现违反 spec。spec 不改设计,仅澄清 "feed 按 per-LG 容量取货" (line 240,行为描述,不含函数名)。
解决方案
采用:feed_rc_link / take_segments 改为按 per-LG slot 容量取货 (回归 spec line 228/240)。
paxi_core.rs(核心):take_segments/drain_pending/submit签名slot_budget: usize→lg_budgets: &mut [usize]。新增take_from_txn_queuehelper,三类处理:未就绪 (记 earliest_wake + break,依赖 earliest_ready 单调) / 就绪但该 LG 满 (跳过留队列,继续看后续) / 就绪且 LG 有空位 (取走扣预算)。keptVecDeque 保同一 LG 内 seg_idx 递增序 (PSN 连续)。paxi.rs:feed_rc_link采样一次lg_budgets, take 与 drain 共享同一被扣减的&mut,禁止重新采样 (否则两段重复发放预算 → 超发 → 复发丢帧)。dispatch_segments对submit_frame返回值加debug_assert!。删死函数rc_link_available_capacity。multi_chip.rs(护栏,正收益):collect_results输出incomplete_txns/unsent_framesstat — release 下兜底,提前终止不再静默。
关键算法不变式:earliest_wake 恒 = 所有未就绪 segment 的最小 earliest_ready (只有未就绪段记 wake;满 LG 跳过的是就绪段,earliest_ready≤now 不污染 wake),故不会算晚 wake → 不复现提前终止;同时保留 "未就绪 break" 剪枝,不回退 ISSUE-031 的 O(P²) 优化。
涉及文件:perfmodel/evaluation/g5/src/tier6/paxi_core.rs, tier6/paxi.rs, top/multi_chip.rs(+tests.rs)。Plan: docs/plans/2026-06-04-alltoallv-frame-drop-fix.md。
验证 (Acceptance Criteria)
- ✅ 守恒测试
test_alltoall_data_conservation_no_frame_drop(16 chip × 3MB/pair alltoall):修复前红 (incomplete_txns=2, unsent_frames=34),修复后绿 (incomplete_txns==0)。code-review revert 实验复证:改回全局口径即红 (submit_frame slot 满返回 false)。 - ✅ per-LG 单测:
test_take_segments_per_lg_skip_full(满 LG 跳过 + seg_idx 递增) /test_take_segments_wake_unaffected_by_full_lg_skip(earliest_wake 不被满 LG 跳过干扰,wake=未就绪真实最小值)。 - ✅ cargo test: 364 passed; debug build 守恒测试
debug_assert不触发 (per-LG 容量与 submit_frame 同口径,ok==true恒成立)。 - ✅ 全量重跑 v5-framefix (exp39) 2779 cell: busbw>400 = 0, max 339.4 (v4 为 203/2779, max 439.9)。busbw 全面下修因数据真传完、total_time 反映真实传输。
- ✅ code-review (opus, in-flow): APPROVED, 0 P0/P1/P2, 2 P3 (docstring / saturating_sub) 已修。
Lessons Learned / Prevention
- 测试盲区是 root cause 长期潜伏的原因:修复前 361 测试只断言
sim_time > 0.0("仿真跑完且时间为正"),零个数据守恒 / 事务完成断言。提前终止满足sim_time>0静默通过。→ 护栏 stat + 守恒测试永久防回归。 - 诊断信息 ≠ 断言:
dump_post_sim_diagnostics早已收集core_txns未完成,但只在G5_DEBUGeprintln,从不 panic,测试看不到。诊断必须升级为可断言的 stat。 - 生产脚本应 check
incomplete_txns==0: validation 脚本每 cell 验收此 stat,残缺数据不再静默进 DB (呼应 validation-data-persistence)。 - 症状定性要追到物理层:ISSUE-033 把 "busbw 超 400" 定性为"chip 出向超速",探针证明单 LG 物理严格 50、单 chip <400,推翻症状定性,才挖到真因。
遗留问题
- 结论修正:v5 干净数据下,拓扑相对排序稳定 (single-switch > torus > fat-tree > ring),但路由第一名 dor/dmodk 互换 (v5: dor 292.7 > dmodk 275.2; v4: dmodk 368.2 > dor 363.5,两者接近、丢帧噪声下翻转)。report.md KA1/KA2/KA3 需基于 v5 重写,路由结论改为 "dor 略优于 dmodk"。
report.md重新出图 (plot_gallery EXPERIMENT_ID → v5) + 走 docs-conventions。