跳到主要内容

ISSUE-002: Overcredit 静默丢包导致 128MB NAK 级联

发现日期: 2026-04-15
状态: 已分析,待修复
影响: G5 仿真 128MB AllReduce 产生 161 次 Go-Back-N,sent 膨胀至预期的 2.3 倍,G5/Meas = 1.31x


问题现象

在 ISSUE-001 credit return 修复后(RMSPE 降至 44.7%),128MB 仍异常:

SizeG5/Meassentnakno_sendableslot_exhaust
64MB0.96x16,38402613,042
128MB1.31x33,0691616,25431,028
256MB1.10x13,0840638,316
512MB1.14x36,40206116,508

128MB 出现 161 次 NAK,sent = 33,069 ≈ 预期 14,336 的 2.3 倍。其他尺寸 NAK = 0。


根因分析

直接原因:pending_packets 溢出时静默丢包

rc_link_tx.rssubmit_packet() 逻辑:

pub fn submit_packet(...) -> bool {
if let Some(slot_idx) = self.alloc_slot(...) {
// slot 可用 → 正常分配
true
} else if self.pending_packets.len() < self.config.overcredit_limit as usize {
// slot 耗尽但 pending 有空间 → 入 pending 队列
self.pending_packets.push_back(PendingPacket { ... });
false
} else {
false // ← 静默丢包!overcredit buffer 已满
}
}

当 512 个 slot 全满 pending_packets(上限 512)也满时,第 1025+ 个包被静默丢弃。 丢包 → 接收方见 PSN gap → 生成 NAK → Go-Back-N 重传所有在途 slot → 更多拥塞 → 更多丢包 → 级联。

为什么 128MB 触发而其他尺寸不触发

每步每链路的包数:

Size每步包数(估算)slot(512) + pending(512) = 1024是否溢出
64MB~512512 ≤ 1024不溢出
128MB~10241024 = 1024,边界触发溢出
256MB~2048> 1024,但步骤节奏不同,credit 阻塞更早待分析

128MB 恰好在临界点:每步包数刚好将 slot + pending 同时填满,触发溢出。


Spec 依据

"若剩余的 Credit 不足,即表示接收方没有足够的缓冲空间,此时数据包调度程序禁止该 VC 从 lossless 队列调度数据包进行传输。"

Credit 耗尽 = 禁止发送(阻塞),不是丢包。

Slot 状态机有四个状态:

EMPTY → WAIT_GRANT → WAIT_ACK → EMPTY
↕ ↕
WAIT_CRD WAIT_GRANT(retry)
  • WAIT_CRD:Credit 不足时 Slot 的正确状态,保留在队列中等 credit 返还,不丢包
  • WAIT_CRD → WAIT_GRANT:credit 返还后自动恢复

硬件接口 §7.3.1 TX_S_CRD_O

"TX_S_CRD_O[3:0]:令牌反馈信号,为高时标识有一个包缓冲区释放,上层逻辑可以发送一个报文"

上游(PAXI/CDMA)必须持有令牌才能提交包,没有令牌就等待。 硬件通过令牌机制保证上游不会超出 TYPE1_OST_N(512)。不存在 overflow 后静默丢弃的机制。


业界对比

主流协议在 buffer 满时均采用阻塞而非丢包:

协议Credit 耗尽行为
PCIe发送方停止,等 UpdateFC DLLP 返还 credit
InfiniBandper-VL 流量停止,等 credit 返还
RoCEv2CBFC/PFC 反压,上游暂停
RC LinkCBFC Status=0,调度程序禁止该 VC 发包

所有协议共同点:buffer 满 = 阻塞上游,不丢包。


解决方案

方案(采用):去掉 pending_packets,改为上游阻塞

核心原则submit_packet() slot 满时直接返回 false,包留在 paxi_core.segment_queue,等 ACK 释放 slot 后再由 feed_rc_link() 补充。

改动清单

文件 1: perfmodel/evaluation/g5/src/tier6/rc_link_tx.rs

  • 删除 pending_packets: VecDeque<PendingPacket> 字段
  • 删除 PendingPacket 结构体
  • 删除 overcredit_limit 配置字段(RcLinkTxConfig
  • submit_packet() 简化为:slot 可用返回 true,否则返回 false(不入 pending)
  • 删除 drain_pending() 方法及其在 process_ack() 中的调用
  • 删除 diag_slot_exhaust 计数器(改为记录 slot 满时的阻塞次数 diag_slot_full
  • 删除 overcredit_remaining() 方法

文件 2: perfmodel/evaluation/g5/src/tier6/paxi.rs

  • rc_link_available_capacity() 改为只返回 self.tx.free_slot_count()(去掉 overcredit 部分)

文件 3: configs/chips/SG2262.yaml

  • 删除 overcredit_limit 配置项

数据流对比

修复前:

paxi_core.segment_queue → feed_rc_link(capacity = free_slots + overcredit_remaining)
→ submit_packet → slot 或 pending_packets [满了丢包!]

修复后:

paxi_core.segment_queue → feed_rc_link(capacity = free_slots)
→ submit_packet → slot [slot 满时不取, 等 ACK]

ACK 触发链路:

ACK → on_ack() → process_ack() [释放 slot] → feed_rc_link() [从 segment_queue 补包] → try_arbitrate()

验证方法

  1. 修复后运行 python docs/validation/validate_g5_allreduce.py
  2. 预期:128MB nak=0sent 接近预期 ~14,336
  3. 预期:128MB G5/Meas 从 1.31x 降至接近 1.0x
  4. 确认其他尺寸(64MB、256MB、512MB)不受影响

遗留问题

  • 小消息(1-16MB)仍有 0.16x~0.63x 误差,根因是 CHS 内存一致性模式未实现(见 plan floating-gliding-island.md
  • 修复后整体 RMSPE 目标 <20%