有一次旁听技术分享,讲者放了一张集群规模变化的曲线,随后又放了一张模型迭代周期的曲线。两条线并没有朝着同一个方向走。规模在涨,单个版本的训练周期却时快时慢,有时甚至比小集群时期更难预估。台下有人追问原因,得到的回答很朴素:机器多了,等机器的时间也多了。
这件事值得单独拿出来说。关于算力,行业里最常见的叙事是“越多越好”,但真正在一线调过任务的人往往有另一种体感:规模扩大的同时,系统的隐性成本也在同步放大,只是它不出现在采购清单上。
先把“快”拆成几段
一次训练任务从提交到拿到结果,中间要经过排队、拉起环境、加载数据、切分并行、频繁同步、写检查点。真正让加速卡满载运转的时间,往往只是其中一段。
规模小的时候,环节之间的摩擦不明显,任何一步慢一点都能靠人盯着补上。规模上去以后,每一步的波动都会被放大。数据读取慢半秒,几百张卡就一起空转半秒;某个节点偶发故障,整个同步就要等它回来。这时候感受到的“慢”,其实不是单卡性能下降,而是等待的占比升高了。
所以讨论算力时,只问“有多少”意义有限,更该问的是“有多少时间真的在干活”。这个比值不写在规格书里,却直接决定迭代节奏。
搬运数据这件事,被低估得厉害
很多人把注意力放在计算单元上,觉得它是瓶颈。但在实际任务里,相当一部分时间花在把数据搬到计算单元附近。存储、网络、内存之间的往返,一旦某一环带宽跟不上,再强的计算单元也只能等。
更麻烦的是,这种等待不会报错。任务照常跑,指标照常打印,只是单位时间内的有效产出变低了。如果没有细粒度的监控,很难意识到问题出在搬运环节,而容易归因成“模型太大”或“框架不行”。
有些团队会先做一轮profiling,把每个阶段的耗时摊开看。这一步不产生任何新能力,但常常能挤掉大量无效等待。相比继续加机器,这种做法的成本低得多,只是它不那么显眼。
调度的颗粒度决定浪费程度
集群里同时跑着许多任务,大小不一。如果调度单位太粗,一个大任务占着资源却用不满,小任务又排不进去,整体利用率就被拉低。反过来,如果把资源切得太细,通信和协调的开销又会上来。
这里没有通用解法,只有取舍。有的团队采用弹性策略,在夜间把空闲资源让给离线任务;有的团队把长任务和短任务分池管理,避免互相拖累。共同点是,他们都在承认一件事:机器是有限的,而需求不是。
也正因为如此,算力的真实成本不只是采购价,还包括闲置、碎片和协调带来的损耗。只看账面规模,很容易高估自己的实际能力。
什么时候不该加机器
有几类情况,扩容的收益会明显打折。
- 任务本身并行度有限,切到一定规模后通信开销超过计算收益;
- 数据管道尚未理顺,上游供给速度跟不上下游消耗速度;
- 监控体系不完整,无法判断瓶颈在哪一环,扩容只是把问题掩盖得更久;
- 团队对现有资源的使用方式尚未统一,同一类任务存在多种低效实现。
这些问题的共同点在于,它们都不是靠堆硬件能解决的。先花一两周做梳理,往往比直接下单更划算。
把算力当成一项要被管理的资源
更务实的视角是,不要把算力看作一次性购入的资产,而当作一项需要持续经营的能力。它包含硬件规模,也包含调度策略、监控覆盖、故障恢复速度和团队的使用习惯。
当这些配套跟上时,同样的机器能跑出更多有效工作;当它们缺位时,追加投入的效果会被稀释。这不是否定扩容的价值,而是提醒投入的顺序:先弄清楚时间花在哪里,再决定买什么。
回到开头那两条没有同步的曲线。它们的背离并不是意外,而是提示了一个常被跳过的环节——在追求更多之前,先让已有的部分跑满。对多数团队来说,这可能是当下更容易拿到的那部分提升。