ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

高性能计算负载均衡实战:从等开销模型到动态重划分调优

高性能计算负载均衡实战:从等开销模型到动态重划分调优 前阵子帮客户排查一个算例512个节点跑分子动力学集群整体利用率已经堆到了97%可作业还是比预计的时间慢了整整两倍。监控面板一拉问题一目了然512个节点里有498个早早收了工剩下的14个节点硬扛着最后几块重载荷整个作业的结束时间被这14个节点死死锁住。这就是高性能计算负载均衡最典型的翻车现场——不是算不动是分不均。如果你也管理过超算集群、调过MPI并行程序或者写过OpenMP异构循环大概率遇到过类似的场景CPU看起来都在跑利用率挺高但墙钟时间就是下不来又或者明明任务量差不多某些进程总是能提前几十分钟干完然后干等着。这篇文章不绕弯子直接讲清楚高性能计算场景下负载均衡的几个核心问题——等开销负载均衡到底在追求什么、三层负载均衡分别怎么做、静态和动态方案怎么选、迁移成本怎么量化最后用一次真实的调优复盘带你走一遍排查链路。无论你是刚接触并行计算的学生还是在集群上跑业务的工程师都能在里面找到直接能用的判断思路和操作方法。1. 等开销负载均衡为什么分得平均不等于跑得快1.1 耗时均衡才是真目标数量均衡只是幻觉很多人第一次接触负载均衡直觉反应是把任务平均分给每个核。这个直觉在玩具场景下成立到了真实的高性能计算里基本是错的。原因很简单任务是平均了但每个任务的计算开销不一样。网格里的单元有大有小粒子在空间里的密度分布不均匀稀疏矩阵的非零元聚集在某些区域自适应网格加密之后有些块的分辨率比其他块高一个数量级。你给每个进程分了同样多的网格块但有的块算起来是十倍量级的工作量最后的结果就是早早干完的进程在MPI_Barrier那里发呆而少数进程在慢悠悠地磨。等开销负载均衡equal-cost load balancing要解决的核心问题就是让每个处理单元分到的总执行开销尽量相等而不是让任务数量相等。这里的开销单位不是FLOPs不是数据条数而是墙钟时间本身。划分的最终目标是让所有进程的完成时间趋同使得并行度所能带来的加速比尽可能接近理论值。1.2 木桶效应背后的数学为什么调度的天花板是最大任务负载不均衡的代价可以用一个很简单的模型看清楚。假设一个并行程序的总墙钟时间为 T_total可以近似写成T_total T_serial max(T_0, T_1, ..., T_{N-1})其中 T_i 是第 i 个进程处理自己那份工作所花的时间。整个作业的结束时间由最大的 T_i 决定而不是由平均值决定。这就是木桶效应桶能装多少水取决于最短的那块板跑得最慢的进程。为了量化不均衡程度业界常用一个指标叫负载不均衡度imbalance T_max / T_mean如果 imbalance 是 1.2意味着最慢的进程比平均慢了20%那么哪怕你有百万核在并行整个作业相对于理想情况的加速比上限也只有 1/1.2 ≈ 0.83。注意这个 0.83 是天花板实际还到不了因为进程间还要通信、还要等待。换句话说负载均衡做得越差加再多的核都是在给最慢的进程陪跑。我见过最夸张的一个算例imbalance 到了 3.0 以上。那个作业挂在 1024 个核上跑了 6 个小时其中真正有效计算的时间只有 2 个小时剩下 4 个小时全部耗在了等待最慢进程上。这就是为什么说负载均衡在高性能计算里不是优化项而是基本项——不均衡的并行作业规模越大浪费越离谱。1.3 开销模型的三个组成部分要把等开销落到实处的第一步是建立一个可用的开销模型。一个进程的墙钟时间大体由三部分组成计算开销compute进程实际执行浮点运算、逻辑判断的时间。这部分取决于算子效率、向量化程度、缓存命中率。同样 100 万次循环有的代码吃满 AVX512 跑得飞快有的被分支预测拖累慢如蜗牛。访存与通信开销memory communication进程读取内存、访问远端数据、跟邻居进程交换消息的时间。高性能计算里计算和通信经常重叠但重叠不掉的通信时间会直接加在墙钟上。I/O 开销I/O读写文件系统的时间。检查点写出、结果落盘、中间数据的交换在高并发下极易变成新的瓶颈。于是每个进程的预估开销可以写成T_i ≈ compute_i comm_i io_i等开销负载均衡要做的就是找到一个划分方式让所有 T_i 尽可能相等。注意这里的难点在于compute 和 comm 往往是此消彼长的你把数据切得越碎越均匀进程间的通信边界就越多通信开销反而增大。所以这不是一个简单的最小化最大任务问题而是一个带约束的优化问题在通信开销的约束下让计算负载尽量均匀。理解了这个模型后面所有选型和参数调整才有依据。否则你只是在盲调。2. 三层视角作业调度、进程通信与线程并行的负载均衡搞高性能计算的人容易犯一个毛病一提负载均衡就只想到 MPI 进程怎么迁移。但实际上负载均衡发生在至少三个层级每一层都有自己完全不同的机制和工具而且三层之间还会相互影响。2.1 节点级作业调度器对资源的第一次切蛋糕节点级的负载均衡发生在作业还没开始跑的时候。Slurm、PBS 这类作业调度器负责决定这个作业分到哪些节点、分多少核、内存怎么配。这一层如果切得不合理后面进程级和线程级的优化都白搭。以 Slurm 为例它的 SelectType 参数决定了资源分配的粒度。默认情况下如果配置成整节点分配一个 512 核的作业可能会吃掉 16 个 32 核节点哪怕别的作业只需要 1 个核也没法挤进这些节点。反之如果你把粒度调成核级CR_Core调度器就可以把不同作业拼到同一节点上。拼节点提升了整体利用率但也带来了新的问题同一个节点上的不同作业会争抢内存带宽和 L3 缓存导致实际单核性能下降。所以调度策略要在利用率和性能稳定性之间做取舍这不是负载均衡调出来的而是集群规划阶段就该定好的事。另一个节点级的常见坑是异构节点。现在的集群很少有纯一色的配置——有的节点带 GPU有的节点有大内存有的节点是旧的 CPU。调度器分配作业时会尽量匹配需求但现实里经常发生作业里分配到的节点性能差异巨大的情况。这种场景下即便进程级负载均衡做得再好慢节点的墙钟时间依然是全作业的瓶颈。2.2 进程级MPI领域分解与动态迁移节点级分好资源之后真正进入算例内部的是进程级负载均衡。这一层的基本思路是领域分解domain decomposition把整个计算域切成若干块每块分配给一个 MPI 进程进程之间通过消息传递交换边界数据。静态领域分解在计算开始时切一刀就完事适合载荷不随时间剧烈变化的算例。但很多真实应用会做自适应网格加密AMR、粒子会迁移、相场会发生相变初始均匀的划分在中途就会变得极度不均匀。这时候就需要动态重划分周期性地收集每个进程的开销数据用图划分工具把计算域重新切一遍然后把需要迁移的数据块通过网络搬到新归属的进程上。进程级负载均衡的三个关键动作是检测统计每个进程的实际耗时、决策算出一个新的划分方案、迁移实际搬运数据。检测周期、迁移的触发条件、重新划分的粒度都是这一层调优的核心参数。2.3 线程级OpenMP调度策略与共享内存下的新问题进程级之下还有线程级负载均衡。典型的场景是进程内用 OpenMP 并行化循环每个线程处理一部分迭代。OpenMP 的 schedule 子句提供了几种策略static编译时就定好每个线程迭代范围开销为零适合每次迭代耗时差不多的规则循环。dynamic运行时从共享队列里动态取块块大小由 chunk 参数控制适合迭代耗时差异明显的场景。guided迭代块大小开始时很大后来越分越小相当于自动缩小版 dynamic。auto把选择权交给编译器实际效果取决于编译器和平台。我通常的建议是先运行一次带 OMP_WAIT_POLICYactive 和 OMP_DISPLAY_ENV 的实验看看线程到底在等什么。如果等待时间集中在隐式 barrier 上说明线程之间的负载不均衡把 schedule 改成 dynamic 或者 guided再试验不同的 chunk 值。chunk 太小会让调度开销变大太大又起不到平衡作用这个平衡点只能靠实测找。线程级负载均衡还有一个容易被忽略的维度线程亲和性。如果线程在运行过程中被操作系统在不同核心间来回搬移每次搬移都伴随缓存失效性能损失可能高达 20%。通过 OMP_PLACEScores 和 OMP_PROC_BINDtrue 把线程固定到物理核心上很多时候比调整 schedule 策略更有效。2.4 三层均衡的关系谁有最终解释权这三层负载均衡不是独立的它们之间存在明显的层级关系。作业调度器先决定节点归属和核数分配这划定了进程能使用的资源边界MPI 进程在此基础上决定数据怎么切、消息怎么传进程内部的 OpenMP 线程再在共享内存里做最后一层调度。如果各层之间不协调就会产生互相拆台的局面。举一个真实的例子MPI 动态重划分为了减少通信倾向于把有大量数据交换的进程放到同一节点上因为节点内通信走共享内存速度快但调度器当时为了利用率的考虑把进程打散到了不同节点。结果 MPI 层省下来的通信在节点间网络上双倍赔了回去。反过来也有OpenMP 动态调度把工作在线程之间搬来搬去导致 MPI 层统计的每进程开销完全失真重划分为的依据全是噪声。我的经验是在做任何一层负载均衡优化之前先把三层的资源绑定关系画清楚哪些进程在哪些节点、哪些线程绑定哪些核心。然后确定每一层的目标是什么——节点层追求整体资源利用率进程层追求 wall time 均匀线程层追求 barrier 等待最小化。目标清晰了才不会各调各的。3. 静态负载均衡与动态负载均衡的选型逻辑负载均衡策略大致分两个流派静态的和动态的。很多人默认动态比静态高级其实完全不是这样。选型的依据是应用本身的特性而不是谁的实现更复杂。3.1 静态方案与图划分工具METIS/Scotch静态负载均衡的核心工具是图划分。把计算域抽象成一个图顶点是需要计算的数据块边代表数据块之间的依赖和通信量。然后问题就变成了把这个图切给 N 个进程使得每个子图的顶点数或加权顶点开销大致相等同时跨子图的边权尽量小。这是一个经典的图划分 NP 难题工程上靠启发式算法解决。最常用的两个库是 METIS 和 Scotch。METIS 的多级 k-way 划分multilevel k-way partitioning思路很直观先把图粗化到很小的规模在粗图上做划分再逐层细化回原图同时做边界优化。用起来也很简单# 用 gpmetis 把网格图切给 512 个进程 gpmetis mesh.graph 512 # 输出 mesh.graph.part.512每行对应一个顶点的归属静态方案的最大优势是零运行时开销。所有划分工作在启动时一次性完成之后进程完全不需要交流划分信息也不用迁移数据。对规则网格、稳定粒子的算例来说静态划分加上一点点手工优化就能把 imbalance 控制在 1.05 以内完全够用。但静态方案有个前提假设每个数据块的开销在运行期间不变化。如果你的算例中某块区域的物理过程突然变得剧烈网格需要加密、粒子数暴增静态划分就会失效而且你在运行中没有任何手段补救。3.2 动态方案工作窃取、任务池与自适应迁移动态负载均衡按实现方式可以分几类。第一类是工作窃取work stealing。每个工作线程维护一个本地任务队列自己队列空了就去别的线程的队列尾部偷任务。C 的 TBB、OpenMP 的任务模型task pragma、MPI 的应用也可以自己做一套类似机制。工作窃取的优势是响应快一个线程空闲的瞬间就能触发负载再分配。缺点是任务粒度要够小否则窃取来的任务通讯成本高而且它对数据局部性不友好——一个任务本来要访问的数据可能还在原线程的缓存里偷走之后要在新线程的缓存里重新加载。第二类是任务池task pool。所有任务放在一个共享队列里每个进程空闲了就去取下一批。好处是实现简单、均衡效果直接坏处是共享队列本身会成为热点进程多了之后锁竞争能把收益全部吃掉。工程上通常用分块chunking来缓解每次取一批任务而不是一个一个取。第三类是测量驱动的自适应迁移这是大型 MPI 应用中真正有分量的方案。基本流程是定期收集每个进程的开销统计数据每个数据块的计算时间、通信量。根据统计数据和当前图结构调用 METIS 重新划分。找出哪些数据块需要搬去哪些进程用 MPI 通信做迁移。Charm 里的 load balancer 框架就是这种思路的典型实现程序被分解成大量细粒度对象chares运行时系统周期性收集每个对象的负载信息然后通过 GreedyLB、RefineLB 等策略决定哪些对象迁移到哪里。这种方案对不规则应用自适应网格、图算法、粒子模拟效果非常好因为它的均衡周期和迁移动作都由运行时统一调度应用代码不需要自己实现复杂的数据搬运逻辑。3.3 结合策略受控冗余与消息驱动的运行时静态和动态不是互斥的实际生产环境里更多是两者结合。一种常见组合是粗粒度静态 细粒度动态先用静态划分保证大部分数据的位置稳定减少通信和迁移量然后对运行中识别出的热点区域只做局部调整——比如把某个负载过重进程里的一部分任务切给邻居进程。这种局部修正方案refinement在 METIS 里对应的是 refine 阶段的算法在 Charm 的 RefineLB 里也是这个思路不是全部打散重排而是只搬走那些明显超载的对象。另一种结合是消息驱动message-driven的运行时思想。作业被切分成大量细粒度任务任务的执行顺序不按每个进程一个循环体来而是按消息到达顺序来。谁有数据谁的活就开跑运行时就天然实现了负载均衡。这套思路在异步多物理场耦合、不规则图计算里特别有效代价是任务调度本身有开销而且调试难度比传统的 SPMD 模式高一个量级。3.4 选型决策表说了这么多不如直接给一张选型表是我自己平时在项目里用的应用特征推荐方案理由规则网格、载荷稳定静态图划分METIS零运行时开销结果可预期粒子数随时间迁移周期性测量 自适应重划分能应对相变和密度偏移自适应网格加密AMR动态重划分按层局部 refine热点区块局部出现全局重排代价太大大量短小任务、依赖关系弱任务池 / 工作窃取响应快均衡粒度细通信占比高、数据局部性强尽量静态只在必要时做小规模迁移迁移的通信代价可能超过均衡收益多物理场强耦合消息驱动运行时任务天然按数据可用性调度这张表不是教条核心是你得先回答清楚一个问题这个应用的开销是算出来的还是搬出来的。算出来的话怎么切都不亏搬出来的话频繁迁移反而会亏得更惨。4. 迁移开销与平衡周期等开销负载均衡的隐性成本动态负载均衡听起来美好但有一个致命的问题常常被忽略重新划分是需要搬数据的搬数据是有代价的。如果为了平衡而付出的迁移代价超过了不平衡本身带来的损失那这个负载均衡动作就是在帮倒忙。4.1 迁移的代价构成一次数据迁移的墙钟代价大约由三部分组成传输时间要搬的数据量除以可用带宽。假设要搬 200MB 数据InfiniBand 有效带宽大约 6GB/s那么传输本身大约 33ms。但注意这是理想值实际还要考虑网络拥塞和 MPI 协议的封装开销。中断损失迁移期间源进程和目标进程都无法继续正常计算。如果迁移是阻塞式的这段停摆时间会直接加在墙钟上。冷缓存损失数据搬过去之后目标进程的缓存是冷的第一次访问要重新从内存甚至远端读一遍。这个损失在小数据块频繁迁移时特别明显常常被低估。可以给一个粗略的决策公式。假设一次迁移的总代价是 M 秒如果当前 imbalance 是 η平均每个进程的负载是 L那么理想情况下平衡后的收益大约是benefit ≈ (η - 1) * L只有当 M (η - 1) * L 时这次迁移才值得做。举个例子平均进程负载 L 120 秒imbalance η 1.3那么潜在收益是 0.3 × 120 36 秒。如果一次重划分加数据迁移要花 40 秒那就不如不迁让那 20% 的慢进程继续慢着整体反而更快。4.2 什么时候不值得平衡根据上面的公式有几类场景明确不适合做动态负载均衡第一类是短作业。总运行时间只有几分钟的应用光收集负载数据、做划分决策就要花掉几十秒收益无从谈起。短作业直接用静态划分或者干脆靠作业调度器的资源打包来保证基本均匀。第二类是通信密集型的应用。这类应用里每个数据块跟邻居的通信量很大重新划分会把原本紧密的邻居关系打散增加的通信量可能远超均衡省下的时间。判断标准就是看每个数据块的计算时间 / 通信量比值这个比值越低动态迁移越不划算。第三类是负载波动频繁但幅度小的应用。每次重划分都要搬数据搬完还没来得及享受平衡的收益负载又变了整个系统一直在迁移-恢复-再迁移的循环里空转。这种情况不如把任务粒度调大一点让每个进程手里都留点余量靠任务池的柔性来吸收波动。4.3 平衡周期的自适应策略如果确定要做动态负载均衡下一个问题是多久做一次。固定周期往往不是最优解周期太短迁移开销吃掉收益周期太长不均衡持续太久。我习惯的做法是做一个滑动窗口判断。维护每个进程最近几轮的开销统计计算窗口内的负载方差或者说变异系数coefficient of variation标准差除以均值。当变异系数超过某个阈值比如 1.1 或 1.2才触发一次重划分。这样在负载平稳时系统根本不会启动重划分流程零开销负载一旦发生相变比如粒子流注入某个区域检测机制会在几个统计周期内发现异常及时触发迁移。触发频率还要用指数退避的思路来控制如果刚做完一次重划分下一轮统计又抖动了一下不要急着再迁先把阈值调高一点给系统一点稳定时间。反之如果负载一直在缓慢漂移就降低阈值让后续重划分更频繁但每次迁移的规模更小用小步快跑来追踪变化。这里要特别提醒负载数据的收集本身也会产生通信。如果你用全局 MPI_Allreduce 来汇总所有进程的负载信息每轮统计都做一次全收集那统计频率就得克制否则你会发现自己花在监控上的时间比干活还多。更优的做法是用非阻塞收集或者只在部分进程之间做局部汇总减少通信量。5. 一次真实调优复盘MD算例从2倍超时到准点收工前面讲了不少理论和策略这一节用一个真实场景把整条链路串起来。这个案例是我之前帮一个做材料模拟的团队处理过的问题现象和排查方法都有代表性可以直接迁移到类似场景。5.1 现象97%利用率下仍超时两倍那个算例是典型的分子动力学模拟大约 8,400 万个原子跑了 512 个节点共 8,192 个 MPI 进程。客户的反馈是作业的预计运行时间是 8 小时实际跑完花 16 小时超时整整一倍。麻烦的是集群监控显示整个作业期间节点平均利用率高达 97%——按常理利用率这么高怎么也不该慢一倍。第一反应自然是怀疑自研的 MD 主循环是不是退化了、数值积分器是不是有 bug。但把 MPI 层面的分析数据拉出来之后真正的嫌疑犯浮现出来了所有进程在 MPI 同步点的等待时间高得离谱。虽然每个 CPU 都在忙但有相当一部分忙是在等别人发数据——MPI 库的自旋等待会把 CPU 利用率撑满实际干的是无用功。5.2 排查链路从监控指标一路挖到分区不均衡排查过程分了几步第一步统计每个进程的墙钟时间分布。方法很简单在程序主循环外面包 MPI_Wtime跑 1000 步然后把每个进程记录的耗时归个总。结果非常清晰大概 6,800 个进程的耗时在 90 到 100 秒之间剩下 1,400 个进程的耗时分布在 120 到 180 秒之间最慢的进程 182 秒最快的 88 秒。imbalance 算下来是 182 / 平均约 105≈ 1.73。这基本坐实了负载不均衡是超时的直接原因——平均负载只有 105 秒的情况下最慢进程拖到了 182 秒整个作业的进度被它锁定。第二步查负载在空间上的分布。MD 模拟里用标准的方法把空间切成三维网格每个进程负责若干格子。把每个格子内的原子数量拉出来画个热图发现原子密度在模拟盒子的一侧显著偏高——因为模拟过程中有原子从边界蒸发后在另一侧重新注入粒子分布已经跟初始均匀状态大相径庭。初始划分是均匀切格子每个进程负责同样数量的格子但格子里原子数量差了三倍计算量自然差了快三倍。第三步确认通信不是元凶。用 mpip 统计 MPI 通信量发现进程间的消息总数和总量都在合理范围网络没有明显拥塞。这说明问题确实出在计算负载分配上而不是消息传递上。5.3 调整动作与复算过程定位之后做了三个调整按收益大小排序一是把划分策略从启动时切一次改为周期性重划分。每 1000 步收集一次每个格子的原子数用这个统计量给格子加权然后调用 METIS 做一次带权的重划分。原子数多的格子权重大划分后每个进程分到的总权重尽量相等。数据迁移用非阻塞的 MPI_Isend/Irecv 来做迁移过程与后续计算重叠尽量不额外占用墙钟时间。二是给 OpenMP 层加了一点余量。这个算例每个 MPI 进程内开了 4 个线程主循环里有一段线程负载不均的代码我把它的 schedule 从 static 改成了 guided并设置了 chunk 大小为 16。这个改动不解决大尺度不均衡但能把线程间的 barrier 等待时间压下去。三是把负载检测周期设为自适应。刚开始用固定 1000 步做重划分观察两轮后发现粒子密度的变化速率不稳定——有些阶段变化快有些阶段基本不变。改成上面说的变异系数触发机制之后平稳阶段基本不触发重划分剧烈阶段能在一两个周期内跟上变化。三个调整做完重新跑了一遍同样的算例。结果如表指标调优前调优后最慢进程耗时182 秒112 秒平均进程耗时105 秒107 秒imbalance1.731.05中间 1000 步的总耗时182 秒被最慢进程主导112 秒每次迁移的数据量无约 2% 的总数据整体作业从 16 小时缩到了 9 小时出头虽然离理论 8 小时还差一点但已经算是准点收工了。那 1 小时左右的多余时间主要是重划分和迁移开销还有 OpenMP 层残余的 barrier 等待——我判断这已经是性价比的平衡点再往下抠的投入产出比就不划算了。5.4 这个案例留给我的几个判断复盘下来有几个点值得记住。第一高 CPU 利用率不等于高效率MPI 的忙等机制会掩盖真实的等待时间看利用率做性能判断会被误导。第二MD 这类粒子模拟的负载不均衡是动态发展的初始均匀划分撑不了多久必须在运行中持续检测和重划分。第三迁移代价是真实存在的调优的目标不是把 imbalance 做到 1.0而是把不均衡损失 迁移开销的总和做到最小——在这个案例里imbalance 从 1.05 压到 1.02 要付出的迁移代价远大于收益所以我选择了停在 1.05。6. 排查负载均衡问题的检查单与常用工具最后给一套可以直接用的排查方法。性能问题排查最忌讳的是凭感觉乱调一套结构化的检查链路能帮你快速定位问题到底出在哪一层。6.1 第一张检查单等待时间与利用率拿到一个跑得慢的并行作业先回答四个问题每个 MPI 进程的实际计算时间分布如何把主循环包一层 MPI_Wtime 总计时按时间排序算一下 imbalance T_max / T_mean。如果大于 1.15负载不均衡基本坐实了。进程都等在哪里用 mpip 或者 TAU 看 MPI 调用明细。如果 MPI_Barrier、MPI_Allreduce 的等待时间占比很高说明是全局同步点上的不均衡如果 MPI_Send/Recv 的时间高可能是通信热点或拓扑问题。进程内的线程在等什么导出 OpenMP 运行时信息关注隐式 barrier 的等待时间。设置 OMP_WAIT_POLICYactive 后观察线程的自旋行为。节点间资源有没有差异登录到慢节点上看 CPU 频率、内存带宽可以用 stream 基准测一下排除硬件降频或邻居作业干扰的可能。这一组问题的核心目的是把问题限定在进程间负载不均通信开销过大节点性能差异三个方向之一避免在错误的层面浪费时间。6.2 第二张检查单通信热点与数据分布如果确认是进程间负载不均下一步要定位是哪里不均空间分布把你的计算域按进程划分画出来跟实际负载原子数、网格单元数、非零元数叠加看是不是某些高密度区域被切碎了、某些低密度区域占着大块地盘。时间演化做两组不同时间点的负载采样对比负载热点是否在迁移。热点固定不动考虑静态划分加上局部 refinement热点持续漂移直接上动态重划分。通信矩阵用 TAU 或者 mpip 导出进程间的通信矩阵看通信量是否集中在少数进程对上。如果重划分把通信密集的进程对拆开了反而会让总通信量暴涨这时候要权衡。6.3 常用监控工具组合工具不在多够用就行。我平时用的一套组合是工具用途使用方式mpip / mpiPMPI 通信与等待时间分析mpirun 前设置 mpiP 环境变量运行后生成报告TAU / HPCToolkit全面的性能剖析含进程内热点重新编译插桩按需采样Caliper轻量级性能标注适配大规模运行在关键循环处插入标注宏mpstat / pidstat单节点粒度观察 CPU 利用率和上下文切换登录到慢节点上看瞬时状态Slurm sacct / sstat作业级资源使用汇总查看作业平均利用率和节点分布需要注意的是插桩工具本身就带开销。8,000 核以上的作业用插桩分析要选采样模式而不是全量追踪模式否则你测出来的不是问题程序的性能是插桩工具的性能。6.4 新手最容易犯的五个错误只按数据量切分不看计算密度最经典的错误切完 1000 块数据不等于 1000 份工作量。过度动态化一切负载均衡就上工作窃取、动态迁移结果迁移开销吃光了收益。先算一笔账再动手。忽略 NUMA 和内存带宽差异同一个节点上两个 NUMA 域的访存性能可能差 30%线程亲和性不绑定负载再均衡也白搭。只在启动作一次划分规则算例没问题但粒子模拟、AMR 这类应用负载会漂移必须周期检测。用全局 barrier 做负载检测barrier 只告诉你有人在等不告诉你是谁在等、等了多久、为什么等。要拿到真实的进程耗时得在关键计算段外面单独计时而不是依赖同步点的等待统计。排查负载均衡问题本质上就是不断追问三件事谁在慢、为什么慢、把它的压力挪给谁最划算。检查单负责回答前两件第三件靠迁移代价的估算来定。掌握这套思路之后再遇到利用率很高但作业就是跑不快的情况你至少能在一小时之内把问题框定在正确的方向上。
返回列表