多 GPU 没分配上,模型只跑在卡 0

本地 LLM 服务在多 GPU 机器上启动后,所有计算只在第一张卡上进行,其他卡显存占用为零。从 tensor parallel 配置、NVLink、环境变量三个层面给出强制分布的修复方案。

在配备 4 × RTX 4090 / 96 GB 总显存的工作站上运行 vLLM 0.4 服务 Llama-3-70B,nvidia-smi 显示 GPU 0 显存占用 24 GB(满载),其他三张卡显存占用 0 GB,生成速度只有单卡 70B 的水平。同样的问题在 llama.cpp 中也会出现:--n-gpu-layers 99 把所有层放到 GPU,但只有卡 0 被使用。这是因为多 GPU 分布需要显式配置张量并行(tensor parallel)或层分割(layer split),不会自动发生。

常见原因

1. vLLM 未设置 —tensor-parallel-size

vLLM 默认 --tensor-parallel-size 1,即只使用单 GPU。若不显式设置为 GPU 数量,服务只会使用第一张可见的 GPU,其余 GPU 完全空闲。

怎么判断:查看 vLLM 启动命令中是否有 --tensor-parallel-sizenvidia-smi 显示只有 GPU 0 有显存占用。

2. llama.cpp 未设置 —tensor-split 或使用了错误比例

llama.cpp 的多 GPU 分布通过 --tensor-split 参数控制,格式为各 GPU 的权重比例(如 1,1,1,1 表示均分到 4 卡)。不设置此参数时,所有层默认堆到 GPU 0(若 GPU 0 装不下,才会溢出到 CPU,而不是分布到其他 GPU)。

怎么判断:检查 llama.cpp 启动命令是否包含 --tensor-splitnvidia-smi 查看各卡显存分配。

3. CUDA_VISIBLE_DEVICES 只暴露了单个 GPU

环境中设置了 CUDA_VISIBLE_DEVICES=0,使应用只看到一块 GPU,即使物理上有 4 块。这在 conda 环境、Docker 容器或 CI 脚本中很常见。

怎么判断echo $CUDA_VISIBLE_DEVICES;若输出 0 或任意单个数字,说明只有一块 GPU 可见。

4. Ollama 对多 GPU 的支持依赖 NCCL,但 NCCL 未正确安装

Ollama 0.4 的多 GPU 推理需要 NCCL(NVIDIA Collective Communications Library)支持。若系统没有安装 NCCL 或版本不兼容,Ollama 会静默回退到单 GPU 模式。

怎么判断ollama serve 日志中搜索 ncclldconfig -p | grep nccl 检查 NCCL 是否已安装;若 Ollama 版本低于 0.3,可能不支持多 GPU。

5. GPU 间没有 NVLink,PCIe 带宽成为瓶颈导致分布性能反而变差

没有 NVLink 的多 GPU 配置(消费级 RTX 系列通常没有 NVLink)下,张量并行需要通过 PCIe 总线在 GPU 间传输激活值,对于 70B 模型每层前向传播需要约 30-50 MB 的通信量,PCIe x16 的带宽(16 GB/s)会成为瓶颈,导致实际吞吐量低于单 GPU。

怎么判断nvidia-smi topo -m 查看 GPU 间拓扑,NV4/NV6/NV18 表示有 NVLink,PIX/PXB/PHB 表示只有 PCIe。若是 PCIe 连接,考虑使用 pipeline parallel 而非 tensor parallel。

6. 模型架构不支持张量并行

某些特殊架构(如 MoE 的 expert 层)对张量并行的支持有限。vLLM 和 llama.cpp 会在这类层上回退到单 GPU 处理,导致实际并行效率远低于预期。

怎么判断:检查 vLLM 日志中是否有 fallback to single GPU for layer 相关警告;对比多 GPU 和单 GPU 的实际吞吐量,若多 GPU 只快了 20-30%(而不是预期的接近线性加速),说明存在回退。

最短修复路径

Step 1:vLLM 明确设置 tensor parallel size

# 启动 4 GPU tensor parallel 服务
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Meta-Llama-3-70B-Instruct \
  --tensor-parallel-size 4 \
  --gpu-memory-utilization 0.90 \
  --max-model-len 32768 \
  --host 0.0.0.0 \
  --port 8000

# 验证各卡显存占用
watch -n2 'nvidia-smi --query-gpu=index,memory.used --format=csv,noheader'
# 期望:4 张卡各占约 20 GB(70B Q8 需要约 80 GB 总显存)

Step 2:llama.cpp 配置 tensor-split

# 均分到 4 张 RTX 4090(各 24 GB)
./llama-server \
  -m llama-3-70b-Q4_K_M.gguf \
  --tensor-split 1,1,1,1 \
  -ngl 999 \
  --ctx-size 8192 \
  --host 0.0.0.0 \
  --port 8080

# 若 4 张卡显存不等(如 24GB + 24GB + 16GB + 16GB),按比例分配
./llama-server -m model.gguf --tensor-split 3,3,2,2 -ngl 999

# 验证多卡均在使用
nvidia-smi --query-gpu=index,utilization.gpu,memory.used --format=csv -l 2

Step 3:确保所有 GPU 对应用可见

# 设置所有 GPU 可见
export CUDA_VISIBLE_DEVICES=0,1,2,3

# 或使用 all 关键字
export CUDA_VISIBLE_DEVICES=all

# Docker 容器内
docker run --gpus all ...
# 或指定特定 GPU
docker run --gpus '"device=0,1,2,3"' ...

Step 4:Ollama 多 GPU 配置

# Ollama 0.4+ 会自动检测并使用所有可见 GPU
# 确保 CUDA_VISIBLE_DEVICES 未被限制
unset CUDA_VISIBLE_DEVICES

# 查看 Ollama 探测到的 GPU
OLLAMA_DEBUG=1 ollama serve 2>&1 | grep -i 'gpu\|cuda\|nccl'

# 期望看到类似:
# msg="inference compute" id=GPU-0 ... name="NVIDIA GeForce RTX 4090"
# msg="inference compute" id=GPU-1 ... name="NVIDIA GeForce RTX 4090"
# msg="inference compute" id=GPU-2 ... name="NVIDIA GeForce RTX 4090"
# msg="inference compute" id=GPU-3 ... name="NVIDIA GeForce RTX 4090"
# 对于无 NVLink 的 PCIe 连接多 GPU,pipeline parallel 更合适
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Meta-Llama-3-70B-Instruct \
  --pipeline-parallel-size 4 \
  --gpu-memory-utilization 0.90 \
  --host 0.0.0.0 \
  --port 8000

# pipeline parallel 将不同层分配到不同 GPU
# 延迟比 tensor parallel 略高,但吞吐量在无 NVLink 时通常更好

预防建议

  • 部署脚本中始终显式设置 --tensor-parallel-size--tensor-split,不依赖自动推断。
  • 启动后第一件事是运行 nvidia-smi 验证各卡显存分配是否符合预期。
  • 生产环境配置 Prometheus + nvidia-smi exporter 监控各 GPU 利用率,快速发现单卡过载。
  • nvidia-smi topo -m 是判断多 GPU 配置最优策略的基础,NVLink 用 tensor parallel,PCIe 用 pipeline parallel。
  • 对于 4 × 24 GB 卡(96 GB 总显存),70B Q4_K_M(40 GB)完全可以装入单个大 GPU(如 A100 80GB)并获得更好的延迟;如果有 A100 可选,不一定要分布到多卡。
  • 在 Docker 容器中运行时,确保 --gpus all 而不是 --gpus '"device=0"',很多问题来自容器只暴露了单卡。

常见问答 (FAQ)

Q: 4 × RTX 4090 的多 GPU tensor parallel 比单 A100 80GB 快吗? A: 取决于任务类型。吞吐量(tokens/s,高并发场景)4 × RTX 4090 通常更高;但单请求延迟(首 token 延迟)因为 PCIe 通信开销,不一定比单 A100 快。对于 TTFT 敏感的交互式场景,A100 可能更合适。

Q: Ollama 支持 pipeline parallel 吗? A: Ollama 0.4 主要实现了 tensor parallel(层内并行),pipeline parallel 支持有限。对于需要精确控制并行策略的生产场景,推荐使用 vLLM,灵活性更高。

Q: 两张不同型号的 GPU(如 RTX 4090 + RTX 3090)能做 tensor parallel 吗? A: 可以,但需要调整 --tensor-split 比例匹配实际显存大小(24:24 vs 24:10 等)。混合型号会受到最慢 GPU 的性能限制(木桶效应),实际效果通常不理想。建议使用相同型号的 GPU。

Q: 多 GPU 运行时某一张卡出故障,会影响其他卡吗? A: 是的,tensor parallel 和 pipeline parallel 都是紧耦合的,任何一张卡故障会导致整个推理进程崩溃。需要重启服务并在排除故障卡后重新配置(如将 --tensor-parallel-size 从 4 改为 3)。

相关阅读

标签: #local-llm #vllm #排查