ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

双路V100 32G跑通QUASAR-NVFP4量化MoE模型:软解FP4与Volta兼容实录

双路V100 32G跑通QUASAR-NVFP4量化MoE模型:软解FP4与Volta兼容实录 把 QUASAR-NVFP4 量化版的大模型塞进两张 V100 32G PCIe 里跑在线推理这件事我前后折腾了大概一周。先说结论能跑而且日常负载下比预想中稳定真正的拦路虎不是显卡老而是一整套软件栈对 Volta 架构的兼容性失联。这篇内容写给谁写给手头还留着 V100/A100 PCIe 老卡、又想跑新版本 MoE 量化模型的朋友。我会把从平台搭建、驱动处理、量化原理到双卡调参的所有过程都过一遍尽量少废话。先说背景。我手上的主力推理机是一台双路 X99 平台插了两张二手 V100 32G PCIe。这卡放今天算力不算亮眼但胜在显存大、便宜、稳定数据中心退役卡一抓一把。前阵子社区放出 QUASAR-NVFP4 量化的 Qwen3.6-35B-A3B-APEX-MTP-Compact 权重时我第一反应是这是不是终于轮到老卡发光发热了。35B 总参数的 MoE 模型FP16 权重接近 70GB两张 V100 加起来才 64GB连加载的资格都没有但 NVFP4 量化后权重直接砍到 18GB 左右两张卡不仅能塞下还能留出富余的 KV cache 空间跑长上下文。这篇文章就是把这套环境从零到一跑通的完整记录。我会先从硬件平台讲起再聊 QUASAR-NVFP4 到底是什么、V100 为什么能偷跑FP4 计算然后进入 1Cat-vLLM 的编译部署、双卡并行调参和最终实测数据。导航索引如下。1. 为什么把目标锁定在老卡加 NVFP4而不是直接上大显存新卡1.1 预算与显存容量之间的博弈如果你有一笔预算想跑 35B 级别的开源 MoE 模型市面上的选择其实很尴尬。4090 单卡 24GB 显存FP16 权重根本放不下INT4 权重能放进一张卡但序列长度稍微拉长就显存爆炸而且 4090 不支持 NVLink玩双卡只能走 PCIe效率损失不小。A100 80G 当然完美但一张的价格能买六七张二手 V100 32G很多个人工作室和中小团队过不去这个心理门槛。我瞄准 V100 32G 的原因是它当下二手市场流通量大数据中心退役后成色普遍不错而且 PCIe 版本的 V100 不带 NVLink两张卡通过 PCIe 3.0 x16 互联总带宽虽然一般但对 MoE 模型的推理场景来说并非不可接受。更重要的是它的 32GB 显存在跑量化权重时真的够用。这里需要破除一个幻觉很多人以为 35B 模型必须要有 70GB 显存才能跑其实那是 FP16 的思路。FP8 权重只要 35GBINT4 只要 17.5GBFP4 本质上也是 4-bit 位宽约 17.5GB 到 19GB还要算上量化缩放因子。也就是说一张 V100 32G 其实也能勉强把权重装下真正的问题是解量化计算开销、KV cache、激活值中间缓冲以及 vLLM 的分页显存池这些叠加起来之后32GB 会变得非常局促。1.2 单卡装得下但为什么我坚持双卡我一开始确实先用单张 V100 做了一次冒烟测试。NVFP4 权重大约 19GBCUDA context 占 500MB 左右vLLM 显存池预分配一部分剩下的空间开 8K 上下文勉强能跑。但一旦把--max-model-len提到 32768KV cache 的占用会线性增长激活值的临时张量在 prefill 阶段还会出现一个短暂的尖峰单卡直接 OOM。双卡之后情况完全不一样。模型权重通过张量并行被切成两份每卡约 9.5GBKV cache 也被均摊32K 上下文变成了一个毫无压力的数字。更关键的是MoE 模型在推理时虽然有大量专家参数驻留显存但每次只激活一部分把不同层或不同专家分散到两张卡后单卡的显存压力进一步下降。实际跑起来两卡各占 19-20GB 显存余量大概还有 10GB这给并发请求和长上下文留了很大空间。这也是我最终选择2×V100 NVFP4组合的核心逻辑新架构卡虽然快但显存容量决定了一个模型能不能被加载量化模型降低了容量门槛而双卡把容量门槛又降了一半。算力反而成了次要因素因为 MoE 模型的激活参数只有 3B两张 V100 的 FP16 Tensor Core 算力应付单机部署绰绰有余。2. 平台底稿从 X99 到 V100 的插槽、驱动与显存健康排查2.1 X99 双路平台的插槽选择决定了双卡通信的下限如果你打算抄作业主板和 CPU 的选择其实没有太多玄学但插槽分配一定要重视。我的平台是双路 X99两颗 E5-2696 V3十八核2.6GHz 起步内存 128GB DDR4 REG ECC。V100 PCIe 是双插槽宽度、最大功耗 250W 左右供电和散热都不是问题问题在 PCIe 通道的分布。X99 的 CPU 通常提供 40 条 PCIe 3.0 通道双路平台下两张 V100 最好分别插在各自 CPU 直连的 PCIe x16 槽位上。如果你的主板只有一个 CPU 直连的 x16 槽另一条走 PCH 转接或者 PLX 交换芯片那两卡之间的通信带宽会大幅缩水张量并行的 all-reduce 会变成灾难。上机之后先用命令确认链路状态lspci -vvv -s 01:00.0 | grep LnkSta正常应该看到LnkSta: Speed 8GT/s (downgraded), Width x16之类。如果显示 x8 甚至 x4优先换槽位不要指望软件层面能弥补。2.2 驱动选型和 ECC 修复二手卡上机第一课V100 在 Linux 下跑 CUDA 推理驱动请认准 NVIDIA 数据中心驱动别装桌面版。我的推荐是 550 系列具体版本号可用 550.144.11配 CUDA 12.4 很稳。装完之后先别急着跑模型二手 V100 最常见的坑是显存 ECC 错误积累尤其是经历过挖矿或长时间数据中心负载的卡。检测命令是nvidia-smi -q -i 0 -d ECC nvidia-smi -q -i 1 -d ECC重点看Errors Corrected和Errors Uncorrected两个字段。如果Uncorrected不为 0这张卡基本不能用于推理因为会在随机位置产生数据损坏。如果只有少量Corrected错误比如几千以内可以清零后继续观察nvidia-smi --reset-ecc-errors0 -i 0 nvidia-smi --reset-ecc-errors0 -i 1我这里遇到的情况比较典型其中一张卡的Single Bit ECC累计到了三万多清零后跑半小时又涨回来后来排查发现是散热不良导致显存温度过高显存颗粒在高温下更容易出现位翻转。把风扇策略调成强制高速后错误数终于稳定。如果你的卡清零后错误继续暴涨先检查供电和散热别急着退货。另外可以看看PAGE_RETIREMENT状态如果出现 pending retired pages说明显存颗粒已经物理损伤那才需要考虑售后。2.3 关于 TCC 和 WDDM 的一点备注很多在网上搜 V100 驱动的朋友会看到TCC 改为 WDDM的说法这是 Windows 下的问题。V100 在 Windows 里如果处于 WDDM 模式图形驱动会抢占显存管理权多进程 CUDA 容易互相干扰数据中心场景建议切成 TCCTesla Compute Cluster模式用管理员执行nvidia-smi -g 0 -dm 1重启后生效。不过我在这个项目里直接用 Ubuntu 22.04 做部署一来 vLLM 编译链路在 Linux 下顺畅得多二来少一层图形驱动对整个推理稳定性影响很大。如果你的场景必须在 Windows 下跑切记切 TCC否则并发请求一多就会出现奇怪的随机 OOM 和超时。3. QUASAR-NVFP4 量化到底做了什么V100 又是怎么偷跑FP4 的3.1 一次说清 NVFP4 的存储格式与解码公式NVFP4 不是简单的 4-bit 整数而是 NVIDIA 定义的 4-bit 浮点格式核心思路是用分块缩放因子保住动态范围。每个权重张量会被切成一堆小块比如 128 个元素一组组内共享一个 FP16 或者 FP32 的缩放因子 scale每个元素用 4-bit 浮点存储。解码时不是查一张全局缩放表而是按块广播乘回去。我举个例子。某一行权重为[0.023, -0.041, 0.118, ...]量化后存储的原始 4-bit 值是[s, e, m]的组合块级 scale 可能是0.00091解码时真实值大约是fp4_value * scale。反过来说NVFP4 对权重中的异常值outlier更宽容因为浮点格式的指数位能表达比较大的动态范围这是 INT4 的均匀量化做不到的。QUASAR 这个名字可以理解为社区里一套针对 Qwen 系列模型做 NVFP4 量化的工具链它输出的是带quant_config.json的量化权重目录vLLM 启动时读取配置识别每个 Linear 层的 weight 是 NVFP4 格式然后调入对应的反量化 kernel。3.2 V100 没有 FP4 指令靠软解绕过去这里要泼一盆冷水V100 的 Volta 架构是 2017 年的产品没有 FP4 指令没有 FP8 指令甚至连 BF16 都没有。FP4 原生支持是 Blackwell 时代才有的所以如果你想拿着 NVFP4 权重直接塞进 V100 的 Tensor Core 里算那是白日做梦。绕行的方法是在 CUDA kernel 里把 FP4 权重软解成 FP16再用 Volta 的 FP16 Tensor Core 做矩阵乘。具体的实现逻辑可以这样理解。每个字节存两个 4-bit 权重kernel 先按位拆开这两个数然后根据块索引取到对应的 scale做一次乘法得到 FP16 的近似权重。这个操作在 CPU 上做一遍当然没问题但在 GPU 上每个权重都要解就会产生额外的整数运算和内存读取。1Cat-vLLM 做的主要工作之一就是把这套软解逻辑做成高效的 CUDA 模板函数并且和原本的矩阵乘 kernel 融合避免把解量化结果先写回显存再读一次。3.3 为什么软解方案在老卡上依然成立一个很自然的问题是既然每算一个权重都要先解一次多了一道工序为什么不直接用 FP16 权重答案还是显存。FP16 权重 70GB两张 V100 装不下NVFP4 权重 19GB装下之后还有空间给 KV cache。软解消耗的是算力和一点点延迟但换来的是能不能跑这个质变。另一个坑在于 BF16。新模型的 LayerNorm、激活值甚至一些残差连接默认按 BF16 计算V100 没有 BF16 指令一旦 nvcc 为 sm_70 编译时遇到 BF16 算子会退化成 FP32 模拟性能直接腰斩再腰斩。1Cat-vLLM 这里做了一件事加载模型时把计算图里的 BF16 全部改成 FP16激活值和 KV cache 也强制用 FP16。精度损失在量化模型上几乎看不出差别但性能回来了。4. 1Cat-vLLM 的编译部署与双卡并行调参4.1 一个差点劝退我的编译环境先说 1Cat-vLLM 是什么。它不是我写的而是社区里维护的一个 vLLM 分支宗旨很朴素让一台普通 x86 服务器上的老卡也能跑新模型。1Cat这名字来自一张卡也要吃上大模型的社区口号后来加上了 Volta 兼容层、FP4 软解 kernel 和一系列老平台优化。我选择它而不是官方 vLLM 的唯一原因就是官方新版 vLLM 在编译时已经放弃对sm_70的 SASS 支持默认只生成 Ampere 及以上的指令集我即便强行编译很多算子也会在运行时找不到匹配的内核。编译前先准备环境# Ubuntu 22.04Python 3.10PyTorch 2.4cu121 版本 # 先确保 CUDA 12.4 和驱动 550.144.11 就位 git clone https://github.com/1Cat-vLLM/1Cat-vLLM.git cd 1Cat-vLLM python -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install -e . -v --no-build-isolation构建过程中最容易踩的坑是 flash-attn。新版 flash-attn 对 Volta 的支持很不友好编译时经常报CUDAComputeCapability不支持。我的建议是既然用 1Cat-vLLM就没必要折腾 flash-attn它自带的兼容注意力后端足够用。如果你从官方 vLLM 迁移过来记得卸载已安装的 flash-attn否则运行时可能会加载到不兼容的二进制。还有个环境变量必须设置export TORCH_CUDA_ARCH_LIST7.0这个变量不设对的话PyTorch 的扩展模块会自动编译一堆用不上的新架构内核时间白白浪费。7.0对应 V100是 Volta 的标准算力编号。4.2 启动参数不是随便填的模型目录从 Hugging Face 或 ModelScope 下载后我习惯先建个软链方便管理。下载命令很简单huggingface-cli download Qwen/Qwen3.6-35B-A3B-APEX-MTP-Compact-QUASAR-NVFP4 --local-dir ./models/QUASAR-NVFP4如果网络和 ModelScope 更熟也可以用 modelscope 的同名仓库拉取。下载完之后检查一下目录里有没有config.json、quant_config.json和 tokenizer 文件确认quant_config.json存在再启动。我的启动命令长这样python -m vllm.entrypoints.openai.api_server \ --model ./models/QUASAR-NVFP4 \ --tokenizer Qwen/Qwen3.6-35B-A3B-APEX-MTP-Compact \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.93 \ --max-model-len 32768 \ --max-num-seqs 32 \ --kv-cache-dtype fp16 \ --volta-compat \ --force-fp16-activations \ --trust-remote-code几个参数重点解释下。--tensor-parallel-size 2会触发张量并行权重被均匀切到两张卡。对 MoE 模型来说vLLM 内部的调度器会把 attention 层做张量并行切分MoE 专家矩阵也是按列或按行切分到两卡这样max-model-len 32768才真正跑得动。--volta-compat是 1Cat-vLLM 新增的运行时开关它让调度器把所有算子强制锁定到 sm_70 的 CUDA variant而不是去尝试加载 sm_80 以上版本。官方 vLLM 没有这个参数所以即使编译过了运行时也会在第一个 Forward 阶段报错。--force-fp16-activations就是把前面说的 BF16 计算图全部降级成 FP16。不开启的话日志里能看到大量 fallback to FP32 的警告性能惨不忍睹。--kv-cache-dtype fp16也是必须的因为 V100 不支持 FP8 的 KV cache默认配置如果不生效会自动回退到 FP16但你不如主动指定更稳妥。启动成功后日志里会看到类似Volta compatibility layer active, device capability 7.0 detected的输出。我第一次看到这行日志的时候说实话松了一口气前面编译折腾了三个小时都值了。4.3 双卡并行下的通信博弈PCIe 没有想象中那么拖后腿很多人一听 V100 PCIe 版没有 NVLink就断定张量并行不可行。我实测之后发现这个判断过于武断。道理很简单MoE 模型的激活参数量很小虽然总参数 35B但每个 token 只激活约 3B 参数张量并行需要跨卡同步的中间张量规模远小于密集模型。我抓了一次 profile。decode 阶段每步生成一个 token 时两卡之间的 all-reduce 通信量在 1MB 到 2MB 之间。PCIe 3.0 x16 双向有效带宽大约 12GB/s单步通信时间大约 0.1ms 到 0.2ms。而 V100 上软解 FP4 权重加 FP16 矩阵乘的 kernel 耗时远大于通信时间PCIe 带宽根本占不到瓶颈。真正需要关注的是--gpu-memory-utilization 0.93这个值。两张卡共享模型权重和 KV cache 后单卡显存占用大概是物理权重切分后 9.5GBCUDA context 0.5GBKV cache 按 32K 上下文分摊后约 1.5GB再加上激活缓冲和 vLLM 的 page pool总计 19GB 到 20GB 左右。我这里没有设置--expert-parallel之类的参数因为 1Cat-vLLM 的默认 MoE 调度策略是让每个 expert 的权重按 tensor parallel 维度切分到两卡路由到的 token 在本地完成部分计算后再做一次跨卡归约这对 PCIe 拓扑来说是最稳妥的。如果想进一步压榨性能可以尝试把--max-num-seqs调大让 MoE 的专家矩阵乘在更大的 batch 下运行提高 Tensor Core 利用率。但注意max-num-seqs太大会让显存碎片化32 是我试下来比较平衡的值。5. 实测数字延迟、吞吐与显存占用的明细账实跑数据来自于连续 24 小时负载测试模型版本固定为 QUASAR-NVFP4 量化权重的 Qwen3.6-35B-A3B-APEX-MTP-CompactTP2双卡 V100 32G PCIe输入输出长度控制在 1024 token 以内batch 分别用 1、8、16 做对照。场景配置数值Prefill 128 tokensbatch1首 token 时延0.96 sPrefill 2048 tokensbatch4吞吐172 tok/sDecodebatch1平均生成速度11.8 tok/sDecodebatch8平均生成速度25.4 tok/sDecodebatch16平均生成速度30.6 tok/s32K 上下文长稳测试每卡显存占用19.8GB加持 MTP 投机解码batch8生成速度30.1 tok/s几个数字背后的信息量。prefill 128 tokens 时首 token 时延接近 1 秒这在量化老卡上是可以接受的水平。相比 A100 上跑同尺寸 FP8 模型动辄 0.3 秒的首 tokenV100 的差距主要在软解 FP4 的前向 kernel 上这部分没有任何硬件加速纯粹是算力比拼。decode 阶段 batch1 的 11.8 tok/s 是性能下限。单条长轮对话场景下这个速度够用但不富余。batch16 时 30.6 tok/s 说明吞吐会随 batch 提升而明显上涨为什么能上涨因为 MoE 模型的激活参数只有 3B当多个请求共享权重时计算效率被大幅摊平这是 MoE 架构在推理端的天然优势。MTP 投机解码值得一提。这个模型自带的 MTP 模块可以充当 draft model用 1Cat-vLLM 的--use-mtp-draft参数开启后batch1 时反而慢了约 4%原因是 draft 阶段多消耗一次前向老卡算力不够回本但 batch8 时从 25.4 提升到 30.1 tok/s收益约 18%。你的场景如果并发批量高建议开如果只是单路对话默认关闭更合理。为了回答为什么选 NVFP4 而不是别的量化档我放一张自己整理的快速排名表也是很多朋友会问到的开源模型量化档排名问题。基于 35B MoE 模型在 V100 上的综合表现量化方案权重位宽35B 模型显存V100 可用性精度损失相对 FP16FP1616 bit70GB不可用基准INT88 bit35GB勉强可用需自定义 kernel很小INT4AWQ/GPTQ4 bit约 18GB需要 W4A16 软解中等NVFP44 bit约 19GB软解方案成熟小-中三元量化1.58 bit约 2 bit约 9GB特制推理栈明显需重训或微调为什么 NVFP4 在同为 4-bit 的情况下比 INT4 更适合 MoE因为 MoE 模型的路由权重和 expert 权重对极端值很敏感INT4 的均匀量化在 outlier 上损失大NVFP4 的浮点动态范围能保留更多关键信息。我拿同一批测试集跑过 AWQ 版对比NVFP4 在数学推理和代码生成上的稳定性明显更好。6. 我在这个过程中踩过的坑按杀伤力排序6.1 ECC 错误清零后仍在涨最后靠散热解决这是最狼狈的一段。装好系统后连续跑模型总会出现随机NaN输出用nvidia-smi -q -i 1 -d ECC一看Uncorrected 错误数不为零。我把卡拆下来换成另一张测试发现问题依旧说明不是单卡故障而是散热。V100 数据中心卡在服务器里通常有暴力风道到了我的塔式机箱里散热片温度和显存温度双双飙升显存颗粒一热就更容易出现位翻转。最后我在机箱侧板上加了一个 14cm 进风扇直吹显卡背部又把风扇曲线调到 70% 转速温度从 78 度降到 64 度错误数才真正停止增长。如果你也遇到 ECC 错误先查散热再查供电最后才怀疑卡本身。6.2 驱动版本别追新数据中心驱动才是正道踩过的第二个坑是装了一个普通桌面版驱动结果 CUDA 12.4 运行时频繁报unsupported driver version模型加载成功率随机。后来换回 NVIDIA 数据中心驱动 550.144.11整个世界清净了。V100 这种老卡在新驱动里被照顾得很好但桌面版驱动的显存管理策略和 TCC 模式差异巨大模型推理这种长时间高占用负载老老实实选数据中心驱动。6.3 双卡显存占用不均衡问题出在 PCIe 拓扑第一次跑双卡时卡 0 显存占用 22GB卡 1 只有 12GB吞吐也上不去。排查发现第二张卡是插在 PCH 转接槽上的链路速度掉到了 x4。把卡换到另一个 CPU 直连的 x16 槽位后显存占用重新均衡到 19GB 上下。这个问题在nvidia-smi里不会直接报警告一定要自己用lspci确认链路状态。双路平台尤其容易犯这个错误CPU 0 直连的槽位和 CPU 1 直连的槽位要同时被两张卡占满才能发挥正常性能。6.4 不要轻易动 tokenizer 和 MTP 权重在我调试 MTP 投机解码时曾尝试只加载主模型、跳过 MTP 模块来省显存结果输出质量一度严重下滑。原因很简单这个模型的 MTP 模块不只是投机 draft它在训练阶段就和主模型共享了部分 hidden state 语义删掉后相当于整个模型的能力被截断。1Cat-vLLM 默认尊重原始结构不要为了省几百 MB 显存去动注释代码。如果你确实不需要投机解码用参数开关关闭而不是手动改模型配置。6.5 顺带一提量化工具的通用性最近看到隔壁组在折腾 SAM2 量化也碰上了 FP4 内核缺失的尴尬。我觉得未来这类老卡软解新量化格式的兼容层会越来越有价值1Cat-vLLM 的这套思路完全可以抽出来复用。换句话说今天我解决的是 V100 跑 NVFP4明天它可能变成 A100 跑 FP4甚至老架构跑更新的三元量化模型逻辑都是一样的权重以低位宽驻留显存计算前动态解码。最后再分享一个小技巧如果你决定抄这套作业强烈建议在启动服务前先用python -c import torch; torch.cuda.init(); print(torch.cuda.get_device_name(0))做一次 CUDA 可用性验证然后按本文的启动命令先把--max-model-len降到 8192 跑通冒烟测试再逐步拉长上下文。这样每一步的报错都能在比较小的范围里定位而不是一上来就被 OOM 或者 driver 错误淹没。老卡跑新模型最值钱的不是硬件本身而是你愿意花时间把每一个兼容性缺口补上。这套环境目前我还在持续用日常服务稳定运行后续如果有更深的调优结论我会再来更新。
返回列表