1. 为什么要在 2×V100 上折腾 QUASAR-NVFP4 量化模型手里有两张 Tesla V100 的人这两年心里多少有点复杂。16GB HBM2、单卡 900GB/s 带宽、FP16 算力 31.3 TFLOPS放到今天看纸面参数不算难看但问题在于它是 Volta 架构不支持 BF16不支持原生 FP8更别提 NVFP4 这种 Blackwell 时代才正式铺开的 4bit 浮点格式。偏偏现在开源社区放出来的新模型动不动就是 FP8 权重、NVFP4 量化、MoE 稀疏激活摆明了是冲着 Hopper 和 Blackwell 去的。于是就有了一个很现实的场景手上有 V100想跑最新的量化模型怎么搞。我这次要聊的就是这么一件事——用 1Cat-vLLM 这个推理框架在 2×V100 的机器上把 QUASAR-NVFP4 量化模型跑起来。先说清楚几个概念免得后面绕晕。vLLM是目前最主流的开源大模型推理引擎之一核心卖点是 PagedAttention 显存管理和连续批处理吞吐量比朴素实现高一大截。1Cat-vLLM是在 vLLM 基础上做的一层工程化封装针对国内常见的显卡环境、量化格式和部署习惯做了适配尤其是对老架构显卡跑新量化格式这件事做了不少兼容性工作。QUASAR-NVFP4是一套 4bit 浮点量化方案权重以 NVFP4 格式存储配合特定的 scale 策略在保持精度的同时把显存占用压到 FP16 的四分之一左右。V100就是我们手里的老将Volta 架构算力还在但指令集和数据类型支持是硬伤。这套组合能解决什么问题最直接的就是显存。一个 32B 级别的模型FP16 权重就要 64GB两张 V100 加起来 32GB 根本放不下。换成 NVFP4 量化权重压到 16GB 左右加上 KV Cache 和激活值两张卡刚好能塞进去。其次是成本V100 二手价格现在很友好如果能跑通新量化模型等于用极低的硬件成本获得了一个可用的推理服务。适合谁来参考手里有 V100 或类似老架构卡、想跑量化模型做本地推理、对吞吐量有一定要求但预算有限的开发者。如果你只是偶尔跑跑 7B 模型玩玩那没必要看这篇但如果你想把 32B 级别的模型真正用起来这套方案值得一试。需要提前说明的是V100 跑 NVFP4 并不是原生支持而是靠 1Cat-vLLM 里的反量化 kernel 把 NVFP4 权重在计算时还原成 FP16 再送进 Tensor Core。这意味着会有额外的反量化开销吞吐量比不上原生支持的卡但换来的是能跑起来。这个取舍在后面会详细展开。2. 整体方案设计与选型背后的取舍逻辑2.1 为什么选 1Cat-vLLM 而不是原生 vLLM原生 vLLM 对量化格式的支持是有明确边界的。官方主线主要支持 GPTQ、AWQ、FP8 这几种NVFP4 的支持是在较新版本里才逐步加入的而且默认假设你用的是支持 FP4 指令的硬件。V100 这种 Volta 卡原生 vLLM 加载 NVFP4 模型时大概率会直接报错或者在 kernel 选择阶段找不到合适的实现。1Cat-vLLM 的价值就在这里。它在 vLLM 的量化加载层和 kernel 调度层做了扩展针对不支持 FP4 指令的硬件提供了一套 fallback 路径权重以 NVFP4 格式加载进显存但在做矩阵乘法之前先用一个轻量级的反量化 kernel 把 4bit 数据还原成 FP16再走标准的 FP16 GEMM。这样做的好处是显存占用按 NVFP4 算计算精度按 FP16 算两头都兼顾了。代价是每次前向传播都要多做一次反量化延迟会上升吞吐量会下降但至少能跑。我对比过几个方案。直接用 llama.cpp 的 GGUF 量化V100 上跑是能跑但 llama.cpp 的批处理能力弱并发一高就顶不住不适合做服务。用 TensorRT-LLMV100 支持有限而且 NVFP4 在 TRT 里的路径对 Volta 基本是关闭的。用 SGLang它对老卡的支持还不如 vLLM。绕一圈下来1Cat-vLLM 是唯一一个明确针对这种“老卡跑新量化”场景做过适配的框架。2.2 NVFP4 量化格式到底是怎么回事NVFP4 是 NVIDIA 定义的一种 4bit 浮点格式全称是 NVIDIA FP4。它把一个数拆成三部分1 位符号位、2 位指数位、1 位尾数位。对比一下FP16 是 1 位符号、5 位指数、10 位尾数。NVFP4 的表示范围很窄精度也很粗但它有一个关键设计分块缩放。权重不是整体用一个 scale而是每 16 个或 32 个元素一组每组共享一个 FP8 格式的 scale 因子。这样在反量化的时候用组内 scale 乘以 4bit 值就能还原出接近原始分布的浮点数。QUASAR 这套量化方案在 NVFP4 的基础上又做了一层优化。它不光是权重量化还对激活值做了动态量化处理并且在 scale 的计算上用了更精细的搜索策略尽量减小量化误差。实际效果是一个原本 FP16 下困惑度 5.2 的模型QUASAR-NVFP4 量化后困惑度大概在 5.4 到 5.6 之间精度损失控制在可接受范围内。当然具体数值跟模型本身有关不能一概而论。这里要提醒一点NVFP4 的 4bit 是“存储 4bit”不是“计算 4bit”。在 V100 上计算还是 FP16所以不要指望能获得 4 倍算力提升。显存节省是实打实的算力提升是没有的。这个预期一定要摆正否则后面测出来吞吐量不如预期会觉得很失望。2.3 2×V100 的并行策略选择两张 V100 做推理并行方式主要有两种张量并行TP和流水线并行PP。TP 是把每一层的权重切分到两张卡上每层计算都需要卡间通信PP 是把模型按层切成两段一张卡算前半部分另一张卡算后半部分只在层边界通信。对于 V100 这种没有 NVLink 的机器大多数 PCIe 版本的 V100 都没有 NVLink 桥接卡间通信走 PCIe带宽大概 16GB/s 到 32GB/s跟 HBM 的 900GB/s 差了二三十倍。TP 每层都要 AllReduce通信频繁PCIe 带宽会成为瓶颈。PP 通信次数少但会导致一张卡空闲等待利用率下降。实测下来在 2×V100 上跑 32B 级别的 NVFP4 模型TP2 是更现实的选择。原因是模型单卡放不下必须切分而 PP 在只有两张卡的情况下流水线气泡太大吞吐量反而更低。TP2 虽然通信开销大但至少两张卡都在干活。如果模型小到单卡能放下那就直接 TP1别折腾并行。注意V100 的 PCIe 版本和 SXM 版本差别很大。SXM2 版本有 NVLink带宽 300GB/sTP 通信开销小很多。如果你手里是 SXM 版本的 V100TP2 的体验会好很多。PCIe 版本就要对通信开销有心理准备。3. 环境准备与依赖安装的实操细节3.1 驱动和 CUDA 版本的选择V100 是 Volta 架构计算能力 7.0。它支持的 CUDA 版本上限是 CUDA 12.x再新的 CUDA 13 就不支持了。驱动方面数据中心版本的 V100 建议用 535 或 550 系列的驱动这两个系列对 Volta 的支持比较稳定。太老的驱动可能不支持新版 PyTorch太新的驱动又可能对 Volta 做了功能裁剪。我这次用的是驱动 550.90.07CUDA 12.4。安装驱动的时候有个坑如果你的机器之前装过其他版本的 CUDA一定要先彻底清理否则容易出现库版本冲突。清理命令大概是先卸载旧驱动再删掉/usr/local/cuda下的残留然后重新安装。安装完用nvidia-smi确认两张卡都能正常识别并且 ECC 状态正常。V100 的 ECC 如果报错显存可用容量会减少严重的话会直接影响模型加载。# 确认显卡识别和 ECC 状态 nvidia-smi -q | grep -E Product Name|ECC Mode|Total如果 ECC 报错可以尝试用nvidia-smi -e 0关闭 ECC 再测试但生产环境不建议关 ECC数据完整性更重要。ECC 报错通常是显存颗粒老化如果频繁报错这张卡可能要考虑维修或更换了。3.2 Docker 镜像的拉取和配置1Cat-vLLM 官方提供了 Docker 镜像这是最省事的部署方式。镜像里已经打包好了 vLLM 本体、1Cat 的扩展层、CUDA 运行时和常用的 Python 依赖。你不需要在宿主机上装 PyTorch只要驱动装好剩下的都在容器里。拉镜像之前先确认 Docker 和 NVIDIA Container Toolkit 都装好了。NVIDIA Container Toolkit 是让容器能访问 GPU 的关键组件没装的话容器里看不到显卡。装好之后用docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi测试一下能输出显卡信息就说明配置正确。镜像拉取命令大概是这样docker pull 1cat/vllm-openai:latest具体 tag 根据你需要的版本调整。1Cat 的镜像 tag 通常包含 vLLM 版本号和 CUDA 版本号选跟你驱动匹配的就行。镜像大小在 10GB 到 15GB 之间拉取需要一点时间。3.3 模型文件的准备和校验QUASAR-NVFP4 量化模型通常以 safetensors 格式分发一个模型可能被切成多个分片文件外加一个config.json和一个量化配置文件。下载完之后一定要校验文件完整性尤其是分片文件少一个都加载不了。校验方法很简单看文件大小是否和发布页一致然后用sha256sum对一下哈希值。如果发布方没提供哈希至少确认文件数量对得上。我踩过一次坑下载了 8 个分片结果第 5 个分片因为网络问题只下了一半加载的时候报了个很隐晦的 tensor shape mismatch 错误排查了半天才发现是文件不完整。模型目录结构大概是这样model_dir/ ├── config.json ├── quantize_config.json ├── model-00001-of-00008.safetensors ├── model-00002-of-00008.safetensors ├── ... ├── model-00008-of-00008.safetensors ├── tokenizer.json └── tokenizer_config.jsonquantize_config.json是关键文件里面记录了量化格式、分块大小、scale 策略等信息。1Cat-vLLM 加载模型时会读这个文件来决定用哪套反量化 kernel。如果这个文件缺失或格式不对加载会失败。4. 启动参数配置与核心环节实现4.1 启动命令的完整拆解启动 1Cat-vLLM 服务核心就是一条docker run命令加上一堆参数。我把完整命令拆开讲每个参数为什么这么设都说清楚。docker run -d --gpus all --shm-size 16g \ -v /path/to/model_dir:/models/quasar-nvfp4 \ -p 8000:8000 \ 1cat/vllm-openai:latest \ --model /models/quasar-nvfp4 \ --tensor-parallel-size 2 \ --dtype float16 \ --quantization quasarnvfp4 \ --max-model-len 8192 \ --gpu-memory-utilization 0.90 \ --max-num-seqs 32 \ --enforce-eager \ --disable-log-requests--shm-size 16g是共享内存大小。vLLM 在多卡并行时会用共享内存做进程间通信默认的 64MB 不够用设成 16GB 比较稳妥。这个值跟你的模型大小和并发数有关模型越大、并发越高需要的共享内存越多。--tensor-parallel-size 2指定用两张卡做张量并行。这个值必须等于你的显卡数量设错了会报错或者只用一张卡。--dtype float16指定计算精度。V100 不支持 BF16所以必须用 FP16。如果你设成bfloat16启动时会报错或者自动降级不如直接指定 FP16。--quantization quasarnvfp4指定量化格式。这个参数告诉 1Cat-vLLM 用哪套量化加载逻辑。不同版本的 1Cat-vLLM 可能参数名略有不同具体看文档。--max-model-len 8192是最大序列长度。这个值直接影响 KV Cache 的显存占用。设得越大能处理的上下文越长但显存占用也越高。8192 是一个比较平衡的值32B 模型在 2×V100 上跑 8192 上下文KV Cache 大概占 6GB 到 8GB。--gpu-memory-utilization 0.90是显存利用率上限。vLLM 会按这个比例预分配显存留 10% 给系统和其他进程。设太高容易 OOM设太低浪费显存。0.90 是比较常用的值。--max-num-seqs 32是最大并发序列数。这个值决定了同时能处理多少个请求。设得越大吞吐量越高但显存占用也越大。32 是一个保守值如果显存有富余可以往上调。--enforce-eager是强制用 eager 模式禁用 CUDA Graph。CUDA Graph 能减少 kernel 启动开销但在 V100 上配合反量化 kernel 容易出问题所以先禁用稳定之后再考虑开启。--disable-log-requests是关闭请求日志减少日志量对性能有轻微帮助。4.2 显存占用的计算和验证启动之前最好先算一下显存够不够。以 32B 模型为例NVFP4 量化后权重约 16GBTP2 切分后每卡 8GB。KV Cache 按 8192 上下文、32 并发算每卡大概 3GB 到 4GB。激活值和临时缓冲区大概 2GB。加起来每卡 13GB 到 14GBV100 的 16GB 显存刚好够用但余量不多。启动之后用nvidia-smi观察显存占用。如果启动过程中 OOM优先降低--max-model-len或--max-num-seqs。如果启动成功但运行中 OOM说明并发太高或者上下文太长需要调整参数。# 启动后观察显存 watch -n 1 nvidia-smi正常情况下两张卡的显存占用应该差不多因为 TP 是均匀切分的。如果一张卡占用明显高于另一张可能是并行配置有问题。4.3 服务健康检查和首次推理测试服务启动后先确认端口监听正常再用一个简单的请求测试推理是否正常。# 健康检查 curl http://localhost:8000/health # 测试推理 curl http://localhost:8000/v1/completions \ -H Content-Type: application/json \ -d { model: /models/quasar-nvfp4, prompt: 你好请介绍一下你自己。, max_tokens: 128, temperature: 0.7 }如果返回正常的文本说明服务跑通了。如果报错看容器日志排查。常见错误包括模型路径不对、量化格式不匹配、显存不足、CUDA 版本冲突。首次推理会触发模型加载和 CUDA kernel 编译速度会比较慢可能要等几十秒。后续请求就快了。如果首次推理特别慢超过几分钟可能是反量化 kernel 在编译耐心等一下。5. 性能调优与常见问题排查5.1 吞吐量和延迟的实测数据我在 2×V100 PCIe 版本上跑了一个 32B 的 QUASAR-NVFP4 模型实测数据大概是这样单请求生成速度约 18 tokens/s并发 8 请求时总吞吐约 95 tokens/s并发 16 请求时总吞吐约 130 tokens/s并发 32 请求时总吞吐约 140 tokens/s但延迟明显上升。首 token 延迟在 200ms 到 500ms 之间取决于并发数。这个数据跟原生支持 FP8 的 H100 比肯定差很远但考虑到硬件成本我觉得可以接受。V100 的算力瓶颈主要在反量化开销和 PCIe 通信上。如果你用的是 SXM 版本通信开销小吞吐量应该能再高 20% 到 30%。调优的方向主要有几个一是调整--max-num-seqs找到吞吐量和延迟的平衡点二是调整--gpu-memory-utilization在显存允许的情况下尽量提高三是考虑开启 CUDA Graph但需要测试稳定性四是调整 KV Cache 的 block size默认值不一定最优。5.2 常见报错和排查思路我把踩过的坑整理成一个速查表方便对照排查。报错信息可能原因解决方法CUDA out of memory显存不足降低 max-model-len 或 max-num-seqsUnsupported quantization format量化格式不匹配确认 1Cat-vLLM 版本支持 QUASAR-NVFP4Tensor shape mismatch模型文件不完整校验分片文件完整性和哈希NCCL error卡间通信失败检查 PCIe 连接和 NCCL 配置Kernel launch failedCUDA kernel 不兼容尝试 --enforce-eager 或换镜像版本Model loading timeout模型太大或磁盘慢增加超时时间或换 SSDNCCL 错误在多卡环境里比较常见。V100 PCIe 版本没有 NVLinkNCCL 默认会尝试用 P2P 通信但有些主板和 BIOS 对 P2P 支持不好会导致通信失败。解决方法是在启动参数里加NCCL_P2P_DISABLE1环境变量强制走共享内存通信。这样带宽会低一些但稳定性好很多。docker run -d --gpus all --shm-size 16g \ -e NCCL_P2P_DISABLE1 \ ...5.3 精度验证和效果评估量化模型跑起来之后一定要做精度验证确认量化没有把模型搞坏。最简单的办法是拿几个标准问题测一下看回答是否合理。更严谨的做法是用困惑度或者标准评测集跑一遍跟 FP16 版本对比。我一般会准备一组测试问题覆盖常识、推理、代码、数学几个维度分别用 FP16 和 NVFP4 版本跑对比回答质量。如果 NVFP4 版本出现明显的胡言乱语、重复、逻辑断裂说明量化有问题可能是 scale 计算不对或者反量化 kernel 有 bug。提示量化模型的精度损失在长文本生成时更容易暴露。短回答可能看不出差别但生成几百个 token 之后量化模型的错误会累积。测试时尽量用长输出任务。5.4 实操心得和避坑建议第一条心得不要一上来就拉满参数。先把max-model-len设小一点max-num-seqs设少一点确认服务能稳定跑起来再逐步往上调。我见过太多人一上来就设 32768 上下文、128 并发结果 OOM 排查半天。第二条心得日志是你的朋友。1Cat-vLLM 的日志会输出模型加载、kernel 选择、显存分配等关键信息。启动失败时先看日志最后 50 行大部分问题都能定位。可以把日志级别调到 DEBUG看到更详细的信息。第三条心得V100 的散热要注意。V100 是数据中心卡被动散热需要机箱有强风道。如果散热不好跑一会儿就降频吞吐量直接掉一半。用nvidia-smi -q -d TEMPERATURE看温度超过 85 度就要考虑加强散热了。第四条心得模型文件放 SSD 上。32B 模型加载要读 16GB 数据机械硬盘要读好几分钟SSD 几十秒就搞定。如果模型放在网络存储上加载时间会更长甚至超时。第五条心得多测试几组参数再定配置。max-num-seqs和gpu-memory-utilization这两个参数对性能影响很大但最优值跟你的模型、硬件、请求模式都有关。花点时间做参数扫描找到适合你场景的配置比直接用默认值强很多。6. 后续扩展和实际使用中的体会这套方案跑通之后可以做的事情不少。比如接一个 Chatbox 或者类似的客户端把服务包装成可交互的界面比如加一层 API 网关做鉴权和限流比如把多个模型实例部署在不同端口用负载均衡分发请求。1Cat-vLLM 兼容 OpenAI 的 API 格式所以大部分现成的客户端和工具都能直接对接。如果后续要换模型只要新模型也是 QUASAR-NVFP4 格式改一下--model路径就行其他参数基本不用动。如果换成其他量化格式比如 GPTQ 或 AWQ需要确认 1Cat-vLLM 版本支持并且调整--quantization参数。我在实际使用中最大的体会是老卡跑新模型关键不在于算力而在于显存和兼容性。V100 的算力跑 32B 模型其实够用瓶颈在显存放不下 FP16 权重。NVFP4 量化解决了显存问题1Cat-vLLM 解决了兼容性问题两者结合才让这件事变得可行。如果你手里也有 V100不妨试试这套方案成本不高折腾的过程本身也能学到不少东西。最后分享一个小技巧启动服务之前先用一个很小的模型比如 1B 级别跑一遍完整流程确认环境没问题再换大模型。这样排查问题时变量少效率高很多。直接上大模型一旦报错你分不清是环境问题还是模型问题排查起来很痛苦。