为什么单卡总是“爆显存”?
跑大模型最让人头秃的瞬间,莫过于看着终端里CUDA out of memory(或者在 AMD 平台上是HIP out of memory)的报错发呆。尤其是面对 Llama 3.1 405B 这种巨无霸,单张显卡哪怕有 80GB 甚至 192GB 显存,光是加载权重就捉襟见肘,更别提留出空间给 KV Cache 做推理了。之前我也试过强行量化到 INT4,虽然能塞进去,但模型“智商”下降得厉害,生成的内容逻辑混乱,完全没法用在生产环境。
其实,解决这个问题的核心思路很简单:既然单卡装不下,那就多卡一起扛。vLLM 对多卡并行的支持已经非常成熟,特别是在 ROCm 7.x 生态下,配合 AMD Instinct MI300X 这类大显存 GPU,只要配置得当,不仅能跑起来,吞吐量还能线性增长。今天就不聊那些虚的理论,直接复盘一下我在 DevCloud 上折腾 vLLM 张量并行(Tensor Parallelism, TP)的实战过程,重点讲讲怎么配才能让卡间通信不成为瓶颈,以及如何通过细节调优把显存榨干。
张量并行:让多卡变成“一张卡”
vLLM 实现多卡并行的核心参数是--tensor-parallel-size(简称 TP)。它的原理是把模型的权重切分,分散存储在不同的 GPU 上,计算时各卡协同工作。对于 405B 这种模型,我们通常需要 8 卡甚至更多。
启动命令并不复杂,关键在于环境变量的预设。在 ROCm 环境下,务必先指定架构代码,否则编译出的内核可能无法运行:
exportPYTORCH_ROCM_ARCH=gfx942exportHSA_OVERRIDE_GFX_VERSION=9.4.2接着是启动服务。假设我们有 8 张 MI300X,想要全用上:
python-mvllm.entrypoints.api_server\--modelmeta-llama/Meta-Llama-3.1-405B-Instruct\--tensor-parallel-size8\--gpu-memory-utilization0.92\--block-size16\--port8000这里有个极易被忽视的坑:通信总线。TP 模式下,卡与卡之间需要频繁交换中间激活值,如果通信走的是低速的 PCIe 甚至以太网,延迟会高到让你怀疑人生。在 AMD 的多卡服务器(如 HGX 模组)中,必须确保卡间通信走Infinity Fabric高速互联。这通常依赖于正确的 RCCL(ROCm 版 NCCL)配置。如果是在容器化环境(如 DevCloud),要检查是否透传了所有 GPU 设备,并且没有错误的网络隔离策略阻碍了 P2P 通信。可以通过rccl-test工具简单测一下带宽,如果达不到数百 GB/s 的水平,说明通信链路有问题,这时候跑大模型基本就是“爬着走”。
显存碎片与进程绑核:细节决定生死
配置好 TP 只是第一步,真正让服务稳定运行的是对显存和 CPU 资源的精细控制。
对抗显存碎片
vLLM 的 PagedAttention 技术虽然高效,但在多卡场景下,显存碎片化问题会被放大。--block-size参数决定了显存块的大小。默认值通常是 16,这对于大多数场景是平衡点。但如果你的业务场景中长序列请求特别多,可以尝试调大到 32 或 64,减少管理开销;反之,短序列居多则保持较小值以提高利用率。
更重要的是--gpu-memory-utilization。很多教程建议设为 0.95 甚至更高,但在生产环境我强烈建议压在0.90 - 0.92。多卡系统中,只要有一张卡因为瞬时峰值 OOM,整个推理进程就会崩溃重启。留出 8%-10% 的缓冲空间,是为了应对并发突增时的 KV Cache 动态分配,这点“浪费”换来的是服务的稳定性,绝对值得。
进程绑核(CPU Affinity)
这是一个容易被遗忘的性能杀手。vLLM 启动多个 TP 进程时,如果操作系统随意调度,很可能出现多个 GPU 进程争抢同一个 CPU 核心的情况,导致上下文切换开销剧增,推理延迟抖动。
我们需要使用numactl将每个进程绑定到对应的 NUMA 节点。虽然 vLLM 启动脚本内部做了一些优化,但在极端高并发下,手动干预更稳妥。你可以编写一个简单的封装脚本,利用taskset或numactl根据 GPU ID 绑定 CPU 核心掩码。例如,将 GPU 0-3 的进程绑定到 CPU 0-31,GPU 4-7 绑定到 CPU 32-63。这样能确保数据在本地内存访问,减少跨 NUMA 节点的内存拷贝,显著降低首字延迟(TTFT)。
并发测试与扩容建议
配置完成后,别急着上线,先用benchmark_serving.py压测一下。我发现一个有趣的现象:在 TP=8 的配置下,随着并发数(Concurrency)的提升,吞吐量(Token/s)并非一直线性增长。
在并发数较低时(如 1-16),系统表现完美,延迟极低。但当并发数超过某个阈值(比如 64 或 128,取决于具体模型和硬件),吞吐量开始 plateau 甚至下降。这通常是因为显存带宽饱和,或者 CPU 调度成为了瓶颈。
实操建议:
- 寻找拐点:通过压测绘制“并发数 - 吞吐量”曲线,找到性能拐点。生产环境的最大并发限制应设定在拐点左侧,预留 20% 余量。
- 动态批处理:vLLM 的 Continuous Batching 是提升吞吐的关键,确保
--max-num-seqs参数设置合理。如果显存允许,适当调大该值可以容纳更多并发请求。 - 监控指标:上线后重点监控 GPU 显存使用率和 RCCL 通信流量。如果发现某张卡显存长期高于其他卡,可能是负载不均;如果通信流量异常低,再次检查互联总线状态。
总的来说,用 vLLM 跑超大参数模型,本质上是在做资源管理的艺术。TP 参数解决了“能不能跑”的问题,而显存预留、进程绑核和通信优化则决定了“跑得稳不稳、快不快”。在 AMD ROCm 生态日益成熟的今天,这套方案已经具备了替代昂贵专有方案的能力,只要肯在细节上多磨一磨,效果往往出乎意料。
200小时GPU算力已就位,快来领取:https://marketing.csdn.net/questions/Q2604140858304426315?utm_source=AIpaper