新闻详情

新闻详情

首页 / 资讯中心 / 详情

双RTX 3090跑Qwen2.5-14B:vLLM张量并行实现低成本本地大模型部署

发布时间:2026/10/2 13:07:06来源:尧图网络
双RTX 3090跑Qwen2.5-14B:vLLM张量并行实现低成本本地大模型部署
说实话双RTX 3090跑Qwen2.5-14B这件事我是被预算逼出来的。手头有一张闲置的3090想升级大模型服务又不想花几万块上A100于是琢磨着再补一张3090用vLLM的张量并行把模型切成两半拼出一个“穷人版”的48GB显存推理服务器。跑通之后我挺意外的这套组合的稳定性和吞吐完全能支撑小团队的生产级使用而且总成本不到一张A100的零头。这篇文章我会把整个部署过程、我算过的显存账、踩过的坑全部摊开讲适合想用消费级显卡搭本地大模型服务、又不满足于Ollama这种玩具级方案的人。1. 为什么拿两张3090跑Qwen2.5-14B先算清显存账1.1 单卡24GB为什么连模型都装不下先看硬数据。Qwen2.5-14B-Instruct的参数量约147亿BF16精度下每个参数占2字节光权重文件就接近29GB。一张3090只有24GB显存这意味着不做量化的话模型权重本身就已经超出单卡容量更别提推理时还有KV Cache、激活值和CUDA context这些额外开销。有人会问量化到INT4不行吗行4bit权重大概能压到8GB左右单卡确实能跑。但我在代码生成、数学推理这类任务上实测过量化后的输出质量下降是能感知到的尤其在需要精确计算和多步推理的场景里一个符号错就能让整个结果崩掉。如果目标是做一个能真正交付给业务方使用的大模型服务保留BF16精度的意义远大于省那点显存。所以我的结论很简单不量化上双卡。1.2 双3090的性价比到底有多离谱价格上以我2025年初看到的市场行情一张二手RTX 3090大概六七千块两张合计不到一万五。对比A100 80G动辄小十万的价格或者租云GPU一个月几大千的费用双3090的性价比是碾压级的。而且我本身就是做本地部署的这种方案等于一次性买断算力长期跑服务根本不心疼。当然3090毕竟是消费级显卡没有NVLink Switch这种数据中心互联方案功耗还高单卡瞬时功耗能上350W和A100的HBM带宽、NVSwitch不是一个量级。但关键是这些差距在14B这个参数规模的模型上并没有想象中那么致命。后续第四节我会详细说通信瓶颈的问题这里先给结论双3090跑Qwen2.5-14B完全够用。2. 环境准备版本组合选对了后面少踩一半坑2.1 硬件清单和驱动检查动手之前先确认三件事。第一主板上要有两个PCIe x16插槽至少x8x8插入两张3090时要注意间隔否则散热会互相打架。第二电源额定功率建议1200W以上3090瞬时功耗高两张卡同时撞功耗墙时弱电源会直接关机。第三系统内存至少64GBvLLM加载模型时需要先把权重从磁盘读入内存28GB权重加上运行时开销32GB内存会非常紧张。驱动检查简单直接nvidia-smi看右上角CUDA Version是否在12.1以上两张卡的显存是否都识别为24GB。驱动版本在535以上基本都能满足vLLM的要求。有个细节容易被忽略主板的BIOS里要开启Resizable BAR和Above 4G Decoding这两项不打开NCCL在双卡通信时可能遇到P2P映射问题后面避坑清单会详细说。2.2 用虚拟环境安装vLLM版本锁定很重要vLLM的依赖环境比较敏感我强烈建议用conda单独建环境conda create -n vllm python3.10 -y conda activate vllm pip install vllm0.7.3为什么锁0.7.3而不是直接装最新版因为vLLM迭代太快0.8.x系列虽然新增了不少特性但对Ampere架构3090的sm_86支持反而没有0.7.x稳定。我在群里看到不少用40系和50系显卡的朋友追新版本没问题但3090用户普遍反映0.8.x在启动时会有CUDA编译相关警告甚至某些算子自动降级。Ampere架构的用户稳定压倒一切。装完后验证一下python -c import vllm; print(vllm.__version__)2.3 用ModelScope快速拉取模型权重模型下载是很多人忽略的坑。直接到Hugging Face拉模型在国内网络环境下经常断流文件不完整会让vLLM加载失败且报错信息不直观。我这边用的方案是通过ModelScope魔搭社区下载Qwen官方在那边有同步速度快很多。新版ModelScope提供命令行工具pip install modelscope modelscope download --model Qwen/Qwen2.5-14B-Instruct --local_dir /data/models/Qwen2.5-14B-Instruct习惯用Python的话也可以from modelscope import snapshot_download model_dir snapshot_download(Qwen/Qwen2.5-14B-Instruct, cache_dir/data/models) print(model_dir)下载完检查目录里是否包含config.json、model-00001-of-00004.safetensors分片文件、tokenizer.json、tokenizer_config.json。文件不全会导致加载失败而且vLLM的报错不一定直接告诉你缺文件所以我习惯先ls -lh看看文件大小是否符合预期。3. vLLM启动参数逐项拆解为什么不能无脑抄3.1 一行命令启动服务环境就绪后启动服务其实就一行命令。这是我最终定型的启动脚本cd /data/models vllm serve /data/models/Qwen2.5-14B-Instruct \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.92 \ --max-model-len 8192 \ --max-num-seqs 8 \ --served-model-name qwen14b \ --port 8000看到日志输出Application startup complete和Uvicorn running on http://0.0.0.0:8000服务就起来了。3.2 每个参数背后的显存逻辑--tensor-parallel-size 2是这个方案的核心。它告诉vLLM将模型按层内张量切成两块分别加载到两张GPU上。这个参数不能超过GPU数量且两张卡的显存最好一致否则小卡会成为瓶颈。--gpu-memory-utilization 0.92表示每张卡最多使用92%的显存。为什么不是1.0因为vLLM还需要给CUDA context、cuDNN、显存碎片留一点余量拉满容易在运行中途触发OOM。我实测0.92是稳定性和显存利用率的平衡点。如果业务并发很高建议降到0.88左右给KV Cache留出更多余量。--max-model-len 8192控制最大上下文长度。Qwen2.5-14B-Instruct本身支持更长的上下文但长上下文的代价是KV Cache占用成倍上涨。24GB显存下8K是“安全且实用”的长度。硬开到32K也不是不行但剩余显存根本跑不了几个并发请求生成速度还会暴跌。--max-num-seqs 8限制同时处理的请求数。vLLM采用连续批处理架构能同时处理多个请求但如果这个值设得太大KV Cache总容量不够用极端情况下依然会OOM。8对14B模型是个偏保守的起点日常够用。3.3 通过OpenAI兼容接口验证服务服务起来后最快验证方式是用curl发一个对话请求curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen14b, messages: [{role: user, content: 用一句话解释什么是张量并行}], max_tokens: 128, temperature: 0.7 }如果你要在代码里集成推荐用OpenAI SDKfrom openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, api_keyEMPTY ) resp client.chat.completions.create( modelqwen14b, messages[{role: user, content: 你好介绍一下你自己}], max_tokens256 ) print(resp.choices[0].message.content)这里最容易踩的坑是model字段。如果你设置了--served-model-name qwen14bAPI请求里的model必须填qwen14b没设置的话就要填完整的模型路径/data/models/Qwen2.5-14B-Instruct。我第一次对接时填错了返回404排查了好久才发现是model字段和启动参数不一致。4. 张量并行内部原理与显存账本双卡不是简单拼显卡4.1 张量并行到底怎么切模型很多人的第一反应是“把模型前一半放卡1后一半放卡2”这个理解是错的。张量并行切的是每一层内部的矩阵。以Transformer里的线性层为例权重矩阵是[out_features, in_features]张量并行会把这个矩阵按列切成两块每张卡各持一半。前向计算时每张卡用自己手头半边权重做矩阵乘法得到部分结果然后通过一次AllReduce通信把所有卡的部分结果相加拼成完整输出。这意味着每一层Transformer都需要一次卡间通信而不是只在层与层之间通信。这也是为什么双卡方案的通信带宽如此重要——它直接决定张量并行跑得快不快。你买的每一张卡都在“边算边聊天”聊天的效率某种程度上比算力本身还关键。4.2 怎么检查有没有NVLink桥RTX 3090是有NVLink接口的但很多非公版把金手指省了。判断方法nvidia-smi nvlink -s如果能看到两张卡的link ID说明NVLink桥接生效如果提示不支持或者输出为空说明两张卡之间没有NVLink连接NCCL会自动走PCIe P2P通信。没有NVLinkTP也能正常工作。vLLM底层用NCCL通信库PCIe 4.0 x16单向带宽约32GB/s虽然比NVLink低但对付14B模型短上下文场景足够。我实测PCIe下的TP性能损失大约在10%~20%取决于prefill和decode的比例。如果主板第二条插槽只有x8带宽那损失会进一步扩大有条件还是把两张卡放在能跑满x16带宽的槽位上。还有个经典报错启动时NCCL提示Peer mapping not supported或者The legacy P2P API failed to initialize。遇到这个先别急着改环境变量去BIOS确认Resizable BAR和Above 4G Decoding是否开启。我最初安装环境时被这个报错折腾了很久打开这两项后问题直接消失。如果BIOS确实没有这两个选项才考虑用环境变量强制降级export NCCL_P2P_DISABLE1这会放弃GPU间的直接P2P映射改用共享内存中转虽然增加了一次CPU拷贝但至少服务能跑起来。4.3 KV Cache显存账本算清楚才知道为什么会OOM跑在vLLM上的大模型显存大头除了模型权重就是KV Cache。KV Cache用来缓存历史token的key和value向量每次新增token都要把整条序列的KV向量重新读一遍。Qwen2.5-14B有48层、8个KV头、每个头维度128BF16精度下每个token每层需要2×8×128×24KB48层合计约192KB。这还只是一条请求的开销。我们做个简单的乘法8个并发、每个请求上下文2000 tokenKV Cache总占用约 8 × 2000 × 192KB ≈ 3.1GB如果把上下文拉到8192、并发还是8总占用直接变 8 × 8192 × 192KB ≈ 12.6GB张量并行双卡分摊后每卡约6.3GB再加上每卡15GB的权重已经是21.3GB逼近22GB的可用上限。所以显存优化本质是在“权重 KV Cache 激活值”三个变量之间找平衡。权重是固定的激活值比较小真正弹性最大的是KV Cache它取决于同时处理的序列数和上下文长度。这就是为什么我把--max-model-len和--max-num-seqs都控得比较保守。vLLM启动日志里会打印类似Maximum concurrency for 8192 tokens per request: 5.7x的信息意思是这个配置下最多只能容纳5.7个满8K上下文的请求。你要是把--max-num-seqs设成8那极端情况下OOM几乎是必然的。5. 实测性能双3090在14B模型上到底能跑多快5.1 我的测试方法与观测工具部署完我跑了大概一周的线上流量另外用脚本做了几组基准测试。测试环境是双RTX 3090PCIe 4.0 x16无NVLink桥、vLLM 0.7.3、BF16权重、max-model-len 8192、gpu-memory-utilization 0.92。测试方法是用OpenAI SDK写一个多线程脚本按不同并发和输入输出长度统计首token延迟TTFT和生成速度。同时用nvidia-smi dmon监控两张卡的实时显存和SM利用率这个工具比watch nvidia-smi更好用能同时看多张卡的动态数据。5.2 基准数据并发输入长度输出长度平均TTFT平均生成速度总吞吐15122560.8s52 tokens/s52 tokens/s45122561.3s48 tokens/s192 tokens/s85122562.1s41 tokens/s328 tokens/s420485122.8s39 tokens/s156 tokens/s这些数据是我这台机器上的实测值不同主板、不同PCIe配置会有浮动但量级可以参考。对比单卡跑量化版14B模型大概35~45 tokens/s双卡BF16的性能并不差精度还完整保留了。5.3 从数据反推参数调优思路从表里能读出两个规律。第一并发从1提升到4总吞吐接近线性增长从4提升到8吞吐增长开始放缓说明调度开销和KV Cache竞争开始显现。第二输入变长之后TTFT明显上升生成速度也轻微下降因为prefill阶段的计算量增加同时KV Cache占用变多导致可用batch变小。所以参数怎么调取决于你的业务场景。如果是实时对话对延迟敏感输出也短并发设4比较合适TTFT能控制在1.5秒以内体感很流畅。如果是离线批量处理对延迟不敏感、追求总吞吐可以尝试--max-model-len 4096配--max-num-seqs 16的组合。我的实测里这个配置总吞吐能到500 tokens/s以上比保守配置高一截。6. 避坑清单我摸爬滚打总结的八个关键问题6.1 踩坑gpu-memory-utilization拉到0.95服务一跑就OOM第一次我图省事抄别人的命令直接--gpu-memory-utilization 0.95。模型加载时日志一切正常一有请求进来就报CUDA out of memory。原因是CUDA graph捕获阶段会额外占用显存再加上激活值和临时缓冲区0.95把最后一点安全余量挤没了。改成0.92之后问题消失。建议新手从0.90起步观察一整天运行情况再加别一上来就拉满。6.2 踩坑max-model-len和max-num-seqs是绑定的我看到不少人把--max-model-len设成32768心想支持长文本不是更好吗结果KV Cache直接爆掉。长上下文和并发数必须一起考虑。你想开32K上下文--max-num-seqs就不能超过2否则显存铁定不够。我的经验做法是把长文本和短文本拆成两个独立服务实例分别给不同配置而不是试图在一个实例里满足所有场景。6.3 踩坑Docker部署忘了加--shm-size用容器跑vLLM时Docker默认的/dev/shm只有64MB而NCCL通信和DataLoader都要用共享内存vLLM启动后一加载权重就会报No space left on device。解决方法是加参数docker run --gpus all --shm-size10g ...我第一次用docker compose部署时没配shm_size被这个报错卡了半小时把系统内存都检查了一遍最后才发现是共享内存配额问题。6.4 踩坑升级vLLM后命令变了老脚本直接废掉vLLM更新速度极快0.6.x的python -m vllm.entrypoints.openai.api_server在0.7.x里换成了vllm serve部分参数名也有调整。网上大量教程还停留在旧版本照着抄容易出问题。我的建议是安装时就固定版本号比如vllm0.7.3升级前先看release note别盲目pip install -U vllm。特别是3090这样的Ampere卡追最新版本不一定有收益反而可能遇到算子兼容问题。6.5 踩坑系统内存不足模型加载到一半被killvLLM加载模型时会先把权重读进内存再搬到GPU。一台只有16GB内存的服务器加载29GB的BF16权重时大概率触发OOM killer日志里看到Killed字样就晚了。模型文件如果还在磁盘page cache里内存占用会超过29GB。我给的建议是系统内存至少是模型权重的两倍64GB起步。别在这上面省钱内存便宜排查OOM的时间成本可一点都不便宜。6.6 踩坑接口报404或400多半是model字段填错这个问题在API对接阶段特别常见。用--served-model-name qwen14b启动后OpenAI SDK的model字段必须填qwen14b填本地路径会返回404。没设置这个参数的话就填完整模型路径/data/models/Qwen2.5-14B-Instruct。另外请求里的max_tokens不能超过服务端的--max-model-len否则直接400。之前有同事调了一天接口没通最后发现是两个字段都对不上这破事真容易耽误时间。6.7 踩坑3090别盲目追新FlashAttention实现vLLM默认会用FlashAttention加速这对40系很友好但3090是Ampere架构部分新版FlashAttention实现里针对Ampere的优化并不理想。我遇到过升级vLLM后日志里出现flash_attn初始化异常服务虽然能起但prefill速度明显变慢。后来发现是vLLM对sm_86的kernel没有编译完整退回0.7.3恢复正常。如果你想手动指定可以通过设置VLLM_ATTENTION_BACKEND环境变量来切换后端但这属于高级操作普通用户不推荐折腾。6.8 踩坑没有守护进程服务挂了不能自愈这事儿不算vLLM专属但本地部署很容易忽略。vLLM偶尔会因为显存压力或者底层NCCL连接问题崩溃没有守护进程的话服务就悄悄下线了。我用systemd写了一个简单的unit file加上Restartalways再配合Health Check接口做探活。vLLM自带/health端点用curl http://localhost:8000/health就能判断服务是否存活配合定时任务或者监控系统非常方便。最后说点个人体会。这套双3090加vLLM加Qwen2.5-14B的组合我在公司内部已经跑了两个多月承载了知识库问答、代码片段生成、会议纪要整理三个业务稳定性比我预想的好很多。买不起A100不是做不了事的借口至少14B这个档位双3090是真的能顶上。再分享一个实用小技巧日常调试阶段把--max-model-len设成4096能在相同显存下开更高并发明显缩短测试排队时间真正上生产时再拉回8192。如果你手头正好有两张闲置的3090或者正打算低成本搭一个本地大模型服务按这套流程走一遍应该比我当初顺利得多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

「Python 翻车日记 · 第 16 篇」伦敦粉丝凌晨 3 点收到推送?——naive 时间不知道自己在哪个时区 2026/10/2 13:54:14

「Python 翻车日记 · 第 16 篇」伦敦粉丝凌晨 3 点收到推送?——naive 时间不知道自己在哪个时区

Python 翻车日记 第 16 篇:伦敦粉丝凌晨 3 点收到推送?——naive 时间不知道自己在哪个时区 📋 本期菜单:6 个日期与时间的坑,从「datetime.now() 不含时区」到「闰秒不兼容」 [入门] now() 无时区 timedelta 无年/月 [进阶] timestamp() 本地假设 strftime 平台差异 …

阅读更多 →
双极步进电机驱动方案:DRV8818与PIC24硬件设计及固件实现 2026/10/2 13:54:07

双极步进电机驱动方案:DRV8818与PIC24硬件设计及固件实现

1. 方案拆解:为什么是双极步进电机 驱动芯片架构1.1 双极步进电机与单极步进电机的区别先聊清楚一个基础问题:机器人关节、传输带、旋转工作台上大量使用的步进电机,主要分单极和双极两大类。市面上常见的四线、六线、八线电机,如…

阅读更多 →
@Autowired和@Resource的区别 2026/10/2 13:54:01

@Autowired和@Resource的区别

Autowired 和 Resource 都是用来做依赖注入的注解,但它们的来源、装配规则、支持范围和使用场景有明显区别。很多面试会直接问“两者有什么区别”,回答时最好从来源、匹配方式、支持位置、属性、处理类几个维度展开。 一、来源不同注解来源全限定名Autow…

阅读更多 →
远程控制工具推荐 远程控制软件哪个好 2026/10/2 13:54:01

远程控制工具推荐 远程控制软件哪个好

远程控制工具是日常异地办公、设备运维和跨设备协作的常用工具,不少同类产品功能受限、收费繁杂,很难满足长期稳定使用需求。远程控制工具想要兼顾性价比、使用体验与数据安全,适配个人和小型团队的各类场景,无界趣连2.0是综合表现…

阅读更多 →
基于Java+SSM+Flask的城投企业人事管理系统设计实践 2026/10/2 13:54:01

基于Java+SSM+Flask的城投企业人事管理系统设计实践

最近帮人把这套"基于JavaSSMFlask的城投公司企业人事管理系统"完整整了一遍,从需求梳理、库表设计到主工程整合、辅助服务联调,最后连调试文档和演示流程都一起补全了。刚开始接触这个需求的时候,我心里其实有点疑问:都…

阅读更多 →
Trae 智能协作 AI IDE 实战:用 TaoToken 统一 Key 打通多模型协作流 2026/10/2 13:53:53

Trae 智能协作 AI IDE 实战:用 TaoToken 统一 Key 打通多模型协作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉