
先讲个真实场景机器上插了六张 A100 40G模型是 Qwen2.5-72B-Instruct任务是用 vLLM 起一个 OpenAI 兼容的推理服务。我朋友的启动命令写得没毛病--tensor-parallel-size 6看起来合情合理——六张卡就填六个并行度对吧结果服务起不来报错刷了一屏折腾两天没搞定。我过去看了一眼报错第一句话就是这跟显存没关系你先把 6 这个数字换掉。这篇文就是这次6卡 serve qwen 失败的完整复盘从并行原理讲到具体报错再给三套实测能跑的配置。不管你用的是 Qwen2.5 全系哪个尺寸还是准备换 Qwen3 的小模型只要你手里是6卡这种非主流数量这篇文章都能帮你少浪费一个周末。1. 先算笔账6卡到底想跑多大的Qwen1.1 你的目标是一个大模型还是很多并发多卡部署前必须先想清楚一件事你要的是单个大模型跑起来还是同时服务很多请求这两个目标对应的并行策略完全不一样。如果是前者比如 72B 这种单卡塞不下的模型你需要的是把模型拆开、每张卡只装一部分。这种玩法叫张量并行Tensor ParallelismTP和流水线并行Pipeline ParallelismPP。如果是后者比如模型本身不大、但请求量大你需要的是把模型复制成多份每份各接一部分流量这叫数据并行Data ParallelismDP。很多人一上手就是--tensor-parallel-size 6其实默认自己选的是前者但手里那个模型可能根本不需要拆。我见过最典型的场景是有人拿六张 4090 去 serve Qwen2.5-7B。7B 模型 FP16 权重撑死 15GB一张 24G 卡跑得舒舒服服还能留出大量 KV Cache。你非要用 6 卡张量并行结果就是每秒多请求没涨多少通信开销倒是先把延迟拖垮了。所以在动手之前先回答两个问题模型多大单卡多大如果模型权重在单卡显存里能放得下优先考虑数据并行只有放不下才考虑 TP 和 PP。1.2 权重、KV Cache、余量一张表算明白部署前必须会算显存账。模型权重占多少公式很简单权重显存 参数量 × 每个参数的字节数BF16/FP16 每个参数占 2 字节INT8 占 1 字节INT4 量化大约占 0.5 字节。Qwen2.5 全系尺寸对应的权重大小我整理了一下模型参数量BF16 权重INT8 权重INT4 量化权重Qwen2.5-7B约 7.6B约 15GB约 8GB约 4.5GBQwen2.5-14B约 14.8B约 30GB约 15GB约 8GBQwen2.5-32B约 32.5B约 65GB约 33GB约 18GBQwen2.5-72B约 72.7B约 145GB约 73GB约 40GB但权重只是第一部分。推理时还有两块大开销激活值和 KV Cache。KV Cache 是大头它跟上下文长度、并发请求数直接相关。以 Qwen2.5-72B 为例每个 token 的 KV Cache 大约是 0.31MB你给 32K 上下文单条请求就要吃掉约 10GB。如果你同时跑 8 条 32K 的请求光 KV Cache 就是 80GB。Qwen2.5-7B 因为 KV head 少每个 token 大约 0.055MB32K 上下文才约 1.8GB。所以六张卡到底能跑什么取决于卡型。6×24G 总共 144GB跑 72B BF16 权重就要 145GB连权重都放不下必须量化或者换小模型6×40G 总共 240GB跑 72B BF16 权重后剩 95GB 给 KV Cache理论可行但很紧6×48G 甚至 6×80G 才谈得上从容。别一上来就抱怨 OOM你大概率从一开始就没算明白这笔账。2. 6卡失败的根源Qwen的注意力头数不让你TP62.1 张量并行为什么必须整除张量并行的大白话解释一个 Transformer 层里有好几个矩阵乘法TP 就是把这些矩阵按列或者「行切成几份每张卡算一份最后拼接。注意Q、K、V 的投影矩阵是按注意力头来切的但不是随便切必须保证每张卡分到整数个注意力头。为什么因为每个注意力头是一个完整的计算单元你不能把一个头劈成两半劈了就没法做缩放点积注意力了数学上不成立。所以对标准的 MHA多头注意力结构要求num_attention_heads % tensor_parallel_size 0对 Qwen 这种 GQA分组查询注意力结构要求更严格除了 Q head 要整除KV head 也必须整除因为每张卡都要自己持有对应分组的 KV Head。vLLM 这类框架在加载模型时就会做这个检查不满足直接抛ValueError拒绝启动。这不是 bug是硬约束。2.2 Qwen2.5全系的整除性速查表我把 Qwen2.5 全系的层数、Q head、KV head 和可用 TP 值列出来这张表建议保存模型层数Q HeadKV Head可用的 TP 值TP6 是否可行Qwen2.5-7B282841, 2, 4不行28/6 和 4/6 都除不尽Qwen2.5-14B484081, 2, 4, 5, 8不行40/6 除不尽Qwen2.5-32B646481, 2, 4, 8不行64/6 和 8/6 都除不尽Qwen2.5-72B806481, 2, 4, 8不行64/6 和 8/6 都除不尽发现规律了吗Qwen2.5 全系的 KV head 只有 4 或 8 两种而 6 不是 4 的倍数、也不是 8 的倍数。这意味着你不管选哪个尺寸--tensor-parallel-size 6都是死路一条。类似地TP3 也基本不可用因为这些模型的 Q head 个数大多不是 3 的倍数。TP 这个参数在 Qwen 家族里最安全的值永远是 1、2、4、8 这类 2 的幂次。2.3 为什么TP3也不行你可能想退一步TP6 不行那 TP3 × PP2 呢结果同样不行。因为 TP3 要求 Q head 和 KV head 都能被 3 整除。翻一下上面的表7B 的 Q head 是 2828 除以 3 等于 9.33不行14B 的 40 除以 3 也不行72B 的 64 除以 3 也不行。唯一有点希望的是 KV head8 的模型若 Q head 是 24 或 48 才能用 TP3但 Qwen 全系都没有这种配置。所以6 卡这个数量在 Qwen 这里是天然尴尬的6 只能拆成 2×3 或 3×2而 3 作为张量并行度在 Qwen 上不存在剩下唯一的组合就是 TP2 × PP3。这也意味着想六张卡全用起来你的选择非常有限要么流水线并行要么干脆放弃单实例走数据并行。这个结论是我踩完坑之后才彻底想明白的。3. 踩坑全记录从报错到真相的完整debug过程3.1 报错一KV heads 无法整除朋友第一次启动时的命令大概是这个风格vllm serve /data/models/Qwen2.5-72B-Instruct \ --tensor-parallel-size 6 \ --gpu-memory-utilization 0.9 \ --max-model-len 32768日志里出现的第一条致命错误类似这样ValueError: Number of KV heads (8) must be divisible by tensor parallel size (6). Got kv_heads8, tensor_parallel_size6.看到这里第一反应不应该是调参而是应该停下来说这个组合结构上就不成立。我当时直接在群里回了句把 6 改成 4但这只是应急。真正的教训是动手部署之前先去看一眼模型的config.json确认num_attention_heads和num_key_value_heads再决定并行度。这一步 30 秒就能做能省下后面一天的 debug 时间。3.2 报错二NCCL 卡死或超时朋友不甘心把启动参数换成--tensor-parallel-size 4结果没报 head 错误了新的问题来了进程卡在 NCCL 初始化日志刷出大量NCCL_DEBUGINFO级别的通信信息然后超时。RuntimeError: NCCL error in: ... unhandled system error, NCCL version 2.20.5 [rank0]: NCCL communicator creation failed: 7, unhandled cuda error这类问题在多卡机上很常见原因通常是卡之间的互联拓扑和通信配置不匹配。排查办法nvidia-smi topo -m nvidia-smi nvlink -stopo -m会打印出卡之间的互联拓扑。如果是八卡机器前四张卡和后四张卡往往分布在两颗 CPU 上跨 NUMA 节点通信要走 PCIe速度慢一个数量级。如果两张卡之间只有NV#或PIX标记走 NVLink 或 PCIe Switch问题不大如果出现PHB、SYS标记跨 CPU 通信就要小心 NCCL 超时。临时解法是在启动命令前加环境变量export NCCL_P2P_DISABLE1 export NCCL_SOCKET_IFNAMEeth0NCCL_P2P_DISABLE1会强制关闭 GPU 直连通信走共享内存和系统内存中转速度会下降但能绕开 P2P 初始化失败。这里要注意这只是应急不是长久之计。真正要做的是检查驱动版本和 NCCL 版本是否匹配以及确认卡本身的拓扑结构从硬件层面解决。3.3 报错三权重下载不完整第三个坑更蠢也更容易被忽略。模型是从 HuggingFace 上拉下来的但下载时网络断了脚本没重启safetensors文件只下了一半。vLLM 启动时加载权重报错KeyError: model.layers.0.self_attn.q_proj.weight或者SafetensorsError: Error while deserializing header: InvalidHeader这种问题跟并行配置、显存一点关系都没有纯粹是文件没下全。很多人会陷入误区以为是模型格式不支持其实只是文件坏了。解决办法就是重新下载并且建议用带断点续传的工具。国内环境最省心的是用 ModelScopepip install -U modelscope modelscope download --model Qwen/Qwen2.5-72B-Instruct --local_dir ./Qwen2.5-72B-InstructModelScope 上的 Qwen 权重是官方同步的目录结构和 HuggingFace 一致vLLM 直接指定本地路径就能加载。如果习惯用 HuggingFace 的生态也可以设置镜像端点配合huggingface-cli download但一定下载完成后检查一遍目录里的.safetensors文件大小是不是和远端一致别等到加载报错才发现。3.4 报错四模型加载成功但一请求就OOM权重没问题、并行度也对了之后服务终于能起来了。但发第一个请求日志立刻抛torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.00 GiB这个 OOM 不是模型权重占满了而是 KV Cache 分配不了。vLLM 默认会把空闲显存的一大半拿去给 KV Cache 用max-model-len又默认取模型上限。Qwen2.5 支持 128K 上下文vLLM 一开始就按 128K 去预留 KV Cache90% 显存利用率都扛不住。解法是显式收紧参数vllm serve /data/models/Qwen2.5-72B-Instruct \ --tensor-parallel-size 4 \ --max-model-len 8192 \ --gpu-memory-utilization 0.85 \ --max-num-seqs 8--max-model-len 8192把上下文砍到 8KKV Cache 的预留量立刻降一个数量级--max-num-seqs 8限制并发序列数防止同时进来的请求把 KV Cache 瞬时打爆。这里的原则是先让服务能跑再逐步往上调上下文长度和并发数。我见过太多人死在默认参数上其实只要把这两个参数往下调一档立刻就能起来。4. 6卡的正解三套实测方案对比4.1 方案ATP4 2张空闲如果你的目标是稳定跑起来一个大模型最简单可靠的方式就是放弃让 6 张卡都在一个模型里工作的想法老老实实 TP4剩两张卡空着或者干别的。CUDA_VISIBLE_DEVICES0,1,2,3 vllm serve /data/models/Qwen2.5-72B-Instruct \ --tensor-parallel-size 4 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9实测下来4×40G 跑 72B BF16每张卡权重占约 36GB剩余 4GB 给 KV Cache总共约 16GB跑 8K 上下文没问题但再往上就绷不住。如果你手里是 4×48G 或 4×80G那余量就舒服多了32K 上下文也能从容应对。这个方案最大的优点是没有花活——TP4 对 Qwen2.5 全系都是合法值模型头数全能整除启动即用。缺点是两张卡闲置整机利用率只有三分之二。但话说回来部署上线图的是稳定不是把每一克显存都压榨干净。4.2 方案BTP2 × PP3如果你确实想让 6 张卡都服务于同一个大模型唯一可行的组合是 TP2 × PP3。TP2 在 Qwen 上完全合法PP3 把网络层切成 3 段每段分配给一组卡。vllm serve /data/models/Qwen2.5-14B-Instruct \ --tensor-parallel-size 2 \ --pipeline-parallel-size 3 \ --max-model-len 16384注意一个前提PP 要求把层数尽量均匀地分到每个 stage。Qwen2.5-14B 是 48 层48/316完美切分Qwen2.5-7B 是 28 层28/39.33切不均匀Qwen2.5-32B 是 64 层同样切不均匀。vLLM 对不均匀切分支持有限有些版本直接报错有些版本能跑但性能打折。所以这个方案真正适合的只有 14B 这种层数刚好是 3 的倍数的模型。流水线并行还有一个内在代价stage 之间有依赖前一个 stage 算完才能把中间结果发给下一个 stage会产生流水线气泡单位时间吞吐不如纯 TP。但它能让单个大模型的显存容量翻倍是六卡必须全上时的唯一现实解。4.3 方案C3个TP2实例做数据并行这是我最推荐的方案尤其当你手里是 7B、14B 这种中小模型且业务对吞吐有真实需求。思路很简单六个卡拆成三组每组两张卡跑一个 TP2 的实例三个实例互为副本前面挂一个负载均衡。每个实例的启动命令CUDA_VISIBLE_DEVICES0,1 vllm serve /data/models/Qwen2.5-7B-Instruct \ --tensor-parallel-size 2 \ --port 8001 \ --max-model-len 32768 CUDA_VISIBLE_DEVICES2,3 vllm serve /data/models/Qwen2.5-7B-Instruct \ --tensor-parallel-size 2 \ --port 8002 \ --max-model-len 32768 CUDA_VISIBLE_DEVICES4,5 vllm serve /data/models/Qwen2.5-7B-Instruct \ --tensor-parallel-size 2 \ --port 8003 \ --max-model-len 32768再用 Nginx 把这几个端口作为 upstream 做一个简单负载均衡一个入口对外。实测下来三实例的总吞吐大约是单实例 TP6如果它能跑的话的三倍因为三个副本完全独立互不抢通信带宽也不存在并行同步等待。对 7B 模型来说2×24G 的卡组放 15GB 权重后还有 33GB 给 KV Cache32768 上下文绰绰有余。我把这种方案叫用正确的方式浪费硬件——每个实例都只用了两张卡但总吞吐拉满了。4.4 方案取舍表与最终结论方案适用模型单请求延迟总吞吐部署复杂度六卡利用率TP4 2 空闲32B/72B 等单大模型低中低67%TP2 × PP314B 等层数可整除模型中中低中100%3 × TP2 数据并行7B/14B 等中小模型中高中100%我的建议如果是 72B 这种大模型直接 TP4剩下两张卡拿去跑 RAG 嵌入模型或者干脆留着做容错如果是 7B/14B无脑三实例数据并行。中间的 32B64 层没法 PP3TP4 又需要单卡显存足够最好先量化成 AWQ/GPTQ 再上 TP4。5. 多卡部署排错速查表5.1 每条命令干什么多卡排错时我习惯按这个顺序执行命令每一步都有明确目的。先看硬件状态nvidia-smi nvidia-smi topo -mnvidia-smi看每张卡的利用率、显存占用和进程。如果服务起来了但某张卡利用率几乎为零说明并行配置有问题可能有卡在空等。topo -m看卡间拓扑确认通信路径。再看通信配置export NCCL_DEBUGINFO这个环境变量会让 NCCL 打印详细的初始化过程包括每对卡之间的通信走的是 NVLink 还是 PCIe。报错时信息量极大能直接看出是 P2P 失败还是 socket 超时。最后才是看框架日志vllm serve ... 21 | tee serve.log把日志落到文件里而不是只打在终端上。分布式服务有多个 rank崩溃信息经常交错落盘之后可以按[rank0]、[rank1]过滤着看定位是哪一张卡先出问题。5.2 我现在的部署检查清单我现在每拿到一台多卡机器不管跑哪个模型都会先过一遍下面这个清单读config.json看 Q head 和 KV head算并行度是否整除。这一步是最快排除结构性问题的方式。算显存权重 KV Cache 激活值确认目标上下文长度和并发数在总显存内有富余。检查下载的权重文件完整性重点看.safetensors文件大小。用小并发、短上下文先启动一次确认能通再逐步放大参数。遇到 NCCL 问题先区分是拓扑限制还是环境配置问题不要上来就禁用 P2P。这套流程帮我解决过的失败案例比这篇文章里写的还多。很多所谓的6卡 serve qwen 失败其实都不是什么高深问题就是并行度选错、显存没算、文件没下全这三件事的组合。最后再分享一个个人习惯我从来不在模型跑不起来的时候反复改随机参数。每次改参数之前先强制自己回答一个问题——这个参数影响的是显存、通信还是计算逻辑。答不上来就去查不查清楚不动手。分布式推理这个东西表面上是一堆命令实际上是对模型结构和硬件资源的双重理解你对这两件事的把握每深一层能少踩的坑就多一批。希望你下次面对六张卡的时候不是对着报错发呆而是心里已经有了那张整除性速查表。