跳到主要内容

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,这是业界标准做法,不是激进选择)

现状回顾(三句话)

  1. 硬件 spec(RCLINK_AFH_SPEC §5.10 P36L11)规定:上游下发新报文 → slot EMPTY→WAIT_GRANT,没有 polling;G5 当前 polling-driven 的 feed_rc_link 违背 spec
  2. 业界 5/5 event-driven 仿真器(ns-3 HPCC / BookSim2 / Garnet / htsim / OMNeT++ INET)没有一家用"周期 wake + scan 队列",全部 push-driven + drain-time self-scheduling
  3. wake-dependency 真凶已锁定:feed_rc_linknow_ns polling 探测 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 后续)
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

关键设计点

  1. 删除 RcLinkWake self-reschedule(multi_chip.rs:346 那段)→ wake 数严格 = 物理事件数
  2. paxi.rs:167 / 194 submit/on_ack 内部不再 push RcLinkWake event,改为内部 schedule SegmentDispatch
  3. 保留 try_arbitrate / on_tx_done 链路不动(已 event-driven 符合 spec §5.10 P37L24)
  4. WRR 修复保留(已 spec 对齐)
  5. per-chip next_dispatch_handle 句柄管理:Rust 没有 ns-3 EventId.IsExpired(),用 Option<(u64 wake_ns, u64 event_seq)> + SimKernel 加一个 cancel_event_at_seq 接口实现;或用 epoch counter 让旧事件触发时 noop

影响范围(估算)

文件改动
multi_chip.rsRcLinkWake 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 改动结构上是单点替换。

验证标准

  1. wake-frequency 独立性:故意调 SimKernel 内部 polling 节奏(不存在的话则在 test 里手动插入 noop event)×10/×0.1,21 拓扑 latency 数值 bit-exact 一致
  2. cargo test 全过(现 356 pass)
  3. 去重幻象消失:不再需要 G5_NO_FEED_DEDUP 开关(本来就只有物理事件)
  4. 数据集重跑: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

否决理由

  1. 事件数爆炸:tok=1024 N=64 单 cell 已有几百万 segment(每 segment ≤ MPS=4KB),event 数从当前 ~20M 飙升到 ~200M,优于 polling 但仍 O(packets);
  2. 没有真正解决问题:wake-dependency 通过对齐物理事件解决了,但事件数本身放大,违反 Pattern D 的"取 min 单事件"优化;
  3. 业界场景不同:htsim 是单 NIC 单队列,G5 是 multi-LG 多 VC,per-segment event 跟 dispatch 单元粒度不匹配;
  4. 回退成本高:一旦发现性能不够,改回方案 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,仅顺序 + 验收

  1. 加 SegmentDispatch event 类型 + cancel/epoch 机制(基础设施)→ 测试通过
  2. feed_rc_link 计算 next_t + 单 schedule(核心契约)→ 添加 wake-frequency 独立性测试,失败为本步未完成
  3. 删除 RcLinkWake 所有 producer(submit/on_ack/multi_chip/event_handlers)→ grep 全空,cargo test 全过
  4. 跑 21 拓扑 N=64 多 tok 回归,对比新旧 sim_us → 落 v3 数据集到 DB
  5. 更新 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-4WRR 修复 + take_segments per-txn 是否先 commit? 两者跟重构方向无关,纯 bit-identical/spec-correct 收益(锁定独立成果,跟重构 commit 分开 review)
Q-5DCQCN 高-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。

请回复你的决定。