弄巧成拙的 PFC(Priority-based Flow Control)
先说几句车轱辘话,TCP 性能低,所以 RDMA,以太网丢包,所以 PFC,网卡不能太复杂,所以 GBN…
HPC,AI 对吞吐,时延要求非常高,同时需要更多计算资源,而 TCP 处理需要大量计算资源,复杂逻辑意味着高时延,RDMA 旨在将网络逻辑卸载到网卡硬件,解放了 CPU,同时降低了时延。这是伏笔。
InfiniBand 满足 RDMA 的需求,但复杂且昂贵,需要专用 IB 链路层支撑,而 RoCE 旨在普遍的以太网上部署 RDMA,但以太网是有损网络,会拥塞而丢包,丢包重传会增加网卡实现的复杂性同时增加时延,抵消 RDMA 的优势。这是问题。
硬件难以实现复杂的丢包检测和重传机制,因此由网络保证不会丢包,同时网卡以 GBN 兜底,就是 PFC + GBN。这是解法。
看这一路,完全没从第一性原则解决问题,而几乎全是 bugfix 的修补。虽然旨在替换 TCP 的低效并弥补以太网的松散,但根本上还是在学 TCP,学以太网,没新花样。
简单说几个问题。
PFC 阻塞树蔓延。RDMA 网络从一开始可能就没有考虑大规模扩展性,不然不会采用昂贵复杂的 IB,但凡小规模策略扩展到大规模场景,一定会出问题。城邦制度无法统治帝国,因此罗马共和国必须变成罗马帝国。阻塞树一旦蔓延生长,死锁理论上几乎不可避免,概率高低而已。举个最简单的例子,蔓延时间尺度越大,遭遇路由重收敛概率越大,暂时的环路足以让阻塞树成环,锁死整个网络。
于是,又将出现一系列解决死锁的算法,膏药又糊上了一层。
拥塞控制算法受骗。PFC 基于优先级队列 pause 上游,而不是基于流,因此会出现队头阻塞问题,上游受连累的拥塞无关流被阻塞而造成时延增加,端到端拥塞控制算法会因此而受骗,采取措施反制拥塞。另一方面,如果算法没有受骗,不做反制,那么假拥塞也会变成真拥塞,虽然暂时是拥塞无关流,但被 pause 后相当于人为被拥塞。
如何做?只对拥塞源 mark 标记,不 mark 受害流显然不可行,这样会让假拥塞变真,因此只能共克时艰。换个相反的思路,扩展标记状态空间,对受害流 mark 一个不一样的 “受害” 标记,受害 sender 暂时抑制发送,但当拥塞缓解后,马上 undo,而不是真的做拥塞控制。显然这只是我拍脑袋的想法,说到底还是把戏。
…
即便不重构整个网络,只要从本质出发,问题的解法也会比 PFC 更高尚。
不要指望网络无损,因为不丢包不可能,丢包原因又不止拥塞一种,PFC 显然无法覆盖所有场景,问题的核心应集中在丢包恢复,而拥塞控制完全从丢包解耦,拥塞不与丢包关联,自然就是另一个独立问题。
先不管如何实现,丢包检测靠 NACK,拥塞通知靠源抑制,就是非常第一性的做法,其它的或多或少都在学 TCP,而 TCP 的第一代显然是重实现轻设计的 tradeoff。
无论 NACK 还是源抑制都被标准化委员会负面评价过,但这种评价的背后也是基于 tradeoff,足够就是最好。
基于本质去做设计,一切都是自然而然。
说到 NACK,它最初作为 TCP 的备选方案,思想与 TCP 最终确定的积累确认皆然相反,但在争论过程中败下阵来仅因为积累确认实现的简洁和高效,适应了 1970 年代的网络环境,足够就是最好,但还可以更好。自然,有了 NACK 便不需要 GBN(还有个有趣的 GB0),也就没有 SACK 实现复杂的问题了,一个 bitmap 即可丝滑完成整个过程,TTPoE 对此还有更简单的方案。
我们看到,诸多非 TCP 新传输协议都在喷 TCP 的同时又在学 TCP,很少有彻底解决 TCP 问题的。虽然 QUIC 也有诸多限制,但 QUIC 将 packet id 从字节流 sequence 中分离就是一个非常简单但彻底的修改,一下子就解决了重传歧义问题,扫除了大量 TCP 中的启发式把戏,仅仅就是增加了一个字段。
我们或许已经看到,各大厂已经有将 SACK 移植到自研网卡的案例了,这显然还是在学 TCP。
至于源抑制,从交换机引线接入每一个端主机以及其它交换机,单独做带外控制面,这自然而然就是转控分离的 SDN,而源抑制报文只是传输的控制报文的一种。源抑制之所以被负面评价(参见 RFC896 “Congestion Control in IP/TCP Internetworks” Nagle 的评价),核心在于广域网的不确定性和无标准化,然而我们现在设计的是数据中心网络,而非广域网。与此类似,SDN 不适用广域网,也是一样的考虑。
…
再说实现,都砸钱雇人折腾那么大动静了,又是移植又是重构的,拉一个带外控制面很难吗,重新实现一个 NACK 机制很难吗?
总之,以太网是大势所趋,不要试图改变以太网的有损本质,这正是它成功的原因,折腾一圈把以太网变成类 IB,还不如直接用 IB。以太网固然有损,需要主机弥补损失,要做的事情只有两个:
- 减少丢包
- 及时重传
减少丢包的方式分两个方法,用更高质量的设备和线缆,减少误码率,用更大带宽(或更大 buffer 吸收更多 incast?不建议,但赞成)的设备配合源抑制,减少拥塞丢包,而及时重传的方案也没太多花活儿,NACK 足够,但不要猜,如果网络丢包非常罕见,GBN 也不是不可。
此前有朋友问不开启 SACK 的 TCP 能不能做 RACK,这问题拧巴了,但世上大部分问题都是让你知其不可为而为之,然后就有了各种把戏方案,这样或那样,类似 pacing 或 burst,你永远把握不好那个 “度”。其实这个问题只要修改 TCP 的一个细节就能解决,让 receiver 回复 ACK 时,timestamp 的 tsecr 永远携带最近收到数据的 tsval 即可,看似解法简单,但谁知道会带来别的什么新问题呢,因为这不是本质。
浙江温州皮鞋湿,下雨进水不会胖。