1. 一次训练中断排查:丢包为什么会拖垮整个集群
去年秋天我碰到过一次特别棘手的训练中断事故。4千亿参数的多模态模型,256张A100跑分布式训练,loss曲线在关键的第三个epoch突然拉平,然后开始周期性出现NaN。第一反应是代码有bug,算法同事排查了两天毫无头绪,最后我用ethtool -S检查网卡计数器才发现,端口的rx_pause计数在训练峰值时暴涨了几百万次——这根本不是代码问题,是网络丢包引发的连锁反应。
这里必须先澄清一个容易混淆的点。标题里的PFC,在网络领域指的是Priority Flow Control(优先级流控),IEEE 802.1Qbb标准里定义的东西,跟电源圈常说的功率因数校正(Power Factor Correction)完全是两码事。你在搜索引擎敲"PFC",出来的往往是图腾柱电路、交错并联、电压电流双环控制这些电源设计的内容,跟本文讨论的数据中心无损网络没关系。做网络的人聊PFC,一定要带上前缀说"网络PFC"或者直接说"802.1Qbb",否则容易跟硬件同事聊岔。
回到事故本身。分布式AI训练的流量特征,跟传统互联网业务差别太大了。传统Web服务是典型的请求-响应模型,一个用户点一下,后台返回几百KB的页面,流量稀疏、偶发、对延迟不敏感。AI训练完全反过来,它跑的是All-to-All通信模式——每个GPU都要把梯度发给另外所有GPU,同时从所有GPU接收梯度。Singularity等分布式框架会周期性地进行全局梯度同步,每一次同步都意味着几千个GPU同时往交换机的同一个端口方向灌数据,形成极其猛烈的incast突发。
这种突发流量有多猛?我给你一个直观数字。128台GPU服务器做梯度同步,每台服务器在几十微秒内喷出1MB数据,汇聚到核心交换机时,瞬时带宽需求能达到理论线速的5到10倍。交换机内部的缓存,通常是8到16MB,面对这种突发,几百微秒就会填满。缓存一满,交换机只能做一件事——把后续到达的数据包丢掉。
丢包对传统TCP业务来说很常见,重传就行,最多慢一点。但在RDMA(远程直接内存访问)场景下,丢包是灾难性的。RDMA的通信语义是"要么全部完成,要么什么都不算数",一个报文丢了,整块数据就算失败,接收端的网卡直接把整段内存数据丢弃,发送端必须从更上层的协议栈重新发起。更麻烦的是,RDMA网卡里的重传逻辑非常苛刻,一旦重传次数超限,QP(Queue Pair)就直接进入错误状态,需要应用层手动重置。一个QP报错,往往牵动整个训练通信库的teardown,于是几百个GPU全部停下来等网络恢复。这就是那一串NaN的真相。
所以AI训练"怕丢包",怕的不仅仅是重传带来的延迟,而是丢包引发的级联故障——包丢失导致QP错误,QP错误导致集合通信中断,集合通信中断导致整个训练作业挂起。网上搜"wifi的丢包率怎么测试",那是家用网络测速的思路,跟数据中心训练网络的丢包治理完全不是一个量级的问题。为了保住RDMA,业界祭出了PFC这把"大杀器"。
2. PFC的工作机制:交换机之间互相"拉闸"的无损方案
PFC的初衷非常朴素:既然缓存满了会丢包,那我就在缓存快满的时候,让上游设备暂时停手,等等我。它的本质是逐跳流量控制,在相邻两台交换机之间建立"刹车"机制。
2.1 队列级别的流控逻辑
传统以太网的流控是粗粒度的,一停全停,802.3x标准里的PAUSE帧就是干这个的。PFC把它精细化到优先级队列层面。802.1Q标准里,每个数据帧的VLAN Tag带有3位PCP字段,能区分8个优先级。PFC在每台交换机的每个端口上维护8个独立的队列,每个队列对应一个优先级。
当某个队列的缓存占用超过高门限(XOFF阈值),交换机就向对端设备发一个PFC Pause帧,里面带着一个优先级位图,明确说"优先级3的流量先停一停"。对端收到后,只暂停该优先级的发送,其他优先级照常跑。当缓存回落到低门限(XON阈值)以下,交换机会再发一个Resume帧(Pause帧里时间参数为0),对端恢复发送。
门限值的设置很有意思。设得太小,Pause帧发得频繁,链路利用率降低;设得太大,缓存还没到门限就已经开始排队,延迟增大。工业界通常把XOFF设在总缓存的60%到70%,XON设在30%到40%,留出足够余量让Pause帧在缓存溢出前到达对端。这里必须考虑Pause帧的飞行时间——对端收到Pause后,端口上可能还有几十KB的数据已经在链路上了,所以门限必须大于链路带宽 × 链路往返时间,否则就会出现"刹车踩了还追尾"的丢包。
2.2 PFC依赖的物理条件
PFC对链路质量有隐性要求。它对端口的CRC错误、物理误码零容忍。你想,如果链路本身存在微小的误码,哪怕PFC保证了一个包都不丢,坏帧照样会被丢弃,RDMA照样报错。所以无损网络机房里,光模块的验收标准比普通数据中心严苛得多,很多团队要求链路误码率低于10的负15次方,并且用perfquery、ibstatus反复巡检光模块的接收功率和误码计数。
我还想强调一点:PFC是逐跳的,不是端到端的。它的控制范围只在相邻两台设备之间,数据从服务器A经过三台交换机到服务器B,任何一段链路的缓存压力都得靠自己这一段PFC消化,它不会跨设备传递拥塞信息。这意味着,拥塞点一旦出现在核心交换机上,网卡和接入交换机之间的PFC只能兜住自己的这一段,核心交换机的缓存仍会被打爆。这也是PFC后期备受诟病的原因之一——它管得了局部,管不了整体。
3. 无损网络的代价:头端阻塞、风向标反转与PFC风暴
PFC解决了丢包问题,却放出了一堆更阴险的"恶魔"。很多团队上了无损网络之后,业务没变快,反而出现各种莫名其妙的时延抖动和吞吐归零。这里的坑之深,真的只有趟过的人才知道。
3.1 头端阻塞(Head-of-Line Blocking)
先说最经典的头端阻塞。假设一个交换机端口有8个优先级队列,其中队列3跑的是RDMA流量,队列5跑的是普通TCP存储流量。突然队列3拥塞了,PFC把上游的队列3暂停,但共享同一个物理端口的其他队列不受影响,这是PFC比老PAUSE帧强的地方。然而问题出在交换机内部的转发结构上——很多交换芯片的端口之间共享缓存和仲裁资源,队列3把物理链路的传输能力占满,队列5的报文即使有自己的队列,也需要等待链路空闲才能发出去。
更严重的情况是拥塞反向传播。端口A的队列3拥塞,它向交换机X发Pause,交换机X的队列3暂停发送后,X的入端口继续接收来自服务器C的数据,X的队列3缓存越积越多,于是X又向上游交换机Y发Pause。就这样一层一层反向"拉闸",拥塞像多米诺骨牌一样从核心交换机一路传到接入交换机,最后传导到发出数据的网卡上。整个网络的RDMA流量全被暂停,而暂停的原因可能只是某个端口的瞬间拥塞。
3.2 PFC风暴与死锁
PFC还有一个极其可怕的异常场景——PFC风暴。由于网卡固件bug或者交换机芯片的缓存状态机错乱,某个端口可能会疯狂地向对端发送Pause帧,即使它根本没有拥塞。对端收到后傻乎乎地把对应优先级全部停掉,导致该链路的RDMA流量完全中断。2019年某云厂商就出过这类事故,一个交换机端口的异常Pause帧让整个可用区的一半GPU服务器训练中断4个小时。
更有意思的是死锁场景。两台交换机互相之间都有流量,A向B发Pause,B的缓存满了也要向A发Pause,如果两个Pause帧在链路上交叉着飞,双方都在等对方恢复发送,而双方也都在暂停发送——这就是PFC死锁。死锁期间链路完全静止,没有任何数据帧通过,直到某个超时机制介入强制丢弃Pause帧才恢复。虽然交换芯片厂商做了很多防护,比如"Pause帧超时自动忽略",但这类问题在大型无损网络中依然偶有发生。
3.3 PFC是拥塞控制的"风向标反转"
我最想吐槽的是PFC把拥塞信号藏起来了。传统TCP的拥塞信号是丢包,丢包意味着网络过载,TCP会主动退让。PFC的无损设计把丢包消灭了,但它制造了另一个问题——拥塞信号变成了Pause帧,而Pause帧在转发路径上是不可见的。运维人员根本不知道流量到底在哪里堵住了,只好一台一台交换机登录上去看计数器。网上的"下图PFC"讨论串里,经常能看到运维圈的人互相问"哪家的监控能看到逐端口的Pause帧计数",这玩意儿直到现在依然是很多团队监控体系的盲区。
PFC把拥塞控制的责任从传输层"向上推"给了链路层,但又没有提供端到端的可见性,相当于把一个房间里的浓烟藏进了墙里。这也是为什么业界后来一定要引入ECN——我们需要一种能"带话给发送端"的拥塞信号,而不是只在相邻设备之间打手语。
4. ECN的设计思路:从"事后重传"到"事前报信"的关键转向
ECN(Explicit Congestion Notification,显式拥塞通知)最早是RFC 3168定义的IP层能力,最初给TCP用,后来在RoCEv2无损网络里被DCQCN发扬光大。它的核心思想是:交换机在缓存将要溢出时,不是直接丢包,也不是发Pause,而是在数据包的IP头里打上一个标记(CE,Congestion Experienced),接收端看到标记后,通过某种反馈机制告诉发送端"该降速了"。
4.1 ECN标记的报文级逻辑
具体实现上,发送端发出去的报文,IP头的ECT(ECN-Capable Transport)位要置1,表示"我支持ECN"。交换机在转发时如果发现队列长度超过阈值K,就把报文的CE位(Congestion Experienced)置1,然后照常转发。接收端的网卡收到CE标记的报文后,知道网络某处拥塞了,于是根据所使用的协议(DCTCP、DCQCN、RoCEv2的CNP机制等)向发送端回传反馈,发送端据此调整自身的发送速率或拥塞窗口。
这里的阈值K是ECN调优的灵魂参数。K设得太小,交换机动不动就标记CE,发送端频繁降速,带宽利用率上不去;K设得太大,缓存快满了才开始标记,报文在交换机里排队时间过长,时延飙升。业界有个经验公式,K要大于等于带宽时延积(BDP)的某个比例。比如100Gbps链路,RTT按1.5微秒算,BDP大约是100Gb/s × 1.5μs ≈ 18.75KB。很多交换机的默认K值远小于这个数,需要手动调大。我之前在某个项目里把TOR交换机的ECN阈值从默认的8KB调到32KB,训练吞吐直接提升了12%,这个优化性价比极高。
4.2 DCTCP与DCQCN:两个典型的ECN反馈算法
ECN标记只是拥塞信号的载体,怎么处理这个信号,才是区分方案优劣的关键。这里必须提两个经典算法。第一个是DCTCP(Data Center TCP),它在接收端统计每个RTT内被CE标记的报文比例,计算一个拥塞程度参数α,然后通过标准TCP ACK反馈给发送端。发送端不是一刀切地把窗口减半,而是按α的比例做乘法减少,α越大降得越狠,α小意味着拥塞轻微,降速也小。实测下来,DCTCP能明显降低流量突发带来的队列堆积。
第二个是DCQCN(Data Center Quantized Congestion Notification),它是专门为RoCEv2设计的方案。接收端一旦收到带CE标记的RDMA报文,就生成一个CNP(Congestion Notification Packet)报文回传给发送端。发送端维护一个发送速率变量,收到CNP后先做一个大幅降速(乘性减少),然后进入一个恢复阶段,每隔一段时间尝试增加一点速率(加性增加),如果再次收到CNP就再降。这个"先降后慢慢试"的过程,有点像开车遇到堵车,一脚刹车踩到底,然后看路况一点点给油。DCQCN的性能很大程度上取决于CNP的发送速率和速率恢复步长这些参数,不同场景需要细细调。
4.3 ECN相比PFC的本质优势
ECN最本质的优势,是把拥塞控制拉回了端到端闭环。PFC只在相邻交换机之间传递"我满了,你停",而ECN通过端到端的反馈,让真正的流量源头(GPU服务器网卡)感知拥塞并主动降速。这就好比你做菜糊锅了,PFC是邻居闻到焦味跑来拍你窗户让你关火,ECN是厨房里的烟雾报警器直接联动燃气阀门自动关掉火源。
但ECN也不是完美无缺的。它在无损网络里有个致命短板——它只负责"通知",不负责"兜底"。ECN标记的报文从交换机转发到接收端,再让接收端回送CNP,这个链路本身需要时间。在极端突发下,交换机缓存可能在收到CNP之前就满了。所以RoCEv2的实际部署,从来不是二选一,而是PFC做最后一道防线,ECN做主动拥塞避免,两者配合才构成完整的无损网络方案。
5. 生产环境的组合方案:RoCEv2里的PFC兜底与ECN门限调优
RoCEv2是目前AI数据中心里绝对的主流网络协议,它本质上就是把RDMA报文封装进UDP/IP。RoCEv2在链路层跑在无损网络里,下层靠PFC保证"零丢包",上层靠ECN+DCQCN做主动降速。两层机制缺一不可,但要命的是它们之间的配合出过无数问题。
5.1 优先级划分与流量隔离策略
生产环境里,网络流量不只有RDMA。存储走iSCSI/NVMe-oF,管理面走SSH,监控走SNMP,这些业务如果用同一个网络,必须设置不同的优先级。通常的做法是:RDMA流量映射到优先级3,其他流量映射到低优先级队列。这个映射关系要在服务器网卡、接入交换机、核心交换机三处保持一致,任何一处配错,PFC的优先级控制就完全失效。
有个典型的坑:某团队把RDMA映射到优先级3,存储映射到优先级5,但接入交换机上忘了启用PFC的优先级5队列,导致存储流量拥塞时直接丢包。存储的TCP重传还能扛,RDMA流量的缓存池却被存储流量的突发占用,最终触发PFC把所有优先级3的流量也暂停了。所以在部署无损网络时,一定先想清楚"我需要几个无损优先级",把PFC使能范围压到最小,既能保住RDMA,又能减少PFC的副作用面积。
5.2 端到端的关键调优参数
ECN和PFC的参数调优,是整个无损网络上线最磨人的环节。以下是几个我实测过、效果明显的核心参数:
一是ECN标记阈值(K)。刚才说了,不要用交换机默认值。建议先算出链路BDP,把K设成1到2倍BDP,然后观察训练作业的流完成时间(FCT)和吞吐。如果吞吐上不去,适当上调K;如果时延抖动明显,下调K。
二是DCQCN的CNP反馈间隔。CNP报文本身会占用网络带宽,如果每个CE标记都立刻回CNP,拥塞时会形成"反馈风暴"反而加剧拥塞。通常需要配置一个最小间隔(比如每30微秒最多发一个CNP),控制反馈频率。
三是PFC的XOFF/XON门限。这跟交换机的整体缓存大小相关。有条件的话,给不同优先级设置不同的门限值,比如RDMA优先级的XOFF设高一些,因为ECN已经帮它做过主动降速,PFC只是兜底;普通流量的XOFF可以设低一些,宁可丢包也不影响无损流量。
四是网卡侧的PFC队列数。RoCEv2网卡通常支持多个发送队列,每个队列可以独立绑定优先级。建议把不同训练作业的大流和小流分开队列,避免一个慢启动的连接拖累另一个。
还有一个容易忽略的细节:PFC和ECN必须同时覆盖同一段链路。如果交换机A上ECN生效但PFC没打开,拥塞时ECN标记报文传出去了,但交换机B的方向没有PFC兜底,一旦突发超过缓存还是会丢包。很多团队在测试环境只开了ECN,没开PFC,小流量测不出问题,一上大规模训练就崩,往往就是这个原因。
5.3 实测里的观测手段
调参的前提是能观测到现象。推荐几个实用的观测手段:
- 网卡侧:
ethtool -S eth0 | grep pause,查看rx_pause和tx_pause计数,如果rx_pause持续增长,说明对端交换机已经在下达"暂停"指令,拥塞正在向源头蔓延。 - RDMA侧:
rdma_pma_counters能看到CongestionWindow、CNPSent等DCQCN参数在网卡层面执行情况。如果CNPSent暴涨,说明网络经常处于ECN标记状态,你的K阈值可能设得太小了。 - 交换机侧:查看各端口的
ecn_marked和pause_frame_rx计数,对比同一时段涨幅,能判断拥塞到底被ECN吸收了还是打到了PFC兜底层。
有一回我在现场调优,发现所有端口的rx_pause都在涨,但ecn_marked却没怎么涨。排查一番后确认是网卡的DCQCN参数里,反馈系数设置得太保守,发送端收到CNP后降速幅度不够,导致拥塞持续超过PFC门限。改成更积极的降速参数后,rx_pause计数立刻回落了一大截。这类问题不通过计数器对比,光凭主观感受很难定位。
6. 未来方向与我在这几次实战里沉淀的最后建议
无损网络这些年最大的争论点,就是"我们是不是为了保住RDMA的体面,花了太多代价在无损链路上"。PFC作为链路层的兜底机制,本身不可控、不透明、容易引发连锁故障,所以近几年的学术圈和工业界都在探索更激进的方案。
6.1 通向可编程数据平面的新思路
一方面,HPCC(High Precision Congestion Control)这类方案试图利用可编程交换机的带内遥测(INT),让数据面实时携带链路负载信息,发送端基于精确的队列信息做速率控制,而不是依赖ECN这种间接标记。另一方面,也有像NDP(New Datacenter Protocol)这样的方案,直接把"无损"这个目标抛弃掉,允许一定的丢包,但通过网卡侧的快速重传和调度算法把丢包代价降到最低——毕竟今天的高速网卡已经有足够能力在几微秒内完成重传。
这些方向现在还各有各的问题。HPCC要求全网交换机支持INT,升级成本高;NDP对网卡硬件的要求也不低。短时间内,PFC+ECN+DCQCN依然是AI训练网络的主流组合。但有一点已经明确:网络团队不能再把无损网络视为一个"配置完就完事"的静态方案,它更像一个需要持续调参、持续监控、甚至需要根据训练作业特征动态调整的活系统。
6.2 我的几点亲身经验
结合这几次实战,最后分享几条实在的建议:
第一,上线无损网络前,先把监控补齐。Pause计数、ECN标记计数、CNP计数,这三类指标必须纳入告警体系。我见过太多团队网络一抖动只能靠应用层loss曲线反推,浪费大量排查时间。
第二,不要迷信厂家默认参数。不同交换机型号、不同网卡型号的缓存能力和反馈机制差异很大,一定要做一次小规模负载测试,把K值和DCQCN参数放到实际流量里去验证。
第三,优先保大流,别让老鼠流骚扰大象流。AI训练里,集合通信的大流量是核心,心跳、日志这类小流虽然占带宽小,但突发时一样会触发PFC。设置优先级时,给不同训练作业的流量做好VLAN和优先级规划,让"长寿大流"和"短小流"尽量分开队列,能显著减少大象流被PFC误伤的概率。
第四,保留一个逃生通道。就算做成了无损网络,也不能把普通TCP流量完全牺牲掉。给存储和管理面留一个非无损的普通优先级,万一PFC风暴或者死锁真的出现了,你还有一条路能登录设备做排查和恢复。
说到底,从PFC到ECN的演进,本质上是网络拥塞控制从"被动刹车"走向"主动报信"的过程。PFC解决的是"别丢包"的问题,ECN解决的是"别拥塞"的问题,但两者都只是手段。对AI训练来说,核心永远是"别让丢包打断训练"。理解了这一层,再看各种新协议和新算法,思路就清晰了——大家都在往同一个方向使劲:让GPU在通信的时候,少一点等待,多一点算力。