导语摘要

在大模型本地部署场景中,很多服务器并不是标准的同构 GPU 配置,而是同时存在不同型号的显卡。例如一台机器中既有 RTX 4090D,又有 两张 A100-SXM4-40GB

这种机器资源看起来很强,但真正部署 vLLM 时会遇到一个问题:

三张卡型号不同,能不能放在同一个 vLLM 实例里一起跑?

如果直接使用:

CUDA_VISIBLE_DEVICES=0,1,2
--tensor-parallel-size 3

看似简单,但在异构 GPU 场景下并不一定稳定。因为 Tensor Parallelism 会把同一层计算切到多张 GPU 上执行,对 GPU 性能一致性和通信能力要求更高。

本文整理一种更适合当前机器的方案:

方案 A:单实例三卡部署,使用 Pipeline Parallelism,配置为 TP=1,PP=3

也就是:

CUDA_VISIBLE_DEVICES=0,1,2
--tensor-parallel-size 1
--pipeline-parallel-size 3

这套方案的目标不是盲目追求极限吞吐,而是先让 4090D + A100 + A100 三张异构 GPU 在同一个 vLLM 实例中稳定参与推理。

一、部署背景:为什么要做异构三卡

当前服务器 GPU 配置如下:

  • GPU 0:NVIDIA GeForce RTX 4090 D
  • GPU 1:NVIDIA A100-SXM4-40GB
  • GPU 2:NVIDIA A100-SXM4-40GB

如果只追求稳定,其实最简单的方式是只使用两张 A100:

CUDA_VISIBLE_DEVICES=1,2
--tensor-parallel-size 2

这种部署方式属于双 A100 同构部署,稳定性较好,适合生产主服务。

但问题是,服务器上还有一张 4090D,如果完全不用,就会造成资源浪费。

因此我们希望实现:

  • 三张 GPU 都参与
  • 同一个 vLLM 实例对外提供服务
  • 尽量规避异构 TP 带来的问题
  • 通过 systemd 统一管理服务
  • 使用 OpenAI 风格接口对外调用

这就是本文的方案 A。

二、为什么不推荐直接使用 TP=3

很多人看到三张 GPU,会直接想到:

CUDA_VISIBLE_DEVICES=0,1,2
--tensor-parallel-size 3

这表示把三张 GPU 放进同一个 Tensor Parallel 组里。

如果三张卡都是同型号,比如三张 A100,这种方式相对更容易理解和调优。

但当前机器是:

  • 4090D
  • A100
  • A100

这属于异构 GPU 组合。

2.1 Tensor Parallelism 对 GPU 一致性要求更高

Tensor Parallelism 会把同一层的矩阵计算拆分到多张 GPU 上执行。
这意味着多张 GPU 之间需要频繁同步中间结果。

在异构 GPU 场景下,不同显卡之间存在差异:

  • 架构不同
  • 计算能力不同
  • 显存规格不同
  • 通信效率不同
  • 单步计算耗时不同

最终可能导致一个问题:

快卡在等慢卡。

这会让整体性能不一定比双 A100 更好。

2.2 参数不一致时容易启动失败

如果你设置了:

--tensor-parallel-size 3

但是当前进程实际只看到了两张 GPU,就会出现类似错误:

World size (3) is larger than the number of available GPUs (2)

这个错误的意思非常直接:

  • world size 是 3
  • 但当前节点可用 GPU 只有 2
  • 所以 vLLM 无法启动

这种问题通常由下面几种情况引起:

  • CUDA_VISIBLE_DEVICES 只暴露了两张卡
  • --tensor-parallel-size 却设置成了 3
  • systemd 旧配置没有重新加载
  • 实际生效的启动参数和预期不一致

因此,在异构三卡场景下,不建议一开始就直接上 TP=3

三、方案 A 的核心思路

方案 A 的核心配置是:

CUDA_VISIBLE_DEVICES=0,1,2
--tensor-parallel-size 1
--pipeline-parallel-size 3

它的含义是:

  • 三张 GPU 全部暴露给 vLLM
  • 不做三卡张量并行
  • 改用 Pipeline Parallelism
  • 将模型按层切成三个阶段
  • 每张 GPU 负责一个 pipeline stage

3.1 核心并行关系

vLLM 中可以简单理解为:

world_size = tensor_parallel_size × pipeline_parallel_size

本方案中:

world_size = 1 × 3 = 3

正好对应三张 GPU。

3.2 为什么 PP 更适合异构三卡

Pipeline Parallelism 是按模型层切分。

它不是把同一层计算强行拆给三张不同型号 GPU,而是让不同 GPU 负责模型的不同层段。

这样做有几个好处:

  • 避免异构 GPU 直接进入同一个 Tensor Parallel 组
  • 三张卡仍然可以参与同一个模型实例
  • 部署结构更清晰
  • 更适合先验证异构三卡可行性

不过也要注意:

Pipeline Parallelism 并不保证一定比双 A100 TP 更快。
因为如果某一个 pipeline stage 较慢,其他 stage 仍然可能等待。

所以方案 A 更适合:

  • 异构三卡单实例验证
  • 资源充分利用实验
  • vLLM PP 部署实践
  • 非极致吞吐优先场景

四、方案 A 总体架构图

在这里插入图片描述

图 1 展示了方案 A 的核心结构:

  • vLLM API Server 对外提供统一接口
  • GPU 0 的 4090D 作为 Pipeline Stage 0
  • GPU 1 的 A100 作为 Pipeline Stage 1
  • GPU 2 的 A100 作为 Pipeline Stage 2
  • 整体配置为 TP=1,PP=3

这种方式让三张异构 GPU 都参与同一个 vLLM 实例。

五、最终 systemd 配置

下面是完整可用的 systemd 配置。

文件路径:

/etc/systemd/system/vllm-gemma.service

完整内容如下:

[Unit]
Description=vLLM Gemma GGUF Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/data/models

Environment=CUDA_DEVICE_ORDER=PCI_BUS_ID
Environment=CUDA_VISIBLE_DEVICES=0,1,2
Environment=PYTHONUNBUFFERED=1
Environment=HF_HOME=/root/.cache/huggingface

ExecStart=/root/miniconda3/envs/vllm/bin/vllm serve /data/vllm/models/gemma-3-12b-it-qat-q4_0-gguf/gemma-3-12b-it-q4_0.gguf \
  --tokenizer /data/vllm/models/gemma-3-27b-it \
  --served-model-name gemma3-12b-q4 \
  --tensor-parallel-size 1 \
  --pipeline-parallel-size 3 \
  --host 0.0.0.0 \
  --port 8000 \
  --api-key sk-gemma \
  --language-model-only \
  --max-model-len 16384 \
  --gpu-memory-utilization 0.90 \
  --max-num-seqs 64 \
  --max-num-batched-tokens 16384

Restart=always
RestartSec=10
TimeoutStartSec=0
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target

六、关键配置解释

6.1 固定 CUDA 设备顺序

Environment=CUDA_DEVICE_ORDER=PCI_BUS_ID

异构 GPU 环境下建议保留这个配置。

它可以让 CUDA 按 PCI Bus ID 顺序枚举设备,避免因为默认设备排序导致 GPU 编号和预期不一致。

6.2 暴露三张 GPU

Environment=CUDA_VISIBLE_DEVICES=0,1,2

这表示当前 vLLM 进程可以看到三张 GPU:

  • GPU 0:4090D
  • GPU 1:A100
  • GPU 2:A100

如果写成:

Environment=CUDA_VISIBLE_DEVICES=1,2

那 vLLM 只能看到两张 A100,4090D 不会参与。

6.3 不做三卡 TP

--tensor-parallel-size 1

这表示不做 Tensor Parallelism。

6.4 使用三段 PP

--pipeline-parallel-size 3

这表示把模型按层切成三个 pipeline stage。

6.5 模型服务名保持一致

--served-model-name gemma3-12b-q4

这里建议服务名和实际模型保持一致。

因为实际加载的是:

gemma-3-12b-it-q4_0.gguf

如果服务名还写成类似:

gemma3:27b-it-q8_0

后续接口调用、日志排查、监控都会比较混乱。

6.6 删除 TRANSFORMERS_CACHE

不建议再配置:

Environment=TRANSFORMERS_CACHE=/root/.cache/huggingface

保留下面这一行即可:

Environment=HF_HOME=/root/.cache/huggingface

七、TP=3 与 PP=3 的对比

在这里插入图片描述

图 2 展示了两种部署思路:

第一种是直接 TP=3

  • 同一层计算拆到三张 GPU
  • 对 GPU 一致性和通信要求更高
  • 异构卡场景下可能被慢卡拖住

第二种是 TP=1,PP=3

  • 模型按层切成三段
  • 三张 GPU 分别负责不同 stage
  • 更适合异构三卡先跑通

因此,对于当前的 4090D + A100 + A100 组合,建议优先使用方案 A。

八、启动与验证方式

8.1 重新加载 systemd 配置

修改 service 后执行:

sudo systemctl daemon-reload

8.2 重启 vLLM 服务

sudo systemctl restart vllm-gemma.service

8.3 查看服务状态

sudo systemctl status vllm-gemma.service -n 100 --no-pager

重点观察是否出现:

  • Python traceback
  • world size 报错
  • CUDA 报错
  • 显存不足
  • 模型路径错误
  • tokenizer 路径错误

8.4 实时查看日志

journalctl -u vllm-gemma.service -f

8.5 观察 GPU 使用情况

watch -n 1 nvidia-smi

正常情况下,三张卡都应该出现 vLLM 进程,并且显存会有明显占用。

请求进入时,三张卡的 GPU Util 会出现一定波动。

8.6 测试模型列表接口

curl http://127.0.0.1:8000/v1/models \
  -H "Authorization: Bearer sk-gemma"

正常情况下会返回类似:

{
  "object": "list",
  "data": [
    {
      "id": "gemma3-12b-q4",
      "object": "model"
    }
  ]
}

8.7 测试对话接口

curl http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-gemma" \
  -d '{
    "model": "gemma3-12b-q4",
    "messages": [
      {
        "role": "user",
        "content": "请用100字解释什么是 Pipeline Parallelism"
      }
    ],
    "temperature": 0.7,
    "max_tokens": 256
  }'

如果可以正常返回内容,说明 vLLM OpenAI 风格接口已经可用。

九、部署与验证流程图

在这里插入图片描述

图 3 展示了方案 A 的完整部署流程:

  1. 修改 systemd service
  2. 设置三卡可见
  3. 配置 TP=1,PP=3
  4. 重载 systemd
  5. 重启 vLLM 服务
  6. 查看日志
  7. 使用 nvidia-smi 观察三张卡
  8. 使用 curl 测试接口

这个流程可以作为后续所有 vLLM 服务调试的基础模板。

十、常见问题排查

10.1 报错:World size 大于可见 GPU 数量

典型错误:

World size (3) is larger than the number of available GPUs (2)

原因是:

  • 并行参数要求 3 张 GPU
  • 但当前进程只看到 2 张 GPU

常见错误配置:

CUDA_VISIBLE_DEVICES=1,2
--tensor-parallel-size 3

修复方式:

CUDA_VISIBLE_DEVICES=0,1,2
--tensor-parallel-size 1
--pipeline-parallel-size 3

同时确认 systemd 已经重新加载:

sudo systemctl daemon-reload
sudo systemctl restart vllm-gemma.service

10.2 服务还是使用旧参数

如果你已经改了 service,但日志里还是出现旧参数,可能是没有执行:

sudo systemctl daemon-reload

建议直接查看当前 systemd 实际加载的配置:

sudo systemctl cat vllm-gemma.service

确认里面是否仍然存在旧参数,比如:

--tensor-parallel-size 3

10.3 仍然出现 TRANSFORMERS_CACHE 警告

如果日志里仍然看到:

Using TRANSFORMERS_CACHE is deprecated

说明配置里还残留:

Environment=TRANSFORMERS_CACHE=/root/.cache/huggingface

删除它,只保留:

Environment=HF_HOME=/root/.cache/huggingface

然后重新加载服务:

sudo systemctl daemon-reload
sudo systemctl restart vllm-gemma.service

10.4 三张卡显存不完全一致

这是正常现象。

Pipeline Parallelism 是按层切分模型,不是让每张卡做完全一样的事情。
因此三张 GPU 的显存占用、GPU Util 不一定完全相同。

只要满足下面条件,就说明服务基本正常:

  • 三张 GPU 都有 vLLM 进程
  • 三张 GPU 都有显存占用
  • 请求能够正常返回
  • 日志没有持续报错

10.5 服务启动慢

vLLM 启动时需要加载模型、初始化 tokenizer、分配 KV cache、构建执行图,因此大模型首次启动慢是正常现象。

建议通过下面命令观察:

journalctl -u vllm-gemma.service -f

如果长时间卡住,可以重点检查:

  • 模型路径是否正确
  • tokenizer 路径是否正确
  • 显存是否不足
  • 是否开启了过大的 max-model-len
  • 是否设置了过大的 max-num-seqs

十一、性能调优建议

11.1 首次启动建议保守一点

如果是第一次验证异构三卡 PP,建议先用保守配置:

--max-model-len 8192
--gpu-memory-utilization 0.85
--max-num-seqs 32
--max-num-batched-tokens 8192

等服务稳定后,再逐步提高到:

--max-model-len 16384
--gpu-memory-utilization 0.90
--max-num-seqs 64
--max-num-batched-tokens 16384

11.2 不要默认认为三卡 PP 一定更快

方案 A 的重点是:

  • 三张卡都参与
  • 单实例部署
  • 异构 GPU 验证
  • 模型按层切分

但它不一定比双 A100 TP=2 更快。

原因是 PP 会受到最慢 stage 的影响。
如果 4090D 所在 stage 较慢,两张 A100 可能需要等待。

所以建议你实际对比:

方案 配置 特点 适合场景
双 A100 TP CUDA_VISIBLE_DEVICES=1,2 + TP=2 稳定、简单、性能好 生产主服务
三卡 TP CUDA_VISIBLE_DEVICES=0,1,2 + TP=3 看起来简单,但异构不稳 不建议优先使用
三卡 PP CUDA_VISIBLE_DEVICES=0,1,2 + TP=1,PP=3 三卡都参与,适合异构验证 本文方案 A
双实例分流 A100 实例 + 4090D 实例 更灵活、更适合长期生产 进阶方案

11.3 建议压测指标

后续可以重点观察这些指标:

  • 首 token 延迟
  • 平均 tokens/s
  • 并发请求数
  • GPU 利用率
  • 显存占用
  • 请求失败率
  • 请求排队时间
  • vLLM 日志中的调度信息

测试时建议分别对比:

  • 双 A100 TP=2
  • 三卡 PP=3
  • 单 4090D
  • 单 A100
  • 双实例分流

这样才能判断当前方案是否真的适合你的业务请求类型。

十二、适合放到文章中的最终配置总结

本文最终采用的部署方式是:

CUDA_DEVICE_ORDER=PCI_BUS_ID
CUDA_VISIBLE_DEVICES=0,1,2

vLLM 并行参数是:

--tensor-parallel-size 1
--pipeline-parallel-size 3

完整目标是:

  • 4090D 参与
  • 两张 A100 参与
  • 单实例部署
  • 不做异构 TP
  • 使用 Pipeline Parallelism 按层切分
  • 通过 OpenAI 风格接口对外服务

十三、总结

对于 RTX 4090D + 双 A100 这种异构 GPU 服务器,最直观的想法是直接使用:

--tensor-parallel-size 3

但这种方式不一定适合异构场景。

更稳妥的方案是:

--tensor-parallel-size 1
--pipeline-parallel-size 3

也就是本文的方案 A。

它的核心价值在于:

  • 三张 GPU 都能参与同一个 vLLM 实例
  • 避免异构 GPU 直接进入同一个 Tensor Parallel 组
  • 配置相对清晰
  • 适合先验证三卡异构推理可行性
  • 方便后续和双 A100 TP=2 做性能对比

不过也要明确:

方案 A 不一定是极致吞吐最优解,它更适合作为异构三卡单实例部署的验证方案。

如果后续追求长期生产稳定,可以继续演进为:

  • A100 + A100 作为主推理服务
  • 4090D 作为独立副服务
  • 前面通过 API 网关按请求类型分流

这样既能利用所有 GPU,也能让整体架构更加稳定、可控、易维护。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐