两张300I Duo部署Qwen3.5-32B:多卡推理与显存优化实战
1. 为什么两张 300I Duo 跑 32B 模型是个值得认真对待的方案把两张 300I Duo 凑在一起跑 Qwen3.5-32B这个组合乍一看有点非主流——毕竟现在大家聊本地部署张口就是消费级显卡或者整机方案。但如果你手头正好有这两张卡或者正在为团队选一套性价比可控的推理硬件这个配置其实相当能打。我先说结论两张 300I Duo 的显存叠加后跑 32B 级别的模型在显存容量上是够用的真正的挑战不在能不能装下而在怎么把两张卡用起来、怎么把吞吐和延迟调到可接受的范围。300I Duo 这类加速卡的特点是单卡显存不小但单卡算力相对克制所以它的定位天然适合显存优先的场景——也就是模型权重占大头、并发不算特别夸张的推理任务。Qwen3.5-32B 这个体量的模型如果按 FP16 存权重光权重就要 60GB 以上单张卡基本没戏即便上 INT8 量化也要 30GB 出头。两张卡叠加之后显存池子一下子宽裕了这才让 32B 模型有了落地的可能。这篇文章面向的是这样几类人手里有 300I Duo 但不清楚怎么组多卡推理的想跑 32B 级别模型但预算有限、不想上高端整机的以及已经尝试过单卡部署、被显存卡住想扩展的。我会把整个流程拆成硬件与显存账怎么算环境怎么搭模型怎么切分到两张卡推理服务怎么起性能怎么调这几块每一块都给出我实际踩过的坑和验证过的做法。需要提前说明的是多卡推理这件事软件栈的匹配度比硬件本身更决定成败。同样的两张卡用不同的推理框架、不同的并行策略出来的效果可能差一倍。所以下面我会重点讲清楚为什么这么选而不是只丢一堆命令。2. 先把显存账算清楚32B 模型到底吃多少2.1 权重的显存占用不是简单按参数量乘位宽很多人算显存就一句话32B 参数FP16 就是 64GB。这个算法方向没错但太粗糙实际部署时会发现对不上。原因在于几个容易被忽略的部分。第一参数量本身有水分。Qwen3.5-32B 的32B是总参数量但其中可能包含嵌入层、输出层这些占用较大但计算模式不同的部分。真正决定显存的是所有权重张量的总和通常和标称参数量接近但不会完全等于 32×10⁹。第二量化方式直接改变量级。FP16 每参数 2 字节INT8 每参数 1 字节INT4 每参数 0.5 字节。32B 模型在 INT8 下大约 32GBINT4 下大约 16GB。这是决定你能不能塞进两张卡的关键变量。第三KV Cache 是隐形大户。这部分和模型权重无关只和你的并发数、上下文长度挂钩。公式大致是KV Cache 显存 ≈ 2 × 层数 × 注意力头数 × 头维度 × 序列长度 × 并发数 × 数据类型字节数对于 32B 级别的模型如果上下文开到 8K、并发开到 8KV Cache 轻松吃掉十几 GB。很多人部署完发现权重明明装下了一跑就 OOM八成是 KV Cache 没算进去。2.2 两张 300I Duo 的显存池怎么分配假设单张 300I Duo 的可用显存是 X GB两张就是 2X。但这里有个关键认知多卡的显存不是自动合并成一个池子的。你要么用张量并行把模型切开分到两张卡要么用流水线并行按层切分要么干脆一张卡放权重、另一张卡放 KV Cache这种比较少见。不同的切分方式显存利用率和通信开销完全不同。我一般建议先做一张表把预算列清楚项目FP16INT8INT4模型权重~64GB~32GB~16GBKV Cache8K上下文并发4~8-12GB~8-12GB~8-12GB框架运行时开销~2-4GB~2-4GB~2-4GB合计~74-80GB~42-48GB~26-32GB从这张表能直接看出FP16 基本要三张卡才稳INT8 是两张卡的主流选择INT4 则留出了较大余量。所以如果你只有两张 300I DuoINT8 量化是最现实的起点INT4 可以作为追求更高并发时的备选。提示量化不是免费的午餐。INT8 通常精度损失很小INT4 在部分任务上会有可感知的下降。建议先用 INT8 跑通再根据实际效果决定要不要降到 INT4。2.3 一个容易翻车的点显存碎片即便账面上显存够实际跑起来也可能因为显存碎片而分配失败。尤其是长时间运行、反复加载卸载模型的服务碎片会越来越严重。我的做法是服务启动时一次性把显存池预留好避免运行中动态申请大块显存。很多推理框架有类似gpu_memory_utilization或memory_fraction的参数把它设成 0.9 左右留一点余量给碎片和临时张量比设成 1.0 更稳。3. 环境搭建驱动、工具链和框架的匹配顺序3.1 先确认驱动和固件版本别急着装框架多卡部署翻车十次里有三次是驱动和固件版本不匹配。300I Duo 这类加速卡对驱动版本比较敏感两张卡如果固件版本不一致可能出现其中一张识别不到、或者通信带宽跑不满的情况。我的标准流程是先用厂商提供的设备管理工具查看两张卡的型号、固件版本、驱动版本确认完全一致。如果不一致先统一固件再统一驱动顺序不能反。确认系统能同时看到两张卡并且能读到各自的显存容量。这一步看起来啰嗦但能省掉后面一大堆玄学问题。我见过有人卡在模型加载到第二张卡就报错折腾两天最后发现是两张卡固件差了一个小版本。3.2 推理框架的选择逻辑跑 32B 模型的多卡推理主流选择有几类通用推理框架、厂商自带推理套件、以及通用深度学习框架自己写并行。我的建议是优先用支持张量并行的成熟推理框架原因很简单32B 模型单卡放不下必须切分而手写张量并行的通信逻辑非常容易出错成熟框架已经把 all-reduce、all-gather 这些通信原语调优过了。选框架时重点看三个能力是否支持张量并行TP这是把单层权重切到多卡的核心机制。是否支持量化加载INT8/INT4 直接决定显存够不够。是否支持连续批处理continuous batching决定并发吞吐。如果框架只支持数据并行DP那对单模型放不下这个场景是没用的——数据并行是每张卡放一份完整模型显存需求反而翻倍。3.3 依赖安装的坑别让版本冲突毁掉一切安装推理框架时最容易出问题的是底层计算库和框架版本不匹配。我的经验是先装框架官方推荐的底层库版本不要盲目升级到最新。用虚拟环境隔离避免和系统里已有的其他深度学习环境打架。装完后立刻跑一个最小推理测试确认两张卡都能被框架识别。一个具体的检查动作加载模型前先让框架打印它识别到的设备列表和每张卡的可用显存。如果只识别到一张或者显存数字明显不对先别往下走回头查驱动。4. 把 32B 模型切到两张卡并行策略怎么定4.1 张量并行是首选但要理解它的代价张量并行的思路是把每一层的权重矩阵按维度切开两张卡各算一半然后通过通信把结果拼起来。它的好处是显存和计算都分摊了单卡压力小代价是每层都要通信对卡间带宽要求高。对于 32B 这种层数多、每层矩阵大的模型张量并行度设为 2也就是两张卡是比较自然的。切分维度通常选隐藏维度因为这样每张卡拿到的计算量均衡。这里有个实操细节张量并行度最好能整除模型的注意力头数。比如模型有 40 个注意力头TP2 就是每卡 20 个头很干净如果模型有 33 个头TP2 就会有一张卡多一个头负载不均。部署前查一下模型的头数配置能避免不必要的性能损失。4.2 流水线并行作为备选什么时候用流水线并行是按层切分前几层放第一张卡后几层放第二张卡。它的优点是通信量小只在层边界通信缺点是同一时刻只有一张卡在干活除非你同时跑多个微批次来填满流水线。对于两张卡的场景如果卡间带宽不理想流水线并行反而可能比张量并行更稳。但它的显存分摊不如张量并行均匀——如果模型前半部分层参数多、后半部分少就会出现一张卡吃紧一张卡空闲。所以我的建议是优先试张量并行如果通信成为瓶颈再考虑流水线并行。4.3 切分后的显存分布验证模型加载完之后一定要做一次显存分布检查。理想情况下两张卡的显存占用应该接近如果差得很多说明切分策略有问题。我通常会在加载后打印每张卡的显存使用量然后跑一次前向推理再打印一次。两次对比能看出 KV Cache 和临时激活占了多少。如果某张卡在推理时显存飙升可能是那一侧承担了额外的通信缓冲或输出层计算。注意输出层lm_head和嵌入层embedding在很多框架里默认不切分会完整放在某一张卡上。对于 32B 模型这两层加起来可能有好几个 GB足以让那张卡成为瓶颈。如果框架支持把这两层也纳入切分范围。5. 起服务从能跑到跑得好的关键参数5.1 上下文长度和并发数的权衡这是部署里最需要动脑子的地方。上下文长度和并发数都吃 KV Cache而 KV Cache 是显存里最灵活也最容易失控的部分。我的调参思路是先定业务需求再倒推参数如果业务是单轮问答上下文 2K-4K 足够可以把并发开高。如果是长文档处理上下文要 8K 甚至 16K那并发就得压下来。如果两者都要考虑用分页注意力paged attention这类机制把 KV Cache 按块管理减少碎片。一个实测经验把最大上下文设成业务实际需要的 1.2 倍左右不要盲目开大。很多人习惯性设成 32K结果显存被 KV Cache 吃掉一大半并发上不去吞吐反而低。5.2 批处理策略对吞吐的影响连续批处理continuous batching是提升吞吐的关键。它的核心思想是不等一个批次里所有请求都结束只要有请求完成就立刻塞新请求进来让计算单元始终饱和。开启连续批处理后吞吐通常能提升数倍。但它对显存管理要求更高因为同时活跃的序列数变多了。所以连续批处理要和 KV Cache 上限配合调先设一个保守的并发上限观察显存再逐步往上加。5.3 一个完整的启动参数示例下面是一个基于常见推理框架的启动配置思路具体参数名因框架而异这里给的是逻辑结构# 伪代码示意参数名需按实际框架调整 python -m infer_server \ --model /path/to/qwen3.5-32b \ --tensor-parallel-size 2 \ --dtype int8 \ --max-model-len 8192 \ --max-num-seqs 8 \ --gpu-memory-utilization 0.9 \ --enable-continuous-batching \ --port 8000几个参数的解释--tensor-parallel-size 2两张卡做张量并行。--dtype int8权重量化到 INT8显存减半。--max-model-len 8192最大上下文 8K按业务定。--max-num-seqs 8最大同时处理的序列数控制 KV Cache。--gpu-memory-utilization 0.9预留 10% 显存给碎片和临时张量。启动后不要急着压测先发几个请求确认输出正常再看显存占用是否稳定。6. 性能调优与常见故障的排查链路6.1 吞吐上不去先看是不是卡在通信两张卡做张量并行如果卡间通信带宽不够每层都要等通信吞吐会被拖死。判断方法很简单对比单卡跑小模型和双卡跑大模型的每秒处理 token 数如果双卡的效率远低于线性预期通信很可能是瓶颈。缓解手段有几个确认卡间连接方式是否用了高带宽通道如果框架支持调整通信和计算的重叠策略让通信在计算的同时进行实在不行退回到流水线并行牺牲一点显存均衡换通信量下降。6.2 OOM 的三种典型场景和对策OOM 是部署里最常见的报错但原因各不相同场景表现对策加载时 OOM模型还没跑起来就报错降量化精度或检查是否有层没被切分推理时 OOM跑几个请求后报错降并发数或上下文长度检查 KV Cache 上限长时间运行后 OOM跑一段时间才报错显存碎片重启服务或启用分页显存管理我遇到最多的是第二种。很多人把并发设得很高前几个请求没事一上量就崩。解决办法是把max-num-seqs调低观察稳定后再慢慢加。6.3 输出质量异常先怀疑量化再怀疑并行如果模型能跑但输出质量明显不对——比如重复、乱码、答非所问——排查顺序是先换回 FP16 或更高精度跑同样的输入。如果质量恢复说明是量化损失考虑换量化方案或提高精度。如果高精度也有问题检查并行切分是否正确。切分错误会导致某些层的权重对不上输出必然乱。最后检查输入格式。对话模板、特殊 token 的处理在不同框架里可能不一样格式错了模型也会答非所问。6.4 监控别等出问题才看指标服务跑起来之后至少要盯这几个指标每张卡的显存占用、卡间通信带宽、每秒处理 token 数、请求排队长度。显存占用持续上涨通常意味着 KV Cache 没释放干净排队长度持续大于零说明并发不够或计算太慢。有条件的话把这些指标接到监控系统里设个阈值告警。我吃过亏——服务半夜 OOM 挂了第二天才发现中间几个小时的请求全丢了。7. 我在两张 300I Duo 上跑 32B 模型的几点实际体会先说量化选择。我一开始图省事直接上 INT4显存确实宽裕但在一部分需要精确推理的任务上输出质量下降能明显感觉到。后来换回 INT8显存刚好够用质量也回来了。所以我的建议是能用 INT8 就别急着上 INT4除非你确实需要更高的并发或者更长的上下文。再说并行策略。我最初用张量并行吞吐不错但延迟波动大后来发现是通信和计算没重叠好。调整之后稳定了很多。如果你的框架支持通信计算重叠一定要开。还有一个细节是模型加载时间。32B 模型从磁盘加载到显存即便有量化也要几分钟。如果服务需要频繁重启这个时间很折磨人。我的做法是把量化后的模型权重缓存到本地高速存储上重启时直接加载缓存能省不少时间。最后提醒一句多卡部署的稳定性高度依赖环境一致性。两张卡的驱动、固件、框架版本、甚至系统内核参数任何一处不一致都可能埋雷。我现在的习惯是部署前把所有版本信息记一份出问题时第一时间对照能快速定位是不是环境漂移导致的。这套配置不是性能最强的方案但在显存够用、成本可控、能稳定跑 32B这个目标下两张 300I Duo 是个务实的选择。关键是把量化、并行、KV Cache 这三件事的账算清楚剩下的就是耐心调参和盯监控了。