聊个实际问题:我见过不少企业的 GPU 集群,监控面板上的利用率常年只有 30%~50%,但业务方天天喊卡,新任务排队等卡,底层却有大把算力在空转。这种局面通常不是显卡不够,而是算力没被好好调度起来,尤其是当集群里还混着 A100、4090、国产加速卡的时候,情况会更乱。
GPU 利用率提升 1.3 倍,听起来像营销话术,但如果你真的把训练、微调、推理任务混部到同一批异构 GPU 上,并且把显存、算力粒度切分到合理水平,这个数字完全能达到。我最近在整理 ZStack AIOS 这类智算操作系统的落地经验,核心就一句话:把异构算力当成一个可调度的池子,而不是一台台孤立的物理机。这篇文章就围绕这句话展开,讲清楚 GPU 利用率为什么低、异构算力怎么池化、具体调度怎么操作、监控排障怎么做,适合正在折腾 GPU 集群、被利用率折磨的运维和平台工程师参考。
1. 先聊清楚:GPU 利用率低下的病根在哪
1.1 单卡利用率低:采集指标与真实水位
很多团队说“利用率低”,其实是用错了指标。nvidia-smi看到的 GPU-Util 是采样瞬间 SM(流处理器)的忙碌比例,它不代表真实吞吐。我用一个例子解释:一个推理服务每个请求只有 200ms,其中 GPU 计算只占 50ms,剩下 150ms 在等数据从 CPU 侧拷贝、等 Python 做预处理和后处理。这个服务跑满 100 QPS 时,nvidia-smi可能只显示 30%~40%,因为 GPU 总是在“等活儿”。此时就算把利用率数字刷上去,QPS 也上不去,因为瓶颈在数据传输和预处理,不在计算本身。
真正的利用率要看三件事:SM 占用率(SM Occupancy)、显存带宽利用率、以及端到端吞吐。SM 占用率决定计算单元有没有被塞满,显存带宽决定访存密集任务有没有卡在 HBM 上。很多运维只看 GPU-Util,忽视了帧缓冲显存使用——显存快满了但 SM 空闲,这种状态下任务不排队才怪。
另外,单卡利用率低往往是因为并发不够。比如微调一个 7B 模型,batch size 设成 1,一张 A100 哪怕满速跑,SM 占用率也上不去;把 batch size 凑到合适区间,把 TensorCore 喂饱,吞吐能翻番。这就是为什么做 GPU 利用率优化时,第一步不是调调度器,而是先看模型服务的请求并发和 batch 策略。这块不解决,调度层面怎么切分都是白搭。
1.2 传统虚拟化在异构算力面前的尴尬
传统私有云那套 VM 思路,碰到 GPU 特别别扭。原因有几点:
- GPU 设备不能像 CPU 那样轻易超卖。CPU 可以配 oversubscription,因为任务多数时间在等待;GPU 是计算密集设备,超卖后直接表现为显存溢出或任务相互拖慢。
- PCIe 设备直通(PCI Passthrough)虽然性能好,但一台机器最多直通给少量虚拟机,而且 VM 不感知 GPU 拓扑,无法做 NUMA/GPU 亲和调度。
- 传统虚拟化缺少“算力分组”概念,想给两个训练任务各分配 50% 的 A100 算力,如果不做 MIG 或 vGPU,基本做不到。
更麻烦的是异构。数据中心里往往有 A100、A800、4090、3090,还有国产加速卡。不同卡的显存大小、算力规格、驱动接口都不一样。传统平台只能按“机器 + 设备”粒度分配,用户申请一张卡,调度器只能找一张空闲卡塞进去,完全不管这张卡是否满足任务真实需求。结果是:重计算任务被分到弱卡上跑不动,轻任务占了强卡浪费资源。
1.3 ZStack AIOS 解决什么问题
ZStack AIOS 这类智算操作系统,本质是把 GPU、NPU、异构加速卡当成数据中心级资源来管理,而不是虚拟机附属设备。它在平台层做四件事:异构设备统一纳管、资源池化与细粒度切分、基于真实负载的调度、以及多集群统一监控。
我特别想强调“细粒度切分”这点。比如一张 80G 显存的卡,训练任务需要 40G,推理任务需要 20G,另外 20G 可以切给一个轻量服务。平台通过显存隔离和算力限额,让三拨任务互不干扰地跑在同一张卡上。这种能力直接改变了利用率下限:原来一张卡只能跑一个任务,现在可以跑两到三个。标题里说的 1.3 倍,在这种场景下非常现实。
要注意,细粒度切分不是简单的“显存瓜分”。算力份额、显存隔离、显存带宽争用都要考虑。如果只做显存隔离不做算力隔离,两个任务互相抢 SM,最终谁都没跑快。ZStack AIOS 的调度策略支持按显存、按算力、按卡三种粒度,实际部署时我建议优先用“显存 + 算力”组合。
2. 异构算力池化:从硬件异构到统一调度
2.1 异构算力到底指什么
异构算力这个词常被滥用。在数据中心场景,它至少包含三层含义:
- 芯片架构异构:NVIDIA GPU、AMD GPU、国产 GPU/NPU、甚至 CPU 的向量指令集。指令集不同,编程模型也不同,CUDA、ROCm、CANN 各自为政。
- 产品型号异构:同品牌不同代际,比如 A100 与 L40S,显存大小、NVLink 能力、TensorCore 代际都不一样。
- 使用方式异构:同一张卡可以跑训练、微调、推理、渲染,这些工作负载对资源的需求特征完全不同。
异构带来的最大问题是“不可替换性”。CUDA 任务无法直接跑到 ROCm 上,NPU 任务更挑算子适配。所以异构池化的第一步不是“统一到抽象层”,而是“资源打标签 + 声明式调度”。也就是说,每张卡都标注好架构、型号、驱动版本、剩余显存、当前算力份额,任务在提交时声明“我需要什么卡、多大显存、多少算力”,调度器再匹配。这一步不做好,后面所有优化都无从谈起。
2.2 设备插件与 GPU 直通/虚拟化的取舍
在 Kubernetes 体系里,调用 GPU 的标准方式是 device plugin。ZStack AIOS 底层支持标准 Kubernetes 生态,这也意味着很多 K8s 上成熟的 GPU 调度实践可以直接复用。
具体来说,有三种常见的接入方式:
- 直通模式(Passthrough):把整张 GPU 映射给一个容器,性能最好,但粒度最粗,一张卡只能给一个任务。适合少数独占场景,比如需要多卡通信的模型并行训练。
- 虚拟化模式(vGPU / MIG):把一张物理卡切成多个逻辑卡,支持显存和算力隔离。MIG 是 NVIDIA 官方方案,但只支持部分新卡;vGPU 依赖 NVIDIA vGPU 授权;国产卡各有各的切分方案。ZStack AIOS 这类产品会做兼容封装,让用户通过统一界面申请“2G 显存 + 20% 算力”这样的资源。
- 显存共享模式(如 CUDA MPS、池化技术):不切分物理设备,但通过进程级调度限制算力。MPS 适合把多个小推理任务塞进同一张卡,但要小心显存隔离缺失的问题——如果某个任务申请了过多显存,其他任务会被 OOM 牵连。
我的建议是,训练任务尽量用直通或多卡绑定的方式,推理任务和开发调试用 vGPU 或显存共享。全用直通,利用率很难提上去;全用 vGPU,训练性能又可能打折。混合使用才是正解。
2.3 一张表看懂常见调度策略
这里把我常用的调度策略整理成了表格,方便对照:
| 策略 | 粒度 | 适用场景 | 注意事项 |
|---|---|---|---|
| 整卡独占 | 1 张物理卡 | 大模型预训练、多机多卡并行 | 利用率低,但稳定 |
| MIG / vGPU 切分 | 1/7、1/4 等逻辑卡 | 中小推理服务、开发环境 | 需授权,算子兼容性要测 |
| 显存共享 | 进程级 | batch 推理、小请求高并发 | 必须有 cgroup/算力限额 |
| 算力限额 | 按计算资源百分比 | 混合部署、离线在线混部 | 与显存隔离配合使用 |
| 抢占式调度 | 队列级 | 多个团队共享集群 | 需要 checkpoint,否则任务被 kill 损失大 |
我不建议一上来就追求“全集群自动最优调度”。更靠谱的路径是:先用整卡独占跑通业务,再逐步放开 MIG/显存共享,最后再上抢占式。每一步都做容量评估,避免为了利用率数字牺牲稳定性。毕竟调度器再聪明,也扛不住业务自身不配合。
3. 实操:怎么把利用率从 60% 拉到 80%+
3.1 起步:摸清家底和真实负载
想提升利用率,先把集群的真实情况摸清楚。我通常会做一周的基线采集,记录每个节点每小时的平均 GPU-Util、显存使用量、SM 占用率、任务数量、排队时间。这里有个技巧:不要只看平均,要看 P95。平均利用率 60% 可能意味着高峰 90%、低谷 30%,调度器需要在高峰时把任务错峰,而不是无脑扩容。
采集工具方面,DCGM(NVIDIA Data Center GPU Manager)是首选,能拿到 SM 占用率、温度、功耗、显存带宽等细粒度指标。配合 Prometheus + Grafana 做监控大盘,能直观看到任务对资源的真实占用。
我见过一个客户,报表说资源利用率 55%,实际打开 Grafana 一看,白天开发环境的 Jupyter Notebook 占了 60% 的显存,但 SM 占用几乎为 0。这就是典型“显存放着不用”的场景。解决办法不是买新卡,而是给开发环境设置 idle 超时回收(比如 2 小时无操作自动释放资源),顺便把 GPU 共享权限收回,让开发人员按需申请而不是常驻占用。
3.2 任务调度策略:显存切分、算力限额与分布式排队
摸清基线后,就可以按资源需求把任务分类。我建议分成三类:重训练任务(需要整卡或多卡)、轻训练/微调任务(显存小于 40G、算力需求中)、推理任务(高并发、低延迟、显存需求多样)。
然后在 ZStack AIOS 里配置资源模板。比如给微调任务配置“20G 显存 + 30% 算力”的模板,多个微调任务可以共享一张 A100,但算力总量限制在 80% 以下,留出余量给推理任务应对突发流量。给推理任务配置“显存按需 + 算力上限 50%”的模板,推理服务本身有弹性,允许被调度器抢占,配合 HPA 实现副本伸缩。给开发环境配置“低优先级 + idle 回收”策略,避免资源被长期占用。
调度策略上,我强烈建议开启集群级分布式排队。很多平台只做单节点调度,如果一个节点没卡了,任务就在那等着,哪怕其他节点有空卡。分布式排队会把全局空闲资源统一调度,任务等待时间可以从小时级降到分钟级。ZStack AIOS 的调度器我实际观察下来,在几百卡规模下重新调度一个失败任务只需要秒级。
需要注意的是,队列优先级要配合业务方梳理。不能简单按提交时间先来先服务,而要考虑任务紧急程度和预估时长。我的做法是让每个训练任务提交时声明预计时长,调度器在排队空隙插入短任务,把碎片时间利用起来。这个机制对提升集群整体吞吐特别有效。
3.3 推理场景优化:动态批处理与显存复用
推理场景是利用率提升的最大突破口。很多人部署了大模型推理服务,比如用 vLLM 部署 Qwen 或 FunASR 语音识别,然后盯着 GPU-Util 只有 20% 发愁。其实问题往往在动态批处理(continuous batching)没有配好。
vLLM 这类框架原生支持 continuous batching,它的核心思想是:不再等请求凑够一个 batch 才开始推理,而是每生成一个 token 就把新请求插入到当前 batch 的空闲位置。这样 GPU 的利用率会大幅提升。我在实际部署时,会把max_num_batched_tokens调大,并开启抢占式调度,让长序列请求主动让位给短序列请求,减少排队阻塞。
还有个容易被忽视的点:显存复用。推理服务分配 KV cache 时,往往会预留大量显存。如果平台没有做显存复用,每个副本都预留整卡显存,那 GPU 利用率不可能高。正确做法是让推理服务根据实际流量动态扩容副本,但共享同一块显存池。ZStack AIOS 支持显存池化,这一点在部署推理服务时非常有用:不需要每个 Pod 独占整卡显存,而是按需分配 KV cache 大小。
我在部署 FunASR 的时候踩过一个坑:默认参数下它只用一个 batch,GPU-Util 只有 15%。调大 batch size、开启流式处理之后,利用率直接到 60% 以上。所以遇到推理任务利用率低,先别怪平台,先看框架的 batching 配置。
3.4 让模型训练/微调和推理共享集群
最理想的状态,是训练、微调、推理三个类型的任务跑在同一批 GPU 上。但混部不是把任务都丢进去就行,需要做好资源隔离和优先级。
我的典型配置是:用 vGPU 切分出 70% 算力给训练任务,剩余 30% 给推理任务。训练任务可以抢占推理任务,但会提前 5 分钟通知,让推理服务优雅退出。这里的关键是 checkpoint 机制。如果训练任务没有定期保存权重,一旦被抢占,整天的计算就白费了。所以在混部前,必须先保证所有训练任务 30 分钟自动 checkpoint 一次。
还要强调一点:监控混部集群时,不能只看单个指标。如果显存分配率已经 90%,但 SM 占用只有 40%,说明显存不够而算力富余,需要调整任务切分比例。如果反过来,SM 占用 90% 但显存只用 50%,说明算力不够,需要限制并发任务数。
我做扩容评估时,会把这两组指标画成散点图,观察资源瓶颈在哪。这个可视化的过程,比任何调参都管用。也不要迷信 100% 利用率,好的运行状态是“每个任务都能及时拿到资源,整体利用率维持在 70%~85%”。如果你能把集群稳定维持在这个区间,恭喜你,已经跑赢了大多数团队。
4. 监控排障:别等业务挂了才看曲线
4.1 关键监控指标与告警阈值
GPU 集群监控和 CPU 集群有很大区别,最明显的是故障模式不同。CPU 挂了通常是性能下降,GPU 挂了经常是显存 ECC 错误、驱动重置、温度过高,直接影响任务稳定性。
我建议至少盯住这几个指标:
| 指标 | 含义 | 建议告警阈值 |
|---|---|---|
| GPU-Util | 当前流处理器利用率 | P95 持续低于 20% 时关注 |
| SM Occupancy | SM 实际占用程度 | 低于 30% 时检查 batch 配置 |
| 显存使用率 | 已分配显存 / 总量 | 超过 90% 告警 |
| GPU 温度 | 器件温度 | 达到 80℃ 告警 |
| ECC 错误 | 显存纠错事件 | 出现单次即告警 |
| 功耗 | 使用功耗 / TDP | 持续低于 30% 可能是任务没喂饱 |
| DCGM 健康状态 | 驱动、NVLink、PCIe 链路 | 非健康即告警 |
这里有个经验:GPU-Util 高不一定代表健康。如果显存使用率同时很高、SM Occupancy 也高,说明任务在认真跑。如果 GPU-Util 冲到 99%,但显存使用率很低,很可能是任务在读显存时反复等待,实际是显存带宽瓶颈,要检查访问模式。
告警通道上,我用的是 Prometheus Alertmanager 推到企业微信和飞书。告警规则要区分“节点硬件告警”和“业务性能告警”:硬件告警看 ECC、温度、驱动错误;性能告警看任务的排队时长和利用率水位。很多人把这两类混在一起,结果夜里被无用告警轰炸,反而忽略了真正的硬件故障。
4.2 常见问题与排查速查表
根据我接触过的集群,把高频问题整理成了速查表,供各位直接抄作业:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 任务一直 Pending | 节点资源不足 / 标签不匹配 | 看调度器日志,检查卡标签和资源模板 |
| GPU-Util 很低 | batch 太小 / 数据加载慢 | 看框架配置与数据管道 |
| 多任务抢占后卡死 | checkpoint 周期过长 | 缩短 checkpoint 间隔,增加优雅退出机制 |
容器内torch.cuda.is_available()返回 False | 镜像内 CUDA 版本与驱动不匹配 | 检查容器基础镜像,重装 PyTorch GPU 版 |
| 显存 OOM | 显存切分过细 / 任务峰值超申请 | 调整 vGPU 显存大小,记录峰值用量 |
| 性能忽高忽低 | 其他任务的算力抢占 | 开启算力限额,限制最高占用 |
| 温度告警 | 机柜散热不足 / 高负载长时间运行 | 调整温控策略,或错峰调度任务 |
| 驱动重置 | 硬件故障 / VFIO 冲突 | 查看 dmesg,重启后观察是否复现 |
排查这类问题,我有个基本顺序:先看监控曲线,判断是资源瓶颈还是程序问题;再进容器看nvidia-smi和进程状态;之后看调度器日志和事件;最后再看业务日志。千万别一上来就盯业务代码,很多时候是资源模板没写对。
还有个容易翻车的地方:某些国产卡的驱动和监控工具不是nvidia-smi。过去很多团队用统一的脚本采集,结果对不上。ZStack AIOS 这类平台会自动识别不同品牌卡的监控接口,统一成一套指标,省去很多适配工作。如果你的环境是自己写的脚本,记得把接口差异考虑进去。
4.3 我们踩过的三个坑
第一个坑是盲目开 MIG。当时为了提升利用率,给 A100 开了 7 个 MIG 实例,把一张卡切成 7 份跑推理。结果有些算子在小算力切片上性能衰减严重,尤其是 attention 类算子,最终整体吞吐反而下降了。后来我们把算法任务和推理任务分开,算法任务用整卡,推理任务用大切片(比如 1/2 卡),效果立刻好转。
第二个坑是没做显存限额就开共享。某次为了“充分利用显存”,让多个推理任务共享一张 80G 卡,结果其中一个任务显存泄漏,把整张卡占满,其他任务全部 OOM。从那以后我坚持:共享必须配显存硬限额,宁可让任务显存申请排队,也不能让一个任务拖垮整卡。
第三个坑是训练任务 checkpoint 间隔太长。集群做了抢占式调度后,低优先级任务随时可能被高优先级任务挤掉。当时有个微调任务每 4 小时才存一次权重,结果连续两次被抢占,重跑了好几轮。后来统一把 checkpoint 间隔改到 20 分钟,配合分布式文件系统存储,损失控制在几分钟内。混部的前提是有完善的 checkpoint 机制,没有这个,别谈抢占。
5. 选型与落地的几条实在建议
5.1 怎么评估一个智算操作系统
如果你也在评估 ZStack AIOS 或者同类产品,我建议从四个维度做验证:
- 异构纳管能力:是否能把多品牌、多型号的加速卡统一纳管?国产卡的驱动适配做到什么程度?不要只看宣传,要拿实际卡去测试。
- 切分粒度:显存、算力、整卡三种粒度是否都支持?切分后的性能损耗是多少?特别是 MIG 或 vGPU 下的算子兼容性。
- 调度策略:是否支持优先级、抢占、分布式排队?调度器在几百卡规模下的调度耗时是多少?是否有防碎片机制?
- 可观测性:能否统一采集所有卡的监控指标?告警是否可配置?日志和链路追踪是否齐全?
这三个维度缺一不可。我见过有团队只看“能不能调度 GPU”,忽略了切分和监控,结果用起来非常痛苦。从长期看,可观测性甚至比调度本身更重要,因为你要靠数据持续调优。
5.2 优先落地路径:先盘活存量,再谈扩容
很多企业一上来就想买新卡,我觉得这是误区。GPU 利用率低的问题,多数靠“整理存量”就能解决一大半。我建议按这个顺序推进:
- 先做一周基线采集,摸清真实利用率和瓶颈。
- 给所有任务打标签,明确资源需求。
- 把开发环境和测试环境先切到 vGPU/共享模式,释放空闲显存。
- 再把推理服务调优 batching,提高单卡吞吐。
- 最后再考虑训练/推理混部,引入抢占式调度。
每走一步都要量化对比:用了多少卡、跑了多少任务、排队时间缩短多少、利用率提升了多少。等存量盘活了,如果还不够,再考虑扩容,这样扩出来的每张卡都能发挥价值。
5.3 需要避开的隐性成本
这里说几个常被忽略的成本:
- 授权成本:NVIDIA vGPU 需要授权,MIG 在某些卡上也有功能限制,采购前要算清楚。
- 适配成本:国产卡的算子库、框架兼容性参差不齐,迁移一个模型可能要多花几周。选平台时尽量选适配做得全的。
- 运维成本:GPU 故障率比 CPU 高,ECC 错误、驱动重置、散热问题都需要专门的监控和响应流程。
- 人员成本:调度策略、资源模板、监控告警都需要人维护,别指望部署完就什么都不管。
把这些成本算进去,你会发现一个好的智算操作系统省下的不只是硬件钱,还有大量运维人力。这也是为什么我倾向于推荐成熟平台而不是自己从头写脚本的原因。
最后再分享一个我自己反复验证过的经验:GPU 利用率优化不是一个一次性项目,它更像一个持续调优的过程。每次业务模型升级、框架版本更新,都值得重新审视资源模板和监控水位。能稳定把利用率维持在 70% 以上的团队,已经能体现出明显的成本节省。很多时候,问题真不是卡不够,而是卡没被用对地方。希望这篇实战记录能让你少走几步弯路。