☰
高性能计算负载均衡:从调度器到网络层的全面解析
2026/10/5 13:33:17 网站建设 项目流程

做高性能计算的人,应该都体会过一种无力感:集群账面算力很高,跑起真实任务却总差一口气。节点有的忙到排队、有的闲着吃灰,网络动不动就拥塞,最终整个应用的完成时间被最长的那条路径拖死。这就是典型的负载不均衡。

很多人一听到“负载均衡”,第一反应是Nginx、L4/L7转发、加权轮询那套Web场景的东西。但放在高性能计算里,负载均衡完全是另一套逻辑:它面对的往往是成百上千个节点并行协作的海量计算任务,任务之间还有通信依赖,节点之间通过InfiniBand或RoCE高速网络耦合。简单把一个请求转发给空闲后端这种思路,在HPC集群里是行不通的。

这篇文章我想围绕“高性能计算负载均衡”这个词,说说我自己的理解,以及实际集群运维里踩过的坑和沉淀下来的经验。内容涉及作业调度器层面的均衡、并行计算框架里的负载切分、网络链路的均衡,以及最近热词“等开销负载均衡”背后的思想。也算是一个实操向的梳理,希望能给正在折腾HPC集群或准备入坑的朋友一些参考。

1. 为什么HPC的负载均衡和Web场景根本是两回事

1.1 从任务模型差异说起

Web服务的负载均衡,核心是“请求”级别的转发。每个请求通常互相独立,后端节点无状态或弱状态,请求落在哪一个节点上结果都一样。负载均衡器要做的,就是在多个后端里挑一个最合适的目标,算法无非是轮询、最少连接、一致性哈希、带权重随机等,再配合健康检查,基本就能覆盖大部分需求。

HPC不是这个玩法。一个典型的科学计算任务,比如计算流体力学模拟,往往会被分解成几十万个网格子块,分配到几十甚至几百个计算节点上并行计算。这些子块之间不是独立的:每一轮迭代都要和相邻子块交换边界数据,算完一段后还要做全局归约。这就意味着负载均衡不能只看“哪个节点空闲”,还必须看任务之间的通信拓扑。

1.2 “分配”和“调度”带着约束

所以HPC的负载均衡本质上是一个带约束的组合优化问题。你把哪些子块放在哪个节点上,不仅影响节点自身的负载水平,还影响节点之间的通信量。好的分法是:通信量大的子块尽量放同一个节点、同一台机器,或者至少放在同一个交换机域内;通信量小的子块则可以稍微放飞一下。

我经常跟团队里的新人说一句话:把HPC负载均衡想象成往不同大小的背包里装石头,每个背包除了重量上限,每块石头还有其他石头之间的“感情浓度”,关系好的要尽量放一个包里。这显然比“哪个人少往哪丢”复杂几个数量级。

2. 高性能计算负载均衡的典型分层

负载均衡在HPC里不是单点技术,而是一整套贯穿上下的体系。我从下往上给大家梳理一层。

2.1 作业调度层

这是HPC集群最经典的负载均衡层面。典型工具是SLURM、PBS、LSF等作业调度器。它们负责把用户提交的“作业”分配到计算节点的“槽位”上。

这里的均衡逻辑主要是:

  • 队列优先级和公平共享策略;
  • 节点空闲资源多少;
  • 节点的专属属性(比如有没有GPU、显存多大、是不是胖节点);
  • 拓扑感知:尽量减少跨交换机通信。

可以说,只要一个集群里的作业五花八门、长短不一、资源需求各异,调度器的均衡策略直接决定整个集群的利用率上限。

2.2 并行计算框架层

这一层更“微观”。对于一个已经在多个节点上跑起来的并行应用,负载均衡要解决的是计算任务在各个进程间怎么切分的问题。

在MPI编程模型里,这是“静态分区”还是“动态负载再平衡”的问题。有些计算场景,比如自适应网格加密(AMR)、粒子模拟(如SPH)、图计算,计算量会在不同区域剧烈变化,初始分配很快就不均衡了。这时候需要负载均衡库(如Zoltan、ParMETIS)介入,周期性做任务的重新划分和迁移。

到了AI训练场景,模型并行策略(数据并行、张量并行、流水线并行)的选择本质上也是一个负载均衡决策。GPU之间算力差异、显存差异、通信拓扑差异,都会被考虑进去。

2.3 系统软件与网络层

这是很多人容易忽略的一层。HPC的高速网络(InfiniBand、RoCE)通常有多路径。数据在节点之间传输时,到底走哪条物理路径,由ECMP或自适应路由决定。如果哈希做得不好,多条大流量可能打在同一个链路上,形成局部拥塞,整体通信性能就会严重下降。

这一层的负载均衡做得好不好,直接影响上层应用并行效率的上限。再好的任务分配,底下一拥塞,全都白搭。

3. 作业调度器能做哪些实实在在的均衡操作

3.1 静态分配与排序策略

最简单也最有效的做法,是在作业调度时把节点资源想象成“内存碎片”,用类似于内存分配的最佳适配算法去挑选节点。

以SLURM为例,它支持两种节点分配逻辑:

  • 线性分配(linear):逐个节点往下填,优先把作业堆在同一批节点上。
  • 块分配(block):一次性划出一块连续的空闲节点。

如果你的作业是通信敏感的MPI作业,块分配通常更好,因为它能把作业限定在连续的节点集合内,减少和其他作业在节点上互相争抢带宽的概率。

但块分配也会带来碎片化问题:集群跑一段时间后,空闲节点会东一块西一块,一个大作业可能因为找不到足够大的连续块而等待,哪怕集群总体空闲资源充足。这时候就需要靠调度器的回填(Backfill)机制来兜底:在小作业不阻塞大作业预期开始时间的前提下,提前把零碎资源给到小作业。

3.2 动态重调度:抢占与迁移

静态分配只能解决“作业开始时刻”的均衡问题。一旦跑起来,每个作业的实际资源占用可能和申请时差异很大。比如基因测序任务前期内存吃紧,后期CPU繁忙;比如CFD任务在迭代收敛过程中,某个区域网格加密,导致部分进程负载暴涨。

要做动态均衡,就需要调度器支持:

  • 作业抢占(Preemption):高优先级作业来了,把低优先级的作业挂起或杀掉,把节点腾出来。
  • 作业迁移(Migration):通过检查点机制把运行中的作业保存到磁盘,然后在另一个节点上恢复执行。

实际生产环境里,“迁移”通常用得很谨慎,因为检查点文件可能非常大(动辄几百GB),迁移期间的网络开销也不小。所以我一般建议:只有在节点确实需要下线维护,或者任务在一个节点上长时间单点性能异常时,才用迁移,不要太轻易触发。

3.3 拓扑感知分配

当作业规模大到跨多个交换机域时,节点之间的相对位置就很关键了。

SLURM里有--switches参数,可以要求作业的所有节点尽量落在同一个交换机之下。配合scontrol show topology能看到硬件拓扑结构。这种拓扑感知分配,本质上是给调度器多一个“成本维度”:不仅看资源够不够,还要看通信代价高不高。

我自己在操作时的经验是:对于经常跑大规模MPI的集群,把拓扑信息配置好,比单纯加带宽带来的性能收益要显著得多。有时候同一套代码,拓扑感知分配的版本比非感知版本直接快20%~30%,没有任何代码改动,纯靠调度器选择节点的方式变了。

4. 并行程序内部的动态负载平衡实践

4.1 什么时候真的需要动态负载均衡

很多并行程序的负载不均衡,是随时间演化的。典型有:

  • 自适应网格加密(AMR)中,高精度区域不断移动;
  • 分子动力学模拟中,粒子在空间分布不均匀,且随时间漂移;
  • 图算法中,顶点的度数差异极大;
  • 稀疏矩阵迭代中,非零元分布不规律。

如果只是简单地对网格做均匀切分,碰上这类问题,进程间计算量差距会非常大。有的进程跑完了在那儿干等,有的跑到天荒地老,整个作业被最慢的进程拖着。

4.2 使用ParMETIS等库来做动态再划分

对这类问题,最主流的技术路线是:定期在运行时采集各个进程的工作量指标,然后调用图划分工具(ParMETIS、Zoltan、Scotch)重新划分任务图,最后把需要迁移的数据打包搬走。

这样做的好处是划分时考虑了通信代价和负载均衡双重目标,不会为了追求负载一致而把通信搞成一团乱麻。ParMETIS的接口是MPI原生风格,支持直接在分布式环境中做多级图划分,性能也很优秀。

我踩过的一个坑是迁移策略过频。既然负载在演化,那多久重新划分一次?太频繁,迁移数据开销反而抵消收益;太少,节点间再次失衡。我的经验是:让测量和划分之间有一定的滞回冗余,比如只有当集群内“负载偏移度”(负载最重进程和最轻进程的完成时间比)连续两次超过1.2时,才触发一次重划分。宁可在几个时间步里吃点亏,也不要频繁在全集群范围内搬数据。

4.3 动态负载均衡的高开销隐患

动态负载均衡本质是在用“空间交换时间”:把数据搬来搬去,减少了等待,但也引入了传输开销。所以动态均衡算法的核心就是不只要算“当前怎么分最均衡”,还要算“按这个分法,搬迁的通信开销是多少、重新计算的收益是多少”。

这也是最近圈子里“等开销负载均衡”被频繁提及的一个原因。它强调的正是:任何负载均衡动作本身也有成本,均衡求的是“系统整体完成时间的最小化”,而不是“负载方差最小化”。推导下来,很多看似不太均衡的分配,实际整体开销反而更小——因为迁移数据的代价被省下来了。

5. 面向AI训练集群的负载均衡新课题

AI大模型训练和传统HPC有相似之处,也有明显差异。相似之处在于都要大规模并行、都要高速互联;差异在于AI训练的任务拓扑是高度规律的(张量并行、流水线并行、数据并行组合),而且梯度同步的通信频率极高。

5.1 并行策略本身就在做负载均衡

  • 数据并行:模型每个副本吃一样的计算量,靠的是数据批次分配均匀。负载均衡的关键在于让每张卡每次拿到的batch大小、处理难度一致,否则梯度更新会卡在最慢的那张卡上。
  • 张量并行:把一层的权重切成多块放到多卡上,关键是让每块的计算量、通信量均衡。切分维度的选择、是否引入序列并行,都是在做精细的负载对冲。
  • 流水线并行:把网络层分段放到多张卡上。最怕的是各个阶段的耗时差太大,形成漏斗效应,某段卡住,整条流水线都在等。

5.2 异构集群的均衡问题更突出

一个真实的大模型训练集群里,GPU型号可能不统一,有的卡算力强、有的卡显存大,甚至某些节点还挂着不同版本的驱动和NVLink拓扑。这种情况下,天真地把batch均匀分给所有卡,反而是最不均衡的。

正确的做法是:先通过Profiling工具测出每张卡的实际算力矩阵,然后据此做不等比切分。比如A100的节点多分一点数据,H800的节点少分一点,代价是每轮迭代结束时需要做梯度AllReduce,这时候为了让不同算力的卡尽量同步完成计算,可以使用梯度累积或调整microbatch大小来微调负载。这个调整过程,本质上就是负载均衡。

5.3 训练中断恢复中的负载再平衡

AI训练任务通常是超长运行,动辄几十天。中途总会有节点故障或性能劣化。这时候调度系统和训练框架就必须配合:发现某个节点掉队,立刻触发一次“负载再平衡”——把该节点上的模型状态和优化器状态迁移到备用节点,同时调整全局并行策略。这里用到的技术栈包括Megatron-LM / DeepSpeed / ColossalAI里的弹性训练能力,以及集群管理层的节点健康监测、自动故障转移等。

从负载均衡的角度看,这就是一个“运行期重排”问题。靠的依然是:监控数据采集、成本估算、迁移调度、数据同步这几板斧。

6. 网络层负载均衡:最容易被低估的一环

6.1 不要让通信路径变成隐形瓶颈

我带队运维过一套HPC集群,从软件层面看,每台计算节点的CPU利用率都挺均衡,作业调度也很合理。但整个集群跑大规模MPI通信密集型应用时,总是达不到预期的加速比。

排查下来,问题出在网络的ECMP哈希不均。服务器到TOR交换机有两条25G上行链路,理论上链路聚合后带宽会翻倍。但由于哈希因子设计得不好,几个大流量通信对(同一对IP+端口)全都被哈希到了同一条物理链路上,另一条链路闲得发慌。网络层的哈希不是按“链路负载”做的,而是按流的五元组做的,一旦哈希碰撞,多条大流打在同一链路上,局部拥塞就出现了。

解决办法是调整哈希策略:

  • 增加哈希输入因子里的“随机熵”,比如让MPI库为每条通信流使用不同的端口范围;
  • 在交换机侧开启自定义哈希字段,糅合更多IP/端口信息;
  • 有条件的话启用自适应路由(比如InfiniBand的自适应路由功能),让报文动态绕过拥塞链路。

6.2 RoCE网络尤其注意PFC和ECN的配合

现在很多AI训练集群用的是RoCE网络(即RDMA over Converged Ethernet)。RoCE依赖无损网络,靠PFC(优先流控制)做逐跳流控,靠ECN做拥塞标记,配合端到端的DCTCP或DCQCN拥塞控制算法。

这里的负载均衡坑非常隐蔽:如果PFC配置不当,某个端口发生拥塞,反向压力会迅速传导到整个网络,形成“拥塞树”,造成一堆节点无辜躺枪。哪怕你的流量分配在节点层是均衡的,网络层也可能因为PFC的“殃及池鱼”效应而出现严重的不均衡。

我的建议是:

  • 在网络建设阶段就明确区分存储流量、计算通信流量、管理流量,不同优先级打上不同802.1p优先级标签;
  • PFC只对少数高优先级队列开启,不要对全部队列开启,避免无差别的暂停帧影响所有业务;
  • ECN阈值要根据交换机缓冲区大小做实验标定,默认值往往不是最优值。

6.3 存储I/O负载均衡也别忘

HPC应用,尤其是AI训练,对并行文件系统的I/O压力巨大。检查点和日志写入、数据集随机读取,都会在存储层形成热点。如果存储的元数据服务器和对象存储节点上的负载不均衡,同样会成为整体性能瓶颈。

存储侧的负载均衡通常由并行文件系统自身(如Lustre、BeeGFS)负责,通过条带化把文件均匀分布到多个OST(对象存储目标)上。实际操作中,我会特别关注:

  • 文件条带宽度和条带大小是否匹配应用访存模式;
  • 大量小文件读写时,元数据服务是否成为瓶颈;
  • 检查点集中写入时是否造成OST热点。

有些时候,单个作业把一个大文件条带化到过多OST上,反而会因为跨OST的写锁竞争导致性能下降,所以条带设置也要讲究“适度”,这其实也是一种均衡的艺术。

7. 等开销负载均衡:从“平均负载”到“最小开销”

7.1 理念差异

传统负载均衡追求的是“各节点负载差不多”。等开销负载均衡(Equal-Cost Load Balancing)的逻辑完全不同:它认为负载均衡不是目的,完成所有任务的总开销最小才是目的。简单说,它允许某些节点更忙、某些节点更闲,只要整体完成时间最短、系统开销最低就行。

举个例子:一个计算密集型任务,放在GPU节点上可能20分钟跑完,放在普通CPU节点上要跑2小时。如果按“等负载”的思路,可能因为CPU节点空闲就把它调度到CPU节点上,结果是任务变慢、GPU节点闲置,看似均衡实则浪费。而“等开销”的思路会优先把任务放到GPU节点上,哪怕GPU节点已经很忙,因为从全局角度,把任务放到最合适的硬件上才是总开销最小的方案。

7.2 数学直觉:一种加权最短路径思想

从设计理念上说,等开销负载均衡的核心可以概括为一个优化模型:把每个可选目标(节点、链路、存储路径)视为带权路径,权重由实时开销估值决定。负载均衡器每次决策时,都会在满足约束条件的前提下,选择全局开销最小的那条路径组合。它和传统方法的区别,相当于把“平均分配”升级成了“最小成本流分配”,顺序也变成了先建立开销模型,再求解最优分配,最后做决策和调度。

这种思路在跨数据中心、混合云、异构集群场景下尤其有价值。不同节点间算力差异大,网络带宽差异大,存储路径的延迟差异也大,传统“一视同仁”的均衡策略天然不适用。

7.3 实际落地时怎么操作

要做到等开销,首先要解决“开销怎么测”的问题。我们团队的做法是搭建一个轻量的集群遥测系统,持续采集以下指标:

指标类别具体采集项用于什么
节点算力CPU利用率、内存带宽、GPU利用率、排队任务数评估节点当前承载能力
任务历史最近N个同类任务的实际完成时长估计新任务的预期耗时
通信链路各链路的带宽利用率、拥塞丢包率、往返延迟评估跨节点通信成本
存储状态并行文件系统的I/O吞吐、元数据延迟评估数据读取/写入瓶颈

然后将这些指标归一化成统一的“开销分值”,调度器在分配任务时直接优化开销总分。实际操作中,不需要考虑得特别复杂,可以对不同任务类型设置不同的权重:通信密集型任务重点看网络开销权重,计算密集型任务重点看算力权重,I/O密集型任务重点看存储权重。

我在团队里做过对比:采用等开销策略后,混合负载下的任务完成率显著提升,平均排队时间也大幅缩短。核心收益在于它把“隐性开销”显式化了——网络拥塞、存储排队、异构算力差异,这些过去容易忽略的因素,全都变成了调度器可感知、可优化的对象。

8. 实操层面的几个关键细节

8.1 把节点健康状态纳入均衡决策

负载均衡做得好不好,前提是“节点数据”准不准。我们生产集群用Slurm自带的slurmctld+SlurmDBD做记账和健康检查,同时配合Prometheus主动拉取Node Exporter、DCGM、Netdata等指标。

更关键的是:不能只看当前状态,还要看趋势。我们会在调度器前方加一层“节点健康度预测”:如果一个节点在过去30分钟内发生过多次心跳超时或NVIDIA驱动报错,即使当前报告空闲,也不会立刻把新作业调度过去。这个机制比单纯的“当前空闲”判断要可靠得多。

8.2 给调度决策留一个观察窗口

动态调度系统最怕的就是“抖动”。节点负载是波动曲线,如果调度器每次看到某个节点稍微空闲就立刻把任务搬过去,很可能刚搬过去,节点负载又开始上涨,于是又触发下一次迁移,形成“抖动-迁移-再抖动”的恶性循环。

我们的做法是在策略层加一个观察窗口算法:调度器对一个候选节点的负载数据做局部加权平均,只有当这个平均值持续偏离目标阈值超过一个设定周期(比如120秒)时,才真正触发重分布动作。本质上给调度器加了个低通滤波器,滤掉瞬时尖峰。

8.3 迁移前先检查点,再走应用层配合

做动态迁移时,如果直接杀进程再重新拉起来,代价极高。我们一般按这个顺序执行:

  1. 负载均衡器判定某节点需要卸载任务;
  2. 通知任务管理器(如SLURM、或MPI应用内的检查点库)在下一个一致时间点保存检查点;
  3. 检查点数据写入并行文件系统;
  4. 新节点从检查点恢复;
  5. 恢复完成后,原节点的资源即可释放。

这里有个小技巧:迁移前先确认新节点上是否有老节点需要的本地数据。如果有,先把数据用rsync或分布式存储预置过去,避免恢复时再从慢速存储拉数据。预置这一步做得好,迁移停机时间能从分钟级降到秒级。

9. 总结一下我的个人建议

高性能计算的负载均衡,说难也难,说简单也简单。难是因为它涉及的面太广,从调度器到并行框架,从网络到存储,每一层都有均衡的课题;简单是因为核心逻辑就一句话:端到端的瓶颈在哪,就把均衡策略的重心放到哪。

给正在做相关工作的朋友几点实在建议:

  1. 不要一开始就追求完美均衡。先把基础的数据采集、健康检查、拓扑感知做扎实,再逐步迭代策略,收益会更明显。
  2. 均衡不是目标,吞吐才是。总有一些分配方案,从表面看不够均衡,但整体吞吐更高——等开销负载均衡的意义就在于此。不要为了“看起来均衡”而牺牲实际效率。
  3. 监控一定要做全链路。我见过一个AI训练集群,GPU利用率长期偏低,排查到最后发现罪魁祸首是存储热点和网卡哈希不均,跟GPU本身毫无关系。只盯着单一维度看均衡,出了瓶颈你都找不到在哪儿。
  4. 调度策略永远是“适度优先”。无论是支配节点、划分数据、还是触发迁移,都要配合监控数据和历史画像来决定,而不是靠拍脑门。动态反馈调节,才是HPC负载均衡的正确打开方式。

最后,如果你刚接触HPC,建议先从SLURM的调度配置开始,把拓扑感知和回填策略调明白,再逐步往并行框架和网络层深入。每一步都有对应的坑,但踩过之后,你再看整个集群的运行状态,会有一种“豁然开朗”的感觉。整个系统的吞吐、时延、资源效率,说到底都系于“均衡”这两个字上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询