1. 为什么我选择在两张 4090 上折腾 Qwen3.6-27B
先把结论摆在前面:Qwen3.6-27B 这个体量的模型,放在两张 4090 上跑本地推理,是当前消费级硬件里性价比相当高的一套组合,但它绝对不是"插上就能用"的那种省心方案。我从早上九点折腾到晚上十一点,中间经历了显存爆掉、驱动版本对不上、量化格式加载失败、多卡通信卡死等一系列问题,最后才把一套稳定的部署流程跑通。这篇记录就是把这十几个小时里踩过的坑、试过的方案、最后跑通的配置,原原本本写出来,给准备上同样配置的朋友省点时间。
先说清楚这套东西是什么、能干什么。Qwen3.6-27B 是一个 270 亿参数规模的大语言模型,27B 这个量级很有意思——它比 7B、14B 这类小模型明显更"聪明",在长文本理解、代码生成、多轮对话的连贯性上都有质的提升,但又不像 70B、100B 以上的模型那样对硬件要求高到离谱。两张 4090 加起来 48GB 显存(每张 24GB),配合 FP8 量化,刚好能把这个模型塞进去,还能留出一定的 KV Cache 空间做长上下文推理。
那为什么不用单卡?单张 4090 的 24GB 显存,跑 FP8 量化的 27B 模型,权重本身大概占 27GB 左右(FP8 每参数 1 字节,加上一些开销),单卡根本放不下。就算用更激进的 4bit 量化,权重压到 14GB 左右能塞进单卡,但推理质量和上下文长度都会打折扣,而且 4bit 在 4090 这种消费卡上的算子支持不如 FP8 成熟。所以两张卡是这套配置的合理起点。
适合谁来参考这篇内容?如果你手上正好有两张 4090,想跑一个能力够用、响应够快的本地大模型,或者你在做私有化部署、数据不能出内网的场景,那这篇基本能覆盖你 80% 的问题。如果你只有一张卡,也可以看,我会在显存计算那部分讲清楚单卡能跑到什么程度。如果你用的是 A100、H100 这类专业卡,那这篇的很多坑你不会遇到,但多卡部署的思路是相通的。
我用的软件栈是 vLLM 作为推理引擎,这是目前社区里跑大模型推理最主流的选择之一,吞吐高、对多卡支持好、OpenAI 兼容的 API 开箱即用。量化格式用 FP8,这是 4090 这代卡(Ada Lovelace 架构)原生支持的低精度格式,比 BF16 省一半显存,精度损失又比 4bit 小得多。下面我按"整体设计思路—核心细节—实操过程—问题排查"的顺序展开,每一步都会说清楚为什么这么做。
2. 整体方案设计与选型背后的考量
2.1 推理引擎为什么选 vLLM 而不是别的
本地跑大模型,绕不开几个主流选择:vLLM、SGLang、Ollama、transformers 原生推理。我一开始也纠结过,最后选 vLLM,理由很实在。
Ollama 确实是最省心的,一条命令拉模型就能跑,但它的定位是"个人快速体验",多卡支持弱,对 FP8 这种量化格式的支持也不够灵活,而且它的并发吞吐在高负载下明显不如 vLLM。我试过用 Ollama 跑类似的模型,单请求还行,一旦并发上来,响应时间就崩了。
SGLang 是这两年的新秀,在结构化输出、前缀缓存这些场景下性能很猛,但它的生态成熟度和文档完善度相比 vLLM 还是差一截,尤其是多卡部署的踩坑资料少。我这次的目标是"稳定跑通",不是"压榨极限性能",所以选了更稳的 vLLM。
transformers 原生推理是最灵活的,什么模型都能加载,但它的推理速度慢、显存管理粗糙,27B 模型用 transformers 跑,两张 4090 也就勉强能出结果,吞吐低到没法实际用。它更适合做模型调试和验证,不适合做服务。
vLLM 的核心优势在于它的 PagedAttention 机制——简单说就是把 KV Cache 像操作系统管理内存分页那样管理起来,显存利用率高,碎片少,长上下文场景下优势特别明显。再加上它对张量并行(Tensor Parallelism)的支持很成熟,两张卡拆模型跑起来很顺。这就是我选它的根本原因。
2.2 FP8 量化:4090 的甜点区
量化格式的选择直接决定了你能不能把模型塞进显存,以及推理质量能保住多少。常见的几个选项:FP16、BF16、FP8、INT8、INT4。
FP16 和 BF16 都是 2 字节每参数,27B 模型光权重就要 54GB,两张 4090 的 48GB 根本不够,直接出局。INT4 是 0.5 字节每参数,权重压到 14GB 左右,单卡都能跑,但 4bit 量化的精度损失在 27B 这个量级上比较明显,尤其是数学推理和代码生成任务,错误率会上升。而且 4090 对 INT4 的算子优化不如 FP8 到位,实际速度未必更快。
FP8 是 1 字节每参数,权重约 27GB,两张卡分摊下来每张 13.5GB 左右,加上 KV Cache 和激活值,每张卡占用在 18-20GB,留有余量。关键是 4090 属于 Ada Lovelace 架构,原生支持 FP8 的矩阵运算,硬件层面就有加速,这是它相比 30 系卡的一大优势。精度上,FP8 相比 BF16 的损失很小,在大多数对话和生成任务里几乎感知不到差异。
这里要澄清一个常见混淆:FP8 和 BF16、FP16 到底啥区别。你可以把它们理解成"用多少位来存一个数字"。FP16 用 16 位,BF16 也用 16 位但指数位更多、精度位更少(所以动态范围大但精度略低),FP8 只用 8 位,省一半空间,代价是能表示的数值范围和精度都变小。对于大模型推理,权重和激活值的分布通常比较集中,FP8 够用,这就是它能在 4090 上成为甜点区的原因。
2.3 多卡方案:张量并行怎么拆
两张卡跑一个模型,核心问题是"怎么把模型切开"。主流有两种并行方式:张量并行(TP)和流水线并行(PP)。
张量并行是把每一层的权重矩阵按维度切分到多张卡上,每张卡算一部分,然后通过通信把结果汇总。它的优点是负载均衡好,每张卡干的活差不多,延迟低。缺点是卡间通信频繁,对卡间带宽要求高。流水线并行是把模型的不同层分到不同卡上,像流水线一样一级一级传,通信少但会有"气泡"(某些卡在等前面的卡算完),延迟高。
对于两张 4090 这种配置,张量并行是更合适的选择,因为 4090 之间如果走 NVLink 或者 PCIe 4.0 x16,带宽足够支撑 TP 的通信量。我这次用的是 TP=2,也就是两张卡做张量并行。这里有个关键点:TP 的度数最好能整除模型的注意力头数,Qwen3.6-27B 的注意力头数能被 2 整除,所以 TP=2 没问题。如果头数是奇数,TP=2 就会报错,那时候就得考虑别的拆法。
显存的具体计算我列个表,方便你对照自己的配置:
| 项目 | 单卡占用(FP8, TP=2) | 说明 |
|---|---|---|
| 模型权重 | 约 13.5GB | 27B 参数 × 1 字节 ÷ 2 卡 |
| KV Cache | 约 4-6GB | 取决于上下文长度和并发数 |
| 激活值与其他开销 | 约 1-2GB | 推理过程中的临时张量 |
| 合计 | 约 18-21GB | 留 3-6GB 余量给系统 |
这个表是估算,实际会因为你设置的max-model-len(最大上下文长度)和gpu-memory-utilization(显存利用率上限)而变化。我建议gpu-memory-utilization设成 0.9,别设 0.95 以上,留点余量给驱动和系统,不然容易 OOM。
3. 核心细节解析与实操要点
3.1 驱动和 CUDA 版本:最容易翻车的第一步
我踩的第一个大坑就是驱动。4090 是 Ada Lovelace 架构,需要比较新的驱动才能正常识别和发挥性能。我一开始用的是系统自带的旧驱动,nvidia-smi能显示卡,但一跑 vLLM 就报 CUDA 相关的错,折腾了半天才发现是驱动版本太老。
正确的做法是:先确认你的系统版本,然后装匹配的驱动和 CUDA。我用的是 Ubuntu 24.04,这个版本对 4090 的支持比较好。如果你用的是 CentOS 7 这类老系统,装 4090 驱动会麻烦很多,因为内核版本太老,驱动编译容易失败,我建议直接换 Ubuntu 22.04 或 24.04,能省掉大量折腾。
装驱动的步骤我列一下,这是跑通的基础:
# 先卸载可能存在的旧驱动 sudo apt-get purge nvidia-* sudo apt-get autoremove # 添加官方源(Ubuntu) sudo add-apt-repository ppa:graphics-drivers/ppa sudo apt update # 查看推荐的驱动版本 ubuntu-drivers devices # 安装推荐版本(我装的是 550 系列) sudo apt install nvidia-driver-550 # 重启 sudo reboot # 验证 nvidia-sminvidia-smi输出里要能看到两张 4090,并且 CUDA Version 显示 12.4 以上。如果只看到一张卡,检查一下是不是卡没插好,或者 BIOS 里 PCIe 插槽没启用。
CUDA Toolkit 我装的是 12.4,配合 PyTorch 2.4+。这里有个细节:vLLM 对 PyTorch 和 CUDA 的版本匹配很敏感,版本对不上会报各种奇怪的错。我建议直接用 vLLM 官方推荐的组合,别自己乱配。装 vLLM 的时候用 pip 装,它会自动拉取匹配的 PyTorch 版本。
注意:装驱动之前一定要先禁用系统自带的 nouveau 驱动,否则会冲突。Ubuntu 下编辑
/etc/modprobe.d/blacklist-nouveau.conf,加入blacklist nouveau和options nouveau modeset=0,然后sudo update-initramfs -u。
3.2 vLLM 安装与环境隔离
vLLM 的安装我强烈建议用虚拟环境,别直接装在系统 Python 里。原因很简单:vLLM 依赖的 PyTorch、CUDA 库版本很特定,装到系统里容易和其他项目冲突,出了问题也不好回滚。
# 创建虚拟环境 python3 -m venv vllm-env source vllm-env/bin/activate # 升级 pip pip install --upgrade pip # 安装 vLLM(会自动拉取匹配的 torch) pip install vllm # 验证安装 python -c "import vllm; print(vllm.__version__)"我装的时候 vLLM 版本是 0.6.x 系列,这个版本对 FP8 和多卡的支持都比较成熟。如果你装的是更老的版本,可能会遇到 FP8 加载失败的问题,建议升到 0.6 以上。
这里有个坑要提醒:vLLM 安装过程中会编译一些 CUDA 算子,如果你的机器上没有装 nvcc(CUDA 编译器),编译会失败。解决办法是装 CUDA Toolkit,或者用预编译的 wheel。我建议装完整的 CUDA Toolkit,虽然占空间,但省心。
3.3 模型下载与 FP8 权重获取
Qwen3.6-27B 的 FP8 权重,社区里有现成的版本可以直接下载。我建议优先用官方或知名社区发布的 FP8 版本,别自己量化,因为自己量化需要校准数据集,搞不好精度掉得厉害。
下载模型的时候注意磁盘空间,FP8 版本大概 27GB 左右,加上 tokenizer 和其他文件,留 35GB 空间比较稳妥。下载方式我用的是 huggingface 的命令行工具:
pip install huggingface_hub huggingface-cli download <模型仓库名> --local-dir ./qwen3.6-27b-fp8下载完之后检查一下目录结构,应该有config.json、model.safetensors(可能分片成多个文件)、tokenizer.json等。如果缺文件,加载的时候会报错。
提示:下载大模型很耗时,建议用支持断点续传的工具,或者提前在网速好的环境下载好再拷贝过去。我这次下载花了将近两个小时,中途断了一次,幸好工具支持续传。
3.4 启动参数的关键配置
vLLM 启动命令的参数很多,但真正影响能不能跑起来、跑得好不好的就那么几个。我把关键参数列出来,逐个解释。
python -m vllm.entrypoints.openai.api_server \ --model ./qwen3.6-27b-fp8 \ --tensor-parallel-size 2 \ --dtype float8 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --port 8000 \ --host 0.0.0.0--tensor-parallel-size 2是两张卡做张量并行,这个必须和你的卡数匹配。--dtype float8指定用 FP8 精度,如果你的权重本身就是 FP8,这个参数能确保 vLLM 用正确的精度加载。--max-model-len 8192是最大上下文长度,这个值直接影响 KV Cache 的显存占用,设太大容易 OOM,设太小又不够用。8192 是个比较平衡的值,如果你需要更长的上下文,可以往上调,但要相应降低gpu-memory-utilization或者减少并发。
--gpu-memory-utilization 0.9是显存利用率上限,意思是 vLLM 最多用 90% 的显存。别设太高,留点余量给系统和其他进程。我一开始设了 0.95,结果跑着跑着就 OOM 了,降到 0.9 之后稳定了。
启动之后,如果看到日志里显示模型加载成功、API server 监听在 8000 端口,那就成功了一大半。第一次加载会比较慢,因为要把 27GB 的权重读进显存并做初始化,耐心等几分钟。
4. 完整实操过程与关键环节记录
4.1 从零到跑通的完整时间线
我把这一天的折腾过程按时间线记下来,你能看到每个阶段花了多久、卡在哪里,心里有个预期。
早上九点到十点,装驱动和 CUDA。这一步比预想的顺利,因为 Ubuntu 24.04 对 4090 支持好,ubuntu-drivers直接推荐了合适的版本,装完重启就识别了。如果你用老系统,这一步可能就要花掉半天。
十点到十一点,装 vLLM 和依赖。这一步卡了一下,因为第一次装的时候没装 CUDA Toolkit,编译算子失败。装上 Toolkit 之后重装,顺利通过。
十一点到下午一点,下载模型。网速一般,27GB 下了两个小时,中间断了一次,续传搞定。
下午一点到三点,第一次启动尝试。这一步是重灾区。第一次启动直接 OOM,报显存不足。我以为是模型太大,后来发现是gpu-memory-utilization设太高,加上max-model-len设了 32768,KV Cache 把显存吃光了。把这两个参数调下来之后,模型能加载了。
下午三点到五点,多卡通信问题。模型加载成功,但一发起推理请求就卡死,日志显示卡在通信环节。查了半天,发现是 NCCL 的配置问题。两张 4090 之间走 PCIe,需要设置NCCL_P2P_DISABLE=1来禁用 P2P(点对点)通信,因为某些主板的 PCIe 拓扑不支持 4090 之间的 P2P。设置这个环境变量之后,通信正常了。
下午五点到晚上八点,性能调优和稳定性测试。跑通之后开始压测,发现并发一高响应就变慢,调整了max-num-seqs(最大并发序列数)和 KV Cache 的块大小,找到了一个平衡点。
晚上八点到十一点,反复重启验证稳定性,确认配置可复现,整理参数。
4.2 显存参数的精确计算过程
显存是这套配置的核心约束,我把计算过程写清楚,你可以照着算自己的配置。
模型权重的显存占用:27B 参数,FP8 每参数 1 字节,总共 27GB。TP=2 的情况下,每张卡分 13.5GB。
KV Cache 的占用:这个和上下文长度、并发数、模型的层数、注意力头数都有关。粗略估算公式是:KV Cache = 2 × 层数 × 头数 × 头维度 × 上下文长度 × 并发数 × 精度字节数。Qwen3.6-27B 大概是 48 层左右,头数和头维度加起来,在 8192 上下文、并发 8 的情况下,KV Cache 大概占 4-6GB。这个值会随并发数线性增长,所以并发开太大,KV Cache 会爆。
激活值和临时张量:推理过程中产生的中间结果,大概 1-2GB。
把这三部分加起来,每张卡占用在 18-21GB,24GB 的卡留 3-6GB 余量,比较安全。如果你把max-model-len调到 16384,KV Cache 翻倍,每张卡占用就到 22-27GB,很可能 OOM。所以长上下文和并发数是一对矛盾,要根据实际需求取舍。
4.3 多卡通信的配置细节
两张 4090 之间的通信是这套配置里最容易被忽视、也最容易出问题的地方。4090 没有 NVLink(消费卡都不带),只能走 PCIe。PCIe 的带宽比 NVLink 低不少,而且如果两张卡插在不同的 PCIe 插槽上,可能走的是不同的 PCIe Root Complex,通信要绕路,延迟更高。
我遇到的问题是 NCCL 默认尝试用 P2P 通信,但某些主板的 PCIe 拓扑不支持 4090 之间的 P2P,导致通信卡死。解决办法是设置环境变量:
export NCCL_P2P_DISABLE=1 export NCCL_IB_DISABLE=1 export NCCL_SOCKET_IFNAME=eth0 # 换成你的网卡名NCCL_P2P_DISABLE=1禁用 P2P,强制走共享内存或网络通信。NCCL_IB_DISABLE=1禁用 InfiniBand(家用环境一般没有)。NCCL_SOCKET_IFNAME指定通信用的网卡,避免 NCCL 选错网卡。
设置这些之后,通信正常了,但性能会有一定损失,因为走 PCIe 比 NVLink 慢。实测下来,TP=2 的推理速度比单卡(如果能跑的话)大概慢 20-30%,但换来的是能跑更大的模型,这个 trade-off 是值得的。
注意:如果你用的是服务器主板,PCIe 拓扑比较好,可能不需要禁用 P2P。建议先不禁用试一下,如果卡死再禁用。判断方法是看启动日志里 NCCL 的初始化信息,如果卡在 "NCCL INFO" 相关的行不动了,基本就是 P2P 问题。
4.4 验证部署是否成功
部署跑起来之后,怎么确认它是真的能用,而不是"看起来能用"?我用几个方法验证。
第一,发一个简单的请求,看能不能正常返回:
curl http://localhost:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "./qwen3.6-27b-fp8", "prompt": "你好,请介绍一下你自己。", "max_tokens": 100 }'如果返回了合理的文本,说明基本通了。
第二,测长上下文。发一个几千字的 prompt,看能不能正常处理,不报错、不截断。这一步能验证 KV Cache 的配置是否合理。
第三,测并发。用工具同时发多个请求,看响应时间和成功率。我用的是简单的 Python 脚本并发发请求,观察有没有超时或失败。
第四,测稳定性。让它连续跑几个小时,中间不断发请求,看显存有没有泄漏、响应时间有没有劣化。我跑了一晚上,第二天早上看还是稳定的,这才算真正跑通。
5. 常见问题与排查技巧实录
5.1 启动阶段的高频报错与解决
启动阶段是报错最集中的地方,我把遇到的和社区里常见的整理成表,方便你对照排查。
| 报错信息关键词 | 可能原因 | 解决方法 |
|---|---|---|
| CUDA out of memory | 显存不够 | 降低gpu-memory-utilization或max-model-len |
| NCCL error / timeout | 多卡通信问题 | 设置NCCL_P2P_DISABLE=1 |
| dtype not supported | 精度格式不匹配 | 确认权重是 FP8,--dtype float8 |
| model class not found | 模型架构不支持 | 升级 vLLM 到最新版 |
| CUDA driver version insufficient | 驱动太老 | 升级驱动到 550 以上 |
| failed to compile | 缺 CUDA Toolkit | 安装完整 CUDA Toolkit |
这里重点说两个。一个是CUDA out of memory,这个最常见,但原因可能不止一种。除了显存真的不够,还可能是显存碎片化——vLLM 反复加载卸载模型导致显存碎片,这时候重启进程能解决。另一个是model class not found,这个通常是因为 vLLM 版本太老,不认识 Qwen3.6 的架构,升级 vLLM 就好。
5.2 推理阶段的性能问题排查
跑起来之后,性能问题就来了。常见的有:响应慢、吞吐低、并发上不去。
响应慢的第一个排查点是看 GPU 利用率。用nvidia-smi -l 1实时监控,如果 GPU 利用率很低(比如 20% 以下),说明瓶颈不在计算,而在通信或数据加载。多卡场景下,通信瓶颈很常见,尤其是走 PCIe 的时候。
吞吐低的排查点是看 batch size。vLLM 会自动做连续批处理(continuous batching),但如果max-num-seqs设得太小,batch 上不去,吞吐就低。可以适当调大这个值,但要配合显存余量。
并发上不去的排查点是看 KV Cache 的占用。并发数增加,KV Cache 线性增长,显存不够就会拒绝新请求。这时候要么降低max-model-len,要么减少并发,要么换更大显存的卡。
我实测下来,两张 4090 跑 FP8 的 27B 模型,在 8192 上下文、并发 8 的情况下,单请求响应速度大概每秒 20-30 个 token,这个速度做对话是够用的。如果追求更高吞吐,可以牺牲一些上下文长度,把并发开到 16。
5.3 那些文档里不会写的坑
有几个坑是我自己踩出来的,文档里基本不会提,但很关键。
第一个是显存碎片。vLLM 长时间运行后,如果反复加载卸载模型,显存会产生碎片,表现为"明明显存够但就是 OOM"。解决办法是定期重启服务,或者用--enforce-eager参数禁用 CUDA Graph(会损失一些性能但减少碎片)。
第二个是温度墙。4090 的功耗高,两张卡挤在一起,散热不好的话会撞温度墙降频。我一开始机箱风道没弄好,跑一会儿就降频,速度掉一半。后来加了机箱风扇,调整了风道,温度压到 75 度以下,性能就稳了。这个坑很隐蔽,因为nvidia-smi显示的利用率还是满的,但实际频率降了。
第三个是 PCIe 带宽。两张 4090 如果插在 PCIe 4.0 x8 的插槽上(有些主板为了插更多卡会拆分通道),带宽减半,多卡通信性能会明显下降。尽量插在 x16 的插槽上,如果主板支持,查一下手册确认插槽的通道分配。
第四个是电源。两张 4090 满载功耗加起来接近 900W,加上 CPU 和其他部件,整机功耗可能到 1200W 以上。电源功率不够会导致高负载下重启或降频。我用的 1300W 电源,实测够用,但如果你电源是 1000W 以下,建议升级。
5.4 常见问题速查表
把上面这些整理成一个速查表,遇到问题先查这个。
| 现象 | 排查方向 | 快速验证方法 |
|---|---|---|
| 启动就 OOM | 显存参数 | 降gpu-memory-utilization到 0.85 |
| 推理卡死 | 多卡通信 | 设NCCL_P2P_DISABLE=1 |
| 速度突然变慢 | 温度/降频 | nvidia-smi看频率和温度 |
| 并发上不去 | KV Cache | 降max-model-len |
| 长时间运行后 OOM | 显存碎片 | 重启服务 |
| 返回乱码 | 精度问题 | 确认 dtype 和权重匹配 |
6. 一些实操心得和后续可扩展的方向
折腾完这一套,我最大的体会是:消费级硬件跑大模型,硬件本身的性能不是瓶颈,配置和调优才是。两张 4090 的算力足够跑 27B 模型,但如果你不懂显存怎么算、多卡怎么通信、参数怎么调,就会像我一样卡在各种奇怪的问题上。
关于后续扩展,有几个方向可以玩。一是把上下文长度往上推,如果你有长文档处理的需求,可以试试 16384 甚至 32768,但要接受并发数下降的代价。二是试试 SGLang,它在某些场景下性能比 vLLM 更好,尤其是前缀缓存和结构化输出,值得对比测试。三是如果预算允许,上四卡 4090,用 TP=4 跑更大的模型,或者用 TP=2 加 PP=2 跑 70B 级别的模型,那是另一个量级的体验。
最后分享一个小技巧:把启动命令写成一个 shell 脚本,把环境变量和参数都固化进去,这样每次重启不用重新敲一遍,也避免记错参数。我现在的脚本里包含了 NCCL 配置、显存参数、端口设置,一键启动,省心很多。另外,建议把 vLLM 的日志重定向到文件,出问题的时候翻日志比看终端输出方便得多。
这套配置我连续跑了几天,稳定性没问题,日常对话、代码辅助、文档总结这些任务都能胜任。如果你也在折腾类似的配置,希望这篇记录能帮你少走点弯路。