ISSUE-030:族无关路由调查 — FlowMeta 硬编码修复 + packet_spray / flowlet_ar 在 SG2262 RCLINK + alltoallv 下物理不适用
发现日期:2026-06-01 状态:已修复(代码 bug)+ 已剔除(packet_spray / flowlet_ar 从研究) 类型:实现 bug + 建模决策(三层级联调查) 影响范围:G5 仿真 PacketSprayPolicy / PerFlowletAdaptivePolicy。不影响 family-aware 算法(ECMP / dmodk / DOR / UGAL / shortest_path)——这些算法 packet_id / sim_time_ns 是否准确都不影响其行为。
问题现象
调查跨越三个阶段,根因逐层揭示:
阶段 1:packet_spray 数值与 shortest_path 精确相等(bug 假象)
跑 --profile l1-grid --ns 32 后,fat-tree 三个 variant 上 packet_spray 与 shortest_path latency 精确相等:
| (N=32, S1@1.6, EP=32, compact, tok=192) | shortest_path | packet_spray | 差异 |
|---|---|---|---|
| fat-tree-k8(default cpb=8) | 55.70 μs | 55.70 μs | 0 |
| fat-tree-k8-pure(cpb=1) | 54.25 μs | 54.25 μs | 0 |
| fat-tree-k4 | 35.50 μs | 35.50 μs | 0 |
其他拓扑(torus / dragonfly / hypercube / hyperx)上 packet_spray ≠ SP。只在 fat-tree 上精确相等,排除偶然。
阶段 2:修复 packet_id 后 packet_spray 雪崩(协议层问题暴露)
定位 + 修复 c2c_network.rs:312 硬编码 packet_id: 0 后,packet_spray latency 从 33.98μs 雪崩到 8084.65μs(240× 比 SP 慢)。同时 PerFlowletAdaptive 的 sim_time_ns 也硬编码 0,顺手修了。
阶段 3:flowlet_ar 重跑后 17/25 cell 大幅恶化(workload 层问题暴露)
删 28 行 flowlet_ar 旧数据(bug 状态产出),重跑 l1-grid + validate 对比:
| 模式 | cell 数 | 新 latency | 旧 latency | 变化 |
|---|---|---|---|---|
| A. 雪崩 ~8000μs | 7 | 8018-8050 μs | 28-41 μs | +20000-28000% |
| B. 中爆 ~500μs | 10 | 514-533 μs | 21-34 μs | +1400-2280% |
| C. 不变 | 7 | 22-33 μs | 22-33 μs | 0.0% |
模式 A/B 都是 NAK storm,模式 C 是 flowlet 切换后路径"碰巧"等价。
调查过程
Phase 0:预期行为(spec 依据)
项目 Spec(docs/specs/路由模块设计.md):
- @tbl-spec-route-19 (line 563):
flow.packet_id: 报文标识,per-packet 算法的决策维度 - @tbl-spec-route-20 (line 580):
PacketSprayPolicy:按 packet_id 决策
算法 Spec(LetFlow,Vanini et al., NSDI 2017):
Flowlets are bursts of packets within a flow, separated by idle periods larger than the maximum network delay difference between paths.
Phase 1:症状定位
grep "packet_id" perfmodel/evaluation/g5/src/:
// c2c_network.rs:312
let flow = FlowMeta {
flow_id,
packet_id: 0, // ← 硬编码 bug
sim_time_ns: 0.0, // ← 同一构造点同样硬编码
};
调用方 7 处都不传 packet_id / sim_time_ns,所以 PacketSpray (0 % paths.len()) == 0 永远选 path[0](≡ shortest_path);PerFlowletAdaptive now - last_ts == 0 永远不大于 gap → 永不切换 flowlet(≡ first-hop ECMP)。
Phase 2:假设排序与排除
| # | 假设 | 验证结果 |
|---|---|---|
| 1 | c2c_network.rs:312 packet_id=0 硬编码是唯一根因 | ✓ 确认 |
| 2 | flowlet_ar 同样受影响(last_packet_id 比对永不切换) | ✓ 确认 |
| 3 | Layer 1 PacketSprayStrategy 在 fat-tree 上返回单路径 | ✗ 排除(find_equal_cost_paths c0→c30 返回 4 条路径) |
| 4 | G5 Policy descriptor 派遣错策略 | ✗ 排除(sim_engine.py:96-100 按 decision_frequency 正确派 PacketSprayPolicy) |
Phase 3:修复 + 集成测试
c2c_network.next_hop 加 packet_id: u64 + sim_time_ns: f64 参数,7 个调用点构造:
| 调用点 | packet_id 来源 | sim_time_ns 来源 |
|---|---|---|
| paxi.rs (frame_done) | (txn_id << 16) | frame_seq | now |
| paxi.rs (pack_done) | (txn_id << 16) | psn | now |
| paxi.rs (CreditReturn) | 0(控制平面,静态路由) | t |
| shared.rs (reverse ACK) | 0(ACK 静态路由) | t |
| switch.rs (3 处) | (txn_id << 16) | psn | now / fwd.finish_ns |
新增集成测试:
packet_id_threaded_to_packet_spray_policy(4-path 网络 × 16 packet_id,断言至少命中 2 path)sim_time_ns_triggers_flowlet_switch(50 flow × 4 path × gap=100ns × sim_time=500ns,断言至少 5 flow 切换 path)
cargo test --release 356/356 pass,ECMP/SP/DOR/dmodk/UGAL total_time 浮点完全相等(surgical 验证)。
Phase 4:packet_spray 雪崩根因(协议层)
RCLINK Spec(C:\Users\DELL\Documents\项目\PAXI\最新\RCLINK_AFH_SPEC_v2.4.md):
- P7L9:TYPE1 支持端到端保护,支持 Go-Back-N 端到端重传
- P7L11:TYPE2 不支持端到端保护
- P36L11 + P37L15:TYPE1 队列严格 in-order 仲裁
PAXI UG(UV_ORI_001_PAXI_SUE2.0_UserGuide_V2R0P5_20260130_release.md P19L11-12):
PAXI does not guarantee ordering for data across different virtual channels. The upper layer must maintain the order or support out-of-order operations.
含义:
| 层 | 顺序保证 |
|---|---|
| PAXI 跨 VC | 不保证(上层负责) |
| RCLINK TYPE1 单 VC 内 | 强保证(Go-Back-N) |
| RCLINK TYPE2 | 不保证(但非可靠传输) |
Go-Back-N:PSN gap 触发 sender 重传 N, N+1, ... 全部后续包。packet_spray 散布到多 path → 路径延迟差异 → 必然乱序到达 → NAK storm。8084μs 是物理正确的 Go-Back-N 雪崩代价。
Phase 5:flowlet_ar 重跑根因(workload 层)
[假设] Agent 修复 sim_time_ns 后报告 "validate 9 cell 全不变,flowlet_ar 与 ECMP 同值"——推断 alltoallv burst 模式下 flowlet 不触发。
[实验] 系统重跑 24 个 flowlet_ar cell 对比旧值:17/25 cell 大幅恶化(模式 A/B),agent 推断错。
[查代码] per_flowlet_adaptive.rs:92-118 切换条件:now - last_pkt_ts > flowlet_gap_ns。当前 flowlet_gap_ns = 5000ns。
[实验] alltoallv 实际时序:
- N=32 chips × 31 dst 并发 flow → 单 flow 相邻包间隔被 round-robin 拉长到 几μs
- 这个间隔正好跨越 5μs 阈值 → 频繁触发"假 idle"flowlet 切换
- 但旧路径包仍在传播(~3-5μs 链路延迟)→ 切换 = 同 (src, qp) PSN 乱序 → Go-Back-N NAK storm
[查 spec] LetFlow 论文核心假设:flowlets 之间是 app 层自然 idle——前一段包真的已经在网络里排空了。
[假设] alltoallv 不满足 LetFlow 假设 — 确认:
| 特征 | LetFlow 假设 | alltoallv 实际 |
|---|---|---|
| 单 flow 内 idle 间隙 | 有(app think time) | 几乎没有(后台批量持续 push) |
| 典型 inter-packet 间隔 | app 决定,可以 50μs+ | sender round-robin,几μs |
| 适用 workload | web search / RPC / storage | ❌ alltoallv / allreduce 后台批量 |
[实验] 调参实验(理论分析):
flowlet_gap_ns | 物理结果 | 信息价值 |
|---|---|---|
| 小(< 网络 RTT ~5μs) | 频繁触发,乱序到达 → NAK storm 雪崩 | 数据无效 |
| 大(> 任何真实间隔,如 50μs+) | 永不触发 flowlet → 退化为 first-hop ECMP | 数据 ≡ ECMP,无信息量 |
alltoallv 上不存在"甜点 gap"——同一参数既要 > 网络传播时间(避免乱序)又要 < workload 真实 idle 间隔(才能触发),而 alltoallv 没有真实 idle。
根因分析(三层级联)
-
实现 bug(已修):
c2c_network.next_hop隐式构造 FlowMeta 时硬编码packet_id: 0+sim_time_ns: 0.0,违反 spec @tbl-spec-route-19 / 20。这层是直接的代码错误。 -
协议层不兼容(剔除 packet_spray):SG2262 RCLINK TYPE1 用 Go-Back-N 强 in-order,与 packet_spray 必然乱序的特性冲突。这是硬件协议层级的硬约束,修代码改变不了。
-
Workload 层不兼容(剔除 flowlet_ar):LetFlow 假设 app 有 idle 间隙,alltoallv 没有,所以 flowlet_gap_ns 调小则乱序(同 Go-Back-N 问题)、调大则永不触发。alltoallv workload 范式与 LetFlow 不匹配。
解决方案
实施清单
| 操作 | 文件 | 状态 |
|---|---|---|
修代码:加 packet_id: u64 + sim_time_ns: f64 参数 + 7 调用点构造 + 2 集成测试 | c2c_network.rs + event_handlers/paxi.rs / shared.rs / switch.rs | ✓ |
| G5 顺手优化:trace 守卫 + chip_name 反向表 + Cargo LTO(单 cell 9.56s → 6.99s) | multi_chip.rs / c2c_network.rs / Cargo.toml | ✓ |
剔除 packet_spray:_cell_descriptors_for_n 移除 packet_spray 行 + MAIN3 用 dmodk 替代 + DB 清 90 行 | run_alltoallv.py + DB | ✓ |
剔除 flowlet_ar:_mp() 返回 [] + validate 用 shortest_path 替代 + DB 清 24 行 | run_alltoallv.py + DB | ✓ |
| 文档说明:design.md §路由表删族无关列 + 合并剔除论证小节 + 计数 41/46 → 35/39 → 25/28(回到 原始 routing 集) | design.md | ✓ |
| 保留 Policy 实现:packet_spray.rs / per_flowlet_adaptive.rs | 不删 | ✓(未来硬件 / workload 变化可重新启用) |
业界对照(支持剔除决定)
| 协议 / 算法 | 与 SG2262 alltoallv 的兼容性 |
|---|---|
| packet_spray on IB RC | ❌(IB 严格 PSN,同 RCLINK) |
| packet_spray on UEC 1.0 | ✓(UEC spec 强制 OOO RX 配套) |
| packet_spray on Spectrum-X / EFA | ✓(应用层 / 交换机处理 reorder) |
| flowlet_ar on web/storage workload | ✓(LetFlow 论文场景) |
| flowlet_ar on alltoallv workload | ❌(无 idle 间隙) |
业界共识:packet_spray 需要 OOO RX 配套;flowlet_ar 需要 workload 有自然 idle。两者在 SG2262 RCLINK + alltoallv 组合下都不满足前提条件。
验证
修复正确性
| 检验项 | 结果 |
|---|---|
cargo test --release --lib | 356/356 pass(含新增 packet_id / sim_time_ns 集成测试) |
ECMP / SP / DOR / dmodk / UGAL 修复前后 total_time | 浮点完全相等(surgical 验证) |
validate 9 cell | 全 pass |
smoke 1 cell | 4s pass |
剔除完成
# DB 已清
python -c "import sqlite3; con=sqlite3.connect('data/llm_evaluations.db'); \
print(con.execute(\"SELECT COUNT(*) FROM comm_eval_results WHERE experiment_id=36 \
AND json_extract(config_snapshot,'\$.routing') IN ('packet_spray','flowlet_ar')\").fetchone()[0])"
# 预期 0
# DB 现状: 234 rows / 5 routings (ECMP 115 / DOR 76 / SP 30 / dmodk 10 / UGAL 3)
Lessons Learned / Prevention
测试层缺口(导致 bug 长期未被发现)
| 测试层级 | 覆盖 | 是否覆盖根因 |
|---|---|---|
| Unit (PacketSprayPolicy) | 给策略喂不同 packet_id 会散布 | ✗ 测试自造 FlowMeta,不验证生产路径 |
| Unit (c2c_network) | next_hop 内部构造 FlowMeta 填什么 packet_id | ✗ 缺 |
| Integration (G5 全仿真) | 跑完一次 alltoallv 后 packet 真的散布到多 path 吗 | ✗ 缺 |
经验:每个 ForwardingPolicy 都需要"跑完整 sim 后断言行为符合 spec"的集成测试,单元测试给策略喂数据不算数。本次新增的两个集成测试已 lock 这个边界。
流程层缺口 — 路由算法引入双重检查清单
ISSUE-030 的三层级联根因暴露:
| 当前流程 | 缺的环节 |
|---|---|
| 加 routing algorithm | 检查算法对 RX/transport 的协议层依赖 → 缺 |
| 检查 G5 sim 支持 | 检查算法的 workload 范式假设 → 缺 |
| 检查目标硬件 | 是否原生支持 → 缺 |
建议docs/specs/路由模块设计.md 加 "Strategy 引入检查清单":
- 网络层:需要哪种 path enumeration(single / multi / distribution)?
- 协议层:对 RX/transport 有什么 in-order / OOO / SACK 要求?
- Workload 范式:假设 sender 行为如何(burst / idle / steady)?
- 目标硬件实测:查硬件 spec 验证协议兼容性
- 目标 workload 实测:跑一个代表 cell 验证 workload 假设成立
如果 v1.2.0 引入 packet_spray / v1.3.2 引入 flowlet_ar 时走过这个清单,会立刻发现冲突——不会有今天这次 "Implementation → Bug fix → Discover incompatibility" 的弯路。
研究价值
两个剔除都不是"做减法",而是"做了 spec-grounded 的负面结论":
- packet_spray 业界推广(UEC 2025 / NVIDIA Spectrum-X)默认硬件已升级到 OOO-capable;SG2262 用户不能套用业界"应该用 packet_spray"的结论
- flowlet_ar 业界推广(NVIDIA / Cisco)默认 workload 有 idle gap;MoE alltoallv 用户不能套用业界"flowlet 比 ECMP 好"的结论
- 输出"在 SG2262 RCLINK + alltoallv 组合下,只有 ECMP / dmodk / DOR / UGAL / SP 是真正可用的路由"= 有决策价值的负面结论
遗留问题
- ACK 反向路径 packet_id 语义未在 spec 明确:本次修复用
0(静态路由),建议项目 spec v1.4 补充 - PerHopAdaptivePolicy 的 packet_id 用法:未深查,decision_frequency=per_hop 可能有类似问题,需单独验证
- 未来硬件升级路径:若 SG2262 后续版本支持 OOO RX(UEC 兼容),可重新评估 packet_spray
- 不同 workload 评估:若评估 LLM serving 等有 think time 的 workload,flowlet_ar 可能恢复价值
- flowlet_gap_ns 的 tuning guideline:本研究论证它在 alltoallv 上没有甜点,但通用 workload 的 tuning(应 > max 路径间延迟差)值得写进 spec