vLLM 性能问题排查记录:从 Docker 启动失败到发现 CUDA 兼容层的坑

目录

今天花了几个小时排查一个 vLLM 的性能问题,过程中踩了不少坑,记录一下。


🎯 起因:GitHub Issue #35048

有用户反馈 vLLM 从 v0.14.0 升级到 v0.15.1 后,MiniMax-M2.5 模型的推理性能出现了明显退化:

  • Per-request 吞吐量下降约 19%
  • 延迟中位数增加约 24%
  • TTFT p95 几乎翻倍

作为 vLLM 的开发者,决定在本地复现一下这个问题。


🔧 环境准备

手上只有一张 RTX 3090(24GB),先拉两个版本的 Docker 镜像:

docker pull vllm/vllm-openai:v0.14.0
docker pull vllm/vllm-openai:v0.15.1

MiniMax-M2.5 需要 220GB 显存,跑不起来。换成本地有的 Qwen2-7B-Instruct 来测试。


💥 第一个坑:v0.15.1 启动失败

v0.14.0 启动正常,但 v0.15.1 直接报错退出:

RuntimeError: Unexpected error from cudaGetDeviceCount(). 
Did you run some cuda functions before calling NumCudaDevices() 
that might have already set an error? 
Error 803: system has unsupported display driver / cuda driver combination

奇怪的是,两个镜像用的是 同一个 PyTorch 版本(2.9.1+cu129),为什么一个能跑一个不能?


🔍 排查过程

1. 直接用 ctypes 调用 CUDA Runtime

import ctypes
cudart = ctypes.CDLL('libcudart.so')
cudart.cudaGetLastError.restype = ctypes.c_int
print(f'cudaGetLastError: {cudart.cudaGetLastError()}')

结果:

  • v0.14.0: cudaGetLastError: 0
  • v0.15.1: cudaGetLastError: 803

还没调用任何 CUDA 函数,错误就已经存在了!说明 libcudart.so 加载时的初始化代码就出错了

2. 检查 libcudart.so 文件

两个镜像的 libcudart.so.12.9.79 文件 MD5 完全相同,不是库文件本身的问题。

3. 检查 ldconfig 解析路径

ldconfig -p | grep libcuda

v0.14.0:

libcuda.so.1 => /lib/x86_64-linux-gnu/libcuda.so.1

v0.15.1:

libcuda.so.1 => /usr/local/cuda-12.9/compat/libcuda.so.1  # ← 问题在这!
libcuda.so.1 => /lib/x86_64-linux-gnu/libcuda.so.1

🎯 根因定位

v0.15.1 镜像的 ldconfig 配置变了,导致 libcuda.so 优先从容器内的 CUDA 兼容层加载(版本 575.57.08),而不是宿主机驱动(版本 580.126.09)。

CUDA Runtime(libcudart)初始化时会调用 Driver API(libcuda),575 的 libcuda 和 580 的内核驱动不兼容,所以报错 803。

这解释了为什么:

  • nvidia-smi 能正常显示 GPU 信息(走的是宿主机驱动)
  • 但 PyTorch CUDA 初始化失败(走的是容器内兼容层)

✅ 解决方案

设置 LD_LIBRARY_PATH 让宿主机的 libcuda 优先:

docker run --gpus all \
  -e LD_LIBRARY_PATH=/lib/x86_64-linux-gnu:/usr/local/nvidia/lib64:/usr/local/cuda/lib64 \
  vllm/vllm-openai:v0.15.1 \
  ...

问题解决,v0.15.1 正常启动了!


📊 性能对比结果

在 Qwen2-7B-Instruct(非 MLA 模型)上对比:

版本 Per-request tok/s
v0.14.0 49.69
v0.15.1 49.55

差异 <1%,几乎没有性能退化。

这说明 issue 中报告的性能问题 大概率是 MLA attention 相关的,而不是通用推理路径的问题。


📝 代码变更分析

v0.14.0 → v0.15.1 共 371 个 commits,其中 MLA 相关变更 13 个:

  • [MLA] Fuse cat and quant for fp8 kv-cache (#32950)
  • [FlashMLA] Update FlashMLA to expose new arguments (#32810)
  • [Model Runner V2] Support FLASHINFER_MLA backend (#32709)
  • MLA backend 选择逻辑变更

attention 和 MLA 路径大量重构(44 文件,+2080/-1170 行),性能退化可能出在这些改动中。


🤔 遗憾

限于设备(只有单卡 3090),没法跑 MiniMax-M2.5 或 DeepSeek-V2 这类 MLA 模型来完全复现问题。已经把排查结果回复到 issue 上了,希望有 H100/H200 资源的同学能继续 bisect。


💡 经验总结

  1. Docker 镜像升级后启动失败:不一定是代码问题,可能是 ldconfig 配置变化导致加载了错误版本的系统库

  2. CUDA 兼容层是把双刃剑:它让老版本 CUDA 能跑在新驱动上,但如果配置不当也会导致版本不匹配

  3. 排查 CUDA 问题的技巧

    • ctypes 直接调用 cudaGetLastError 看有没有残留错误
    • ldconfig -p | grep cuda 检查库解析路径
    • 对比 /proc/self/maps 看实际加载了哪个版本的库
  4. 性能对比要控制变量:非 MLA 模型测不出 MLA 相关的性能退化,要复现问题得用对应的模型架构


排查问题虽然花了不少时间,但学到了很多 CUDA 底层的知识,值了。