实际开发中很少遇到“GPU完全没干活”的情况,更多是“GPU一直在跑,但不知道它到底有没有干正事”。GPU利用率显示 99%,训练速度却没有提升;显存已经占满,计算单元却在空等数据;机器上插了八张卡,只有一张卡在发热,其余七张闲置;推理服务 GPU 占用只有 20%,但请求排队依然严重。真正想回答“怎么让 GPU 不闲着”,需要先承认一个前提:GPU 利用率不是单一指标,而是一整条数据链路共同作用的结果。
这篇文章从一名普通开发者的视角出发,讲清楚 GPU 在训练、推理、容器、多卡和常见故障场景下的状态观测方法,以及如何通过数据加载、训练策略、显存管理、推理批处理和调度优化,让 GPU 进入更高效的工作状态。内容会尽量用手册式的步骤、命令和表格呈现,适合刚接触 GPU 开发的读者,也适合已经能在单卡上跑通训练、但想提升资源利用率的团队参考。
1. 先搞清楚 GPU 到底在哪一层“闲着”
1.1 GPU 利用率不是“0% 和 100%”二选一
很多刚接触 GPU 的开发者会陷入一个误区:只要 GPU-Util 没有到 90% 以上,就觉得 GPU 被浪费了。实际不是这样。现代 GPU 通常由三部分资源共同组成:流式多处理器(Streaming Multiprocessor,SM)、显存控制器、以及用于数据拷贝的 DMA 引擎。三者都可能成为瓶颈。
常见的情况包括:
- SM 很忙,但显存带宽不够,L2 缓存命中率下降,核心在等数据返回。
- 显存占用很高,但计算请求很少,模型权重和中间激活值只是“占用空间”而已。
- 数据通过 PCIe 从 CPU 拷贝到 GPU 的过程很慢,GPU 的 kernel 执行时间很短,大部分时间都在等拷贝完成。
- 多卡之间做梯度同步,单卡算得很快,但通信阶段所有卡都在等待最慢的那张卡。
所以第一步要做的是区分瓶颈。GPU 利用率只是给了一个宏观信号,要精确定位“闲在哪一层”,还需要功耗、温度、显存带宽、拷贝引擎、SM Active 占比等更多指标。
1.2 用 nvidia-smi 先看宏观状态
NVIDIA 驱动自带的nvidia-smi是最直接的观测工具。建议以固定间隔采样,而不是只运行一次。
nvidia-smi --query-gpu=index,utilization.gpu,memory.used,power.draw,temperature.gpu --format=csv -l 1-l 1表示每秒采样一次。输出大致是:
index, utilization.gpu [%], memory.used [MiB], power.draw [W], temperature.gpu [deg C] 0, 97 %, 16384 MiB, 310.25 W, 71 0, 12 %, 16512 MiB, 78.10 W, 63第一次采样看到 97%,第二次变成 12%,这种情况很典型:GPU 正在执行一些密集计算,但随后进入空闲等待。显存没有释放,说明空间仍在被模型占用,只是 SM 没有任务可跑。
常见指标的含义需要重新梳理:
| 指标 | 含义 | 常见误解 |
|---|---|---|
| utilization.gpu | 采样周期内 SM 繁忙的百分比 | 不等于计算效率,等待数据也会算“忙” |
| memory.used | 显存占用 | 只代表分配了多少,不代表计算量 |
| power.draw | 当前功率 | 远低于 TDP 通常说明负载不高 |
| temperature.gpu | 核心温度 | 温度过高会触发降频,抑制利用率 |
| memory.clock | 显存时钟频率 | 低于满频说明显存带宽未打满 |
| SM clock | 核心时钟频率 | 降频说明遇到功耗墙或温度墙 |
1.3 三种“看起来忙但实际低效”的状态
第一种是等待数据。当磁盘读取慢、CPU 预处理慢,GPU kernel 执行时间极短,nvidia-smi会看到利用率在 0% 和 100% 之间快速跳动,显卡功率不稳定。这种状态在训练场景最常见。
第二种是小 kernel 密集启动。程序每次只提交很小的计算任务,比如逐元素操作一个很小的 tensor,GPU 启动 kernel 的开销会占大头。看起来 GPU 一直在执行,实际大量时间消耗在任务调度和上下文切换上。
第三种是同步等待。多卡训练中,每张卡完成反向传播后需要做 AllReduce 梯度同步。如果网络连接是 PCIe 而不是 NVLink,或者跨机器通信带宽不足,计算时间被通信时间拉长,GPU 会出现明显的“忙一会儿、停一会儿”现象。
2. 先建立可观测性:数据采集比优化更重要
2.1 临时观察工具:nvidia-smi 循环采样和 nvtop
如果只需要临时看几秒钟,可以这样采样:
nvidia-smi dmon -s pucct -d 1-d 1表示每秒输出一行。-s pucct表示监控功率、利用率、时钟、温度和内存状态。这个命令的好处是输出紧凑,适合放在另一个终端里实时观察训练过程。
另一个常用工具是nvtop,它类似 Linux 下的top,可以交互式查看 GPU 占用、显存、温度、风扇转速和每个进程的资源占用。在 Ubuntu/Debian 环境中安装:
sudo apt install nvtopnvtop对多卡机器非常有用,可以一眼看出每张卡的负载是否一致。不过它适合短时排查,不适合长期留存数据的场景。
2.2 长期监控:DCGM 是更完整的底座
真正要做性能优化,不能只靠人工盯终端。NVIDIA 官方提供的 DCGM(Data Center GPU Manager)更适合长期采集。
nvidia-smi dmon -e 1002,1003 -d 5这里1002是 SM 利用率,1003是显存带宽利用率。实际项目一般会把 DCGM 接入 Prometheus 的dcgm-exporter,然后在 Grafana 里生成看板。这样做的好处是:
- 可以记录完整训练周期的利用率变化。
- 可以在训练结束后对比优化前后数据。
- 可以按时间维度分析卡间负载是否均衡。
如果暂时不想搭建整套监控,可以直接用 DCGM 自带命令保存文本:
dcgmi dmon -e 1002,1003,1004,1005 -d 5 > gpu_metrics.log这样至少留了一份可回溯的数据。
2.3 建立利用率画像后再开始调整
优化 GPU 之前,建议先跑一份“基线画像”。固定一个任务、固定输入数据规模,记录十分钟内的四类数据:
| 角色 | 采集指标 | 目的 |
|---|---|---|
| 进程侧 | 每 step 耗时、显存峰值 | 判断全局损耗 |
| GPU 侧 | SM 利用率、显存带宽利用率 | 判断计算是否饱和 |
| 传输侧 | PCIe 读写速率、主机内存占用 | 判断数据链路瓶颈 |
| 系统侧 | CPU 占用、磁盘 IO、内存交换 | 判断 CPU 准备是否拖后腿 |
得到基线之后,每次只改一个变量,再跑同一份任务对比。比如从num_workers=2调到num_workers=8,只看这一项差异。没有基线就改多个参数,最终只能确认“整体变快了”,却说不清是哪一个优化起了作用。
注意:不要只验证任务能跑完,还要记录每一步的时间分布。GPU 利用率只在训练结束后看平均数是远远不够的。
3. 训练场景:让数据搬运和计算真正重叠
3.1 DataLoader 配置是 GPU 空等的首要来源
PyTorch 训练中最容易被忽略的性能瓶颈,是 DataLoader 的 CPU 预处理速度赶不上 GPU 计算速度。
from torch.utils.data import DataLoader train_loader = DataLoader( dataset, batch_size=32, num_workers=8, prefetch_factor=4, pin_memory=True, persistent_workers=True, )这里每个参数都值得解释:
num_workers:负责在子进程中预处理数据的进程数。大于 0 时,主进程只需要从队列中拿已经准备好的 batch。prefetch_factor:每个 worker 提前加载多少个 batch。设置过小会导致 GPU 经常空等。pin_memory=True:把数据固定在主机端页锁内存,可以加速 CPU 到 GPU 的拷贝。persistent_workers=True:训练迭代完一轮后,不销毁 worker,避免反复创建进程的开销。
需要注意,num_workers并不是越大越好。如果数据集的预处理逻辑本身不重,而磁盘 IO 已经接近上限,开更多进程只会让磁盘更忙。建议从2、4、8逐个尝试,观察 GPU 利用率变化。
3.2 batch size、梯度累积和混合精度
batch size 直接决定 GPU 计算密度。同样一个 epoch,如果每个 batch 只有 4 张图,kernel 启动次数多,GPU 的调度开销占比高;如果 batch 能增加到 32 或 64,单位时间内的算力利用率会明显提升。
但显存是有限的。显存不足时,梯度累积是一种补救手段:
accumulation_steps = 4 scaler = torch.cuda.amp.GradScaler() for step, (inputs, labels) in enumerate(train_loader): with torch.autocast(device_type="cuda", dtype=torch.float16): outputs = model(inputs) loss = criterion(outputs, labels) loss = loss / accumulation_steps scaler.scale(loss).backward() if (step + 1) % accumulation_steps == 0: scaler.step(optimizer) scaler.update() optimizer.zero_grad()梯度累积的语义是“多个小 batch 的梯度叠加后再更新权重”,逻辑上接近一个大 batch,但要注意三点:
- 如果模型里有 BatchNorm,小 batch 下的统计量会不准确,需要额外处理。
- 如果原本的大 batch 使用了学习率调度,改成梯度累积后学习率是否需要调整,需要重新实验。
torch.cuda.amp.GradScaler()只对 float16 有效,如果使用 bfloat16,可以省略GradScaler,但必须保证硬件支持。
混合精度能让 GPU 明显“更忙”,因为 float16 的显存占用和计算开销都比 float32 低,同一块 GPU 能装入更大的 batch。遇到不支持混合精度的算子在代码里也不用担心,PyTorch 会自动回退到 float32。
3.3 多卡训练:避免“一张卡干活,其余卡围观”
多卡训练最频繁的问题,是忘记通过环境变量控制设备编号,导致所有进程都跑在同一张卡上,其余卡完全没有负载。
CUDA_VISIBLE_DEVICES=0,1 torchrun --nproc_per_node=2 train.py在代码内部,通常这样设置:
import torch import torch.distributed as dist dist.init_process_group(backend="nccl") local_rank = dist.get_rank() torch.cuda.set_device(local_rank) device = torch.device("cuda", local_rank)torchrun会自动为每个进程分配对应编号。如果机器是四卡,但只想用物理编号 2 和 3,那么环境变量应该写成:
CUDA_VISIBLE_DEVICES=2,3 torchrun --nproc_per_node=2 train.py这样进程内看到的编号就是 0 和 1,分别映射到物理卡 2 和 3。
多卡通信对 GPU 利用率的影响很大。单卡算得再快,如果梯度同步阶段所有卡都在等最慢的卡,整体效率就会被拉低。使用nvidia-smi topo -m可以查看卡间拓扑:
nvidia-smi topo -m如果输出显示两张卡之间有NV#表示走 NVLink,PIX或PHB表示走 PCIe。NVLink 带宽更高,AllReduce 等待时间更短。多机训练还需要额外关注网卡和跨机通信带宽。
3.4 用 Nsight Systems 精确定位空闲区间
当 GPU 利用率低但不知道原因时,建议用 NVIDIA 提供的 Nsight Systems 做一次性能采样。
nsys profile --trace=cuda,nvtx -o trace_output python train.py采样完成后,用 Nsight Systems 打开trace_output.nsys-rep,重点看时间轴上的 GPU 活动区间。通常情况下:
- GPU 在很长一段时间没有任务,说明 CPU 侧数据准备太慢。
- GPU 任务密集但出现规律性空隙,说明存在同步等待。
- GPU kernel 很多但每个都很短,说明需要减少小 tensor 操作,或者合并 kernel。
Nsight Systems 的一次全量采样往往能告诉你“GPU 空在哪”,是性能分析里最值得投资的一步。
4. 推理场景:不要追求 100% 利用率
4.1 先确定目标:在线低延迟还是离线高吞吐
推理场景和训练不同。训练阶段可以为了吞吐量不断增大 batch size,让 GPU 始终保持高负载。但在线推理服务如果强行增加 batch,单个请求需要等同一个 batch 里的其他请求处理完,P99 延迟会上升。
| 场景 | 核心指标 | 典型策略 |
|---|---|---|
| 在线 API 服务 | P99 延迟、单请求耗时 | 控制 batch 大小,不做长排队 |
| 离线批处理任务 | 每秒处理样本数 | 尽量用大 batch 跑满 GPU |
| 大语言模型推理 | 首 token 延迟、吞吐量 | 动态批处理、连续批处理 |
| 图像生成 | 生成耗时、显存占用 | 峰值显存控制、模型优化 |
所以在做推理优化时,不能只看 GPU 利用率。如果延迟达标、吞吐稳定,GPU 利用率只有 40% 也可能是完全合理的。
4.2 动态批处理让稀疏请求填满 GPU
单次推理只发一个请求,GPU 很难跑出高利用率。更合理的做法是引入动态批处理,让服务在等待窗口内聚合多个请求,再一次性送到 GPU。
以 Triton Inference Server 为例,配置 dynamic batching 时通常会设置:
{ "max_batch_size": 8, "dynamic_batching": { "preferred_batch_size": [4, 8], "max_queue_delay_microseconds": 200 } }这样服务会等待 200 微秒,尽量把请求凑满 4 或 8 个再执行。延迟会增加一点点,但 GPU 利用率会明显提升。
对于大语言模型推理,vLLM 等框架采用continuous batching策略,不再等整个 batch 做完,而是不断把结束生成的请求移除、插入新请求。这种方法能在严格延迟约束下显著提高 GPU 吞吐。
4.3 用 TensorRT 和 ONNX Runtime 做计算图优化
深度学习框架默认的推理执行方式,往往包含很多额外开销。TensorRT 会对计算图做层融合、精度校准和 kernel 自动选择,减少 GPU 上的空转片段。常见流程是先把模型导出为 ONNX,再用 TensorRT 转换。
trtexec --onnx=model.onnx --saveEngine=model.engine --fp16 --workspace=4096这里的--fp16开启半精度推理,--workspace指定构建引擎时可用的显存上限。trtexec还可以用来做基准测试:
trtexec --loadEngine=model.engine --shapes=input:1x3x224x224 --fp16但要注意,TensorRT 优化后的引擎是和具体 GPU 架构、CUDA 版本、batch size 绑定的,换卡后通常需要重新生成。模型结构变化频繁的时候,维护多份引擎文件的成本较高。
4.4 图像生成场景的显存优化思路
像 ComfyUI、Stable Diffusion 这类图像生成应用,经常出现“显存不足”,但 GPU 利用率并不高。原因往往是峰值显存分配问题:模型权重、临时 tensor、中间激活同时叠加在显存中,某个瞬间超出容量。
处理这类问题不能靠单纯调大 batch,常用方向包括:
- 降低分辨率或 batch size。
- 使用 fp16 或 fp8 精度。
- 开启模型 offload,把部分层临时放到 CPU 内存。
- 使用显存优化插件,把不需要的中间结果及时释放。
- 检查是否打开了“内存不足自动优化”这类重分配功能。
在显存不足的机器上,第一目标应该是让程序稳定跑通,其次才是把 GPU 利用率拉高。如果每一步都在 OOM 边缘反复试错,效率远低于主动降低 batch。
5. 多卡与容器环境:让每张卡都分到明确的活
5.1 通过 CUDA_VISIBLE_DEVICES 管理设备编排
多卡机器上,如果某个任务没有显式指定 GPU,它会默认使用编号 0。多个任务同时运行时,容易把所有负载都挤到第一张卡。
通过环境变量可以控制程序看到哪些物理 GPU:
export CUDA_VISIBLE_DEVICES=1,3 python train.py这个变量会让程序以为机器上只有两张卡,编号分别是 0 和 1,实际对应物理卡 1 和 3。在多进程任务里,需要注意“进程内编号”与“物理编号”的映射,避免明明设置了多卡,却始终使用同一张物理卡。
在容器环境中同样适用:
docker run --gpus '"device=1,3"' --rm nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi5.2 Docker GPU 直通与 CDI 报错排查
Docker 里要用 GPU,需要安装 NVIDIA Container Toolkit。如果没有正确配置,会看到类似错误:
docker: Error response from daemon: failed to discover gpu vendor from cdi: no known这个错误的常见原因是 Docker 没有使用 NVIDIA Container Runtime。可以这样修复:
sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker然后验证当前 Docker 运行时:
docker info | grep -i runtime正常输出应该包含nvidia。继续用 CDI 列表确认:
nvidia-ctk cdi list如果输出里能看到类似nvidia.com/gpu=0的条目,说明 CDI 配置正常。之后再运行容器就不会再报告no known。
Kubernetes 集群里则需要部署 NVIDIA GPU Operator,由它统一处理驱动、device plugin、DCGM 和运行时配置。手动环境下的排查步骤在集群环境里不一定适用,需要以实际集群的 Operator 状态为准。
5.3 WSL2 和虚拟机场景中 GPU 被识别但没生效
WSL2 里经常遇到一种情况:nvidia-smi能看到 GPU,但 OpenGL 渲染仍然走 CPU 软件模拟。原因通常是宿主机没有安装对应的 GPU 驱动,或者 WSL 版本太旧,导致 D3D12 与 OpenGL 的转换层没有生效。
检查方式:
nvidia-smi glxinfo | grep -i "opengl"如果glxinfo显示的是llvmpipe,说明 OpenGL 正在使用 CPU 渲染。这时需要检查 Windows 宿主机的显卡驱动版本,以及 WSL 是否更新到支持 GPU 加速的版本。WSL2 的 GPU 能力依赖宿主机驱动和 WSL 内核共同工作,不能只在 Linux 侧单独解决。
Ollama 在 WSL 中无法识别 GPU 的情况也类似,需要确认:
- 宿主机驱动是否支持当前 CUDA 版本。
- 运行 Ollama 的用户是否具有访问
/dev/dxg和/dev/nvidia0的权限。 - 有无通过
CUDA_VISIBLE_DEVICES强制屏蔽 GPU。
6. 几种常见的“低效忙碌”问题与处理
6.1 显存 OOM 不等于 GPU 在满负荷运行
显存不足和计算饱和是两个维度。模型可能因为参数巨大占满显存,但实际推理时 SM 几乎不工作。OOM 产生的原因,很多时候是某个中间 tensor 在峰值时刻超出了显存容量。
排查时可以在训练代码里加入显存峰值统计:
torch.cuda.reset_peak_memory_stats() outputs = model(inputs) peak = torch.cuda.max_memory_allocated() print(f"Peak memory: {peak / 1024**3:.2f} GB")通过逐步缩小输入,观察峰值显存随 batch size 的变化,可以判断显存主要消耗在哪些环节。如果模型本身很大,可以换用模型并行或 offload;如果是激活值过大,可以减小 batch size 或开启梯度检查点。
6.2 降频、功耗墙和散热导致利用率偏低
GPU 显示满载,但实际 SM 时钟频率低于理论峰值,说明可能撞到了功耗墙或温度墙。需要检查一下时钟状态:
nvidia-smi -q -d CLOCK在数据中心环境,可以主动限制功耗,让 GPU 长期稳定运行而非间歇降频:
nvidia-smi -pl 250-pl参数只在部分专业卡上有效,普通消费卡可能不支持。它用于设定功耗上限。实际项目中,与其让 GPU 在超标功耗和降频之间反复横跳,不如设置一个合理功耗上限,换来更稳定的吞吐。
nvidia-settings在部分卡上无法调节风扇转速,这是常见现象。笔记本和消费级显卡的风扇控制通常被硬件固件接管,不必在软件层面强行修改。
6.3 PCIe 链路错误计数解读
nvidia-smi的 PCIE 信息里包含一些错误计数器,例如:
GPU 00000000:01:00.0 PCIe Link Current : 8 Max : 16 ... Error Counters Receiver Errors : 5 Bad Dllp Count : 2 Bad Tlp Count : 0Receiver Errors、Bad Dllp Count、Bad Tlp Count都位于传输层,如果持续增加,通常说明 PCIe 链路不稳定,而不是 GPU 计算资源不足。可能原因包括:
- 显卡电源供电不足。
- PCIe 插槽或转接线接触不良。
- PCIe 链路被强制工作在超出稳定范围的速率。
- 主板 BIOS 设置或 PCIe spread spectrum 配置问题。
排查时先查看nvidia-smi -q -d PCIE中的Current和Max是否一致,再检查系统日志:
dmesg | grep -i pcie如果错误计数持续增长,优先检查供电、插槽和线缆,必要时在 BIOS 中把 PCIe 模式从 Gen4 降到 Gen3 做稳定性验证。不要第一反应就换显卡。
7. 遇到 GPU 资源效率问题时的排查顺序
7.1 七步排查链路
遇到 GPU 利用率低下的问题,按以下顺序排查,可以避免在一堆信息里失去方向。
- 先确认进程是否真的在使用 GPU。使用
nvidia-smi查看进程列表,确认程序 PID 出现在哪张卡上。 - 采集一段时间的利用率曲线。单次采样没有说服力,建议至少观察 60 秒。
- 看 CPU 占用。如果程序某几个 CPU 核已经打满,而 GPU 利用率低,优先怀疑数据预处理和加载。
- 检查磁盘 IO 和网络 IO。训练数据在远程存储上时,网络等待会直接拖慢 GPU。
- 查看功耗和温度。功率远低于 TDP,说明 GPU 没有持续执行重计算任务。
- 确认多卡通信拓扑。如果多卡训练出现周期性等待,检查卡间链路是 NVLink 还是 PCIe。
- 最后再深入代码层。用 Nsight Systems 或 PyTorch Profiler 抓取具体 kernel 时间线。
7.2 常见问题速查表
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| GPU 利用率在 0% 和 100% 间跳 | CPU 预处理跟不上 | 观察 CPU 占用、DataLoader 参数 | 增加 num_workers、prefetch_factor |
| 显存占满但利用率低 | 模型权重占用,计算不足 | 查看进程显存分配 | 减小 batch、检查模型参数量 |
| 多卡只有一张在跑 | CUDA_VISIBLE_DEVICES 设置错误 | 检查进程可见设备 | 显式指定多卡编号 |
| Docker 无法识别 GPU | NVIDIA Runtime 未配置 | docker info、nvidia-ctk cdi list | 执行 runtime configure 并重启 Docker |
| WSL2 里 OpenGL 用 CPU | 宿主机驱动或 WSL 版本问题 | glxinfo | 更新驱动和 WSL |
| ComfyUI 报显存不足 | 峰值显存超限 | 查看显存曲线 | 调低 batch、打开重分配 |
| 温度过高导致频繁降频 | 散热或功耗墙 | nvidia-smi -q -d CLOCK | 清理散热、设置功耗上限 |
| PCIe 错误计数增加 | 链路不稳定 | dmesg、PCIE 信息 | 检查供电、插槽、线缆 |
注意:排查时要记录时间点。利用率曲线的“上下文”比单个数字重要得多。训练启动、数据加载、模型编译、梯度同步阶段,GPU 的利用模式完全不同。
8. 把“让 GPU 不闲着”做成日常流程
8.1 训练和推理发布前的检查清单
每次启动大规模训练或上线推理服务之前,可以用下面这份清单快速自检:
- 驱动、CUDA、PyTorch 版本是否兼容。
- 当前任务使用的 GPU 编号是否明确。
- 显存需求是否超过单卡容量,是否需要梯度累积或 offload。
- DataLoader 参数是否与 CPU 核数、磁盘性能匹配。
- 多卡任务是否确认卡间拓扑和通信方式。
- 容器环境是否配置了 NVIDIA Container Runtime。
- 是否有其他进程占用 GPU,导致资源竞争。
- 是否记录了本次运行的基础指标,方便事后对比。
8.2 低成本的优化顺序
先不要急着加卡或者换更强的 GPU。多数项目的 GPU 浪费都发生在数据链路和任务调度层,按以下顺序做成本更低:
- 用工具抓住数据,确认 GPU 是否真的在空闲。
- 调整 DataLoader 和 batch size,解决大部分训练场景空等。
- 开启混合精度,扩大 batch,提高计算密度。
- 在推理场景引入动态批处理或连续批处理。
- 用 TensorRT 或 ONNX Runtime 做计算图优化。
- 如果依旧达不到目标,再考虑量化、蒸馏、裁剪或更换硬件。
8.3 利用率不是最终目标
“让 GPU 不闲着”本质上是让 GPU 在单位时间内处理更多有效任务,而不是让指标停留在 99%。有些场景下,低利用率反而是正确选择:在线 API 需要预留余量应对突发流量,小请求任务不适合强行凑成大 batch,生成类任务需要控制峰值显存。优化 GPU 的目标,应该是让任务在满足延迟、吞吐和稳定性要求的前提下,减少不必要的资源浪费。只要能稳定交付业务结果,GPU 利用率只是辅助判断的指标,不是唯一的考核标准。