ISSUE-031 后续重构方案候选
日期:2026-06-02 输入:ISSUE-031 后续重构:RC Link Spec 真值笔记(硬件 spec 真值) + 业界事件驱动仿真器 dispatch 机制调研(业界 5 个仿真器调研) 决策类型:Type 1(改 latency 数值 → 数据集需重跑) 目的:给用户拍板用,不展开实现细节
Thesis
采用 Garnet + ns-3 混合的"Conditional Single-Event Reschedule"模式:删除 RcLinkWake self-reschedule polling,改为 ACK / on_tx_done / CDMA 入队三类物理事件 push-driven 调用 feed_rc_link,后者在末尾根据物理参数(next segment earliest_ready、datapath busy until)计算唯一未来时刻并 schedule 一个 SegmentDispatch 事件,用单一事件句柄 + IsExpired-like 标记防重 schedule。
这同时满足三个硬约束:
- 符合 spec(P36L11 上游 push + §5.10 P37L24 仲裁 event-driven)
- 不简化(每个事件都对应硬件物理事件,无 polling 伪事件)
- 业界共识(5 个仿真器,event-driven 阵营 5/5 都是 push-driven,这是业界标准做法,不是激进选择)
现状回顾(三句话)
- 硬件 spec(RCLINK_AFH_SPEC §5.10 P36L11)规定:上游下发新报文 → slot EMPTY→WAIT_GRANT,没有 polling;G5 当前 polling-driven 的
feed_rc_link违背 spec - 业界 5/5 event-driven 仿真器(ns-3 HPCC / BookSim2 / Garnet / htsim / OMNeT++ INET)没有一家用"周期 wake + scan 队列",全部 push-driven + drain-time self-scheduling
- wake-dependency 真凶已锁定:
feed_rc_link用now_nspolling 探测earliest_ready→ wake 频率影响喂入时刻 → vc_pending 序列变 → latency 漂移;21 拓扑 DCQCN/credit/NAK 全 dormant,证明真凶是纯时序漂移而非状态机 bug
推荐方案 A:Conditional Single-Event Reschedule
借鉴 Garnet checkReschedule() 单事件停摆 + ns-3 m_nextSend.IsExpired() 防重 schedule + htsim queueWasEmpty edge trigger。三家融合。
行为契约
| 触发事件(物理) | Handler 行为 |
|---|---|
| CDMA submit(新 segment 入队) | 直接调 feed_rc_link(now) |
| on_ack(slot 释放) | 直接调 feed_rc_link(now) |
| handle_rc_frame_done → on_tx_done(datapath 释放) | 当前已 push-driven(try_arbitrate)→ 保留 |
| SegmentDispatch 事件触发 | 调 feed_rc_link(now) |
| DCQCN pacing 解除(暂缓,21 拓扑 dormant) | 单独处理(见高-2 后续) |
feed_rc_link 改后契约(伪代码)
fn feed_rc_link(now):
# 1. 喂入所有当前已就绪的 segment(unchanged)
while seg = segment_queue.peek() and seg.earliest_ready_ns <= now:
rc_link_tx.submit_frame(seg); segment_queue.pop()
# 2. 计算下一次必须 dispatch 的物理时刻
next_t = min(
segment_queue.front().earliest_ready_ns or +inf, # 下一 segment 物理就绪
rc_link_tx.datapath_busy_until or +inf, # datapath 释放
)
# 3. 单一 schedule + 防重(借鉴 ns-3 m_nextSend.IsExpired)
if next_t < +inf and next_t > now:
if self.next_dispatch_handle.is_none() or
self.next_dispatch_at > next_t:
# 新时刻更早 → cancel 旧的(若有), schedule 新的
cancel(self.next_dispatch_handle)
self.next_dispatch_handle = schedule(SegmentDispatch, next_t)
self.next_dispatch_at = next_t
关键设计点
- 删除
RcLinkWakeself-reschedule(multi_chip.rs:346 那段)→ wake 数严格 = 物理事件数 paxi.rs:167 / 194submit/on_ack 内部不再 push RcLinkWake event,改为内部 schedule SegmentDispatch- 保留
try_arbitrate / on_tx_done链路不动(已 event-driven 符合 spec §5.10 P37L24) - WRR 修复保留(已 spec 对齐)
- per-chip
next_dispatch_handle句柄管理:Rust 没有 ns-3EventId.IsExpired(),用Option<(u64 wake_ns, u64 event_seq)>+ SimKernel 加一个cancel_event_at_seq接口实现;或用 epoch counter 让旧事件触发时 noop
影响范围(估算)
| 文件 | 改动 |
|---|---|
multi_chip.rs | 删 RcLinkWake handler self-reschedule,替换为 SegmentDispatch |
paxi.rs | 移除 submit/on_ack 内部 RcLinkWake event push,改 SegmentDispatch |
paxi.rs:feed_rc_link | 加 next_dispatch_handle 管理 + cancel 逻辑 |
kernel.rs 或新增 | 加 cancel_event/event_seq epoch 机制 |
types.rs | 加 SegmentDispatch event,标记 RcLinkWake deprecated 或删除 |
tests | 加"仿真步长 ×10/×0.1 latency bit-exact"回归测试 |
预估改动:~150-250 行(中)。Type 2 改动结构上是单点替换。
验证标准
- wake-frequency 独立性:故意调 SimKernel 内部 polling 节奏(不存在的话则在 test 里手动插入 noop event)×10/×0.1,21 拓扑 latency 数值 bit-exact 一致
cargo test全过(现 356 pass)- 去重幻象消失:不再需要
G5_NO_FEED_DEDUP开关(本来就只有物理事件) - 数据集重跑:topo-routing-comm-eval 21 拓扑 × 多 tok 重跑,新数值作为 spec-correct baseline 入库
风险
| 风险 | 缓解 |
|---|---|
| Rust 实现 cancel_event 复杂度高 | 用 epoch counter 兜底:event seq 不匹配时 handler 直接 return |
| 漏掉某个 wake 路径(如 NAK timeout) | grep "RcLinkWake" 全删,每个 producer 改 SegmentDispatch 并跑测试 |
next_dispatch_at per-chip 状态正确性 | 加单元测试覆盖 submit/ack/dispatch 三角的状态转换 |
| 改完后数值跟 RTL 仿真结果对不上 | 当前已无 RTL 对照,本来就只能跟 spec 对齐;若后续有 RTL 数据,作为独立 spec gap 处理 |
备选方案 B(否决):htsim 风格 Per-Segment Event Chain
做法:create_segments 时为每个 segment schedule 一个 SegmentReady(earliest_ready) 事件;handler 直接喂入 LG + 链式 schedule 下一 segment。
类比:htsim Queue::completeService → beginService → sourceIsPendingRel。
否决理由:
- 事件数爆炸:tok=1024 N=64 单 cell 已有几百万 segment(每 segment ≤ MPS=4KB),event 数从当前 ~20M 飙升到 ~200M,优于 polling 但仍 O(packets);
- 没有真正解决问题:wake-dependency 通过对齐物理事件解决了,但事件数本身放大,违反 Pattern D 的"取 min 单事件"优化;
- 业界场景不同:htsim 是单 NIC 单队列,G5 是 multi-LG 多 VC,per-segment event 跟 dispatch 单元粒度不匹配;
- 回退成本高:一旦发现性能不够,改回方案 A 需要重做事件 schedule 逻辑。
保留为对照:若方案 A 实施中发现 single-event reschedule 在某些场景仍有漂移(比如 segment 之间间隔比仿真步长还小),可降级到 per-segment event 兜底。
不推荐 C(兜底):接受现状,只 commit 已固化两修复
做法:停止重构,提 commit 锁定 take_segments per-txn + WRR 两个修复,把 wake-dependency 文档化为"已知非阻塞问题",后续不修。
理由:
- 21 拓扑 DCQCN dormant 已确认,当前 sim_us 数值不是错的,只是 wake 数多了浪费 wall time(6× 提速已固化,放弃额外 18×)
- 不改 latency → 不重跑数据集 → 现有 validation 结果可用
- 不动 spec
保留为决策选项:如果用户判断"现在数据集刚跑完不想动",这是合法的暂缓选择;但违背"不简化,符合 spec"的约束——polling-driven 仍然在 spec 违规列表里,只是被 dormant 掩盖,incast 场景或更高数据量场景(>tok 1024 或 N=128)仍会暴露。
推荐做法的 5 步实施(高层级)
不展开 code,仅顺序 + 验收
- 加 SegmentDispatch event 类型 + cancel/epoch 机制(基础设施)→ 测试通过
- 改
feed_rc_link计算 next_t + 单 schedule(核心契约)→ 添加 wake-frequency 独立性测试,失败为本步未完成 - 删除
RcLinkWake所有 producer(submit/on_ack/multi_chip/event_handlers)→ grep 全空,cargo test 全过 - 跑 21 拓扑 N=64 多 tok 回归,对比新旧 sim_us → 落 v3 数据集到 DB
- 更新 spec 文档
G5-RC-Link传输层设计规格.md:加一节"Segment Dispatch 机制"明确 push-driven,引用 spec §5.10 + 业界经验
待澄清问题(需用户决策)
| # | 问题 | 推荐 |
|---|---|---|
| Q-1 | 现在重构 vs 暂缓? 重构会让 topo-routing-comm-eval 数据集失效需要重跑(2-4 小时) | 现在重构(否则 wake-dep 留作技术债,未来更难拆) |
| Q-2 | 选方案 A 还是 B? | A(Garnet+ns-3 融合,单事件 reschedule;B 事件爆炸否决) |
| Q-3 | 同步改 spec 文件? 加"Segment Dispatch 机制"小节明确 push-driven | 是(spec 一直没写过 dispatch 机制,补这个一致性 gap) |
| Q-4 | WRR 修复 + take_segments per-txn 是否先 commit? 两者跟重构方向无关,纯 bit-identical/spec-correct 收益 | 是(锁定独立成果,跟重构 commit 分开 review) |
| Q-5 | DCQCN 高-2 修复要不要捎带? 21 拓扑 dormant 但 incast 场景需要 | 暂缓(主链重构完后单独 PR,避免一次改动太多) |
决策结论(给用户的 1 句话)
我推荐:(1) 先 commit 当前 WRR + take_segments per-txn 两个修复,锁定独立成果;(2) 然后按方案 A 重构 segment dispatch 为 push-driven,移除 RcLinkWake polling;(3) 同步改 spec 文档明确机制;(4) 重跑 21 拓扑数据集作为 spec-correct v3 baseline。预计 1-2 天工程量,对所有未来高数据量 / incast 场景一劳永逸消除 wake-dep。
请回复你的决定。