news 2026/10/20 12:25:22

V100老卡跑70B大模型:vLLM+AWQ量化实战与压测全记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
V100老卡跑70B大模型:vLLM+AWQ量化实战与压测全记录

前阵子把机房闲置的一台 8 卡 V100 32G 服务器翻出来,接了一个 70B 级别模型的实际业务负载。一开始同事都觉得这卡太老,跑大模型纯属自找麻烦,但压测数据出来之后,结果比想象中好很多——8 张 V100 32G 配合 vLLM 部署,不仅能跑,还能稳定承接真实线上流量。

这篇文章把我从模型选型、量化方案、vLLM 参数调优到业务吞吐压测的完整过程写出来,重点会落在实测数据和踩坑记录上。如果你手头也有老卡,或者正纠结要不要花钱上新的加速卡,这篇可以给你一个比较实心的参考。

1. 为什么还在用 V100 跑大模型:老卡的真实处境

1.1 不是怀旧,是算力成本卡住了

很多团队的情况其实都一样:算力采购审批下来之前,手里真正能用的就是一些前几年攒下的老卡。V100 32G 在当时是旗舰级的配置,但现在跑大模型推理,它有几个明显的短板。

V100 不支持 bf16。这点非常关键,它的 SM 计算能力是 7.0,硬件上没有 bfloat16 的 Tensor Core 路径。现在很多新模型预训练权重都是 bf16 产出,直接加载到 V100 上要么精度溢出,要么被迫回退到 fp32 导致显存翻倍。所以在 V100 上做推理,标准做法是显式指定fp16,同时模型权重也得保证是半精度或更低。

显存带宽相对低。V100 32G 的 HBM2 带宽单卡约 900GB/s,A100 是 2TB/s,H100 到了 3.35TB/s。大模型 decode 阶段是典型的带宽敏感型负载,每生成一个 token 都要把权重完整读一遍。带宽低,天花板就低,这点在后面的吞吐测试里会非常明显。

NVLink 带宽也不是一个时代的产品。如果是 SXM2 版本的 V100,NVLink 1.0 双向约 300GB/s;如果是 PCIe 版本,走 PCIe 3.0 x16,通信带宽只有 16GB/s 左右,做张量并行基本等于拿命在拼。我这次用的机器是 SXM2 版本,NVLink 还能用,但和 A100 的 600GB/s 相比差了整整一倍。

这些限制综合起来只有一个结论:V100 不是不能跑大模型,而是要用对方式跑。最核心的手段就是量化。

1.2 硬件画像:先把能力边界画清楚

在谈部署和压测之前,先把这 8 张卡的硬件参数列出来,后面的所有决策都基于这张表:

硬件项V100 32G (SXM2)备注
计算能力7.0不支持 bf16,FP8 更不用想
单卡显存32GB HBM28 卡合计 256GB
单卡显存带宽约 900GB/s聚合约 7.2TB/s
NVLink1.0,约 300GB/s 双向每个 GPU 6 条链路
FP16 Tensor Core约 100+ TFLOPSprefill 阶段够用
显存介质HBM2相比 HBM3 延迟也偏高

从这个配置能算出一个很有意思的数据:如果跑一个 70B 级别的 4-bit 量化模型,权重大约 40GB 左右。8 卡做张量并行之后,每卡只需要承载约 5GB 权重。decode 阶段每卡每秒钟能读 900GB,理想状态下每卡每秒能处理 900 / 5 = 180 个 token 的权重读取,8 卡聚合就是接近 180 token/s 的理论上限。

当然这是理想值,实际还要算上 KV cache 读取、TP 通信、调度开销,最后能跑到 60% 到 80% 就已经非常不错了。这个推理过程在后面压测数据里会继续验证。

2. V100 上的模型选型与部署方案:32B 还是 70B?量化怎么选?

2.1 为什么必须走 4-bit 量化路线

模型选型这一步,我起初纠结过要不要跑一个更小一点的模型。我们的业务需要比较强的生成能力,所以目标定在 70B 级别。这时候必须面对一个问题:70B 模型如果有 fp16 权重,大约是 70 × 2 = 140GB。8 张卡合计 256GB,听起来放得下,但 KV cache 还要占空间,max-model-len 稍微拉长一点就撑不住了。

如果硬上 fp16,假设每卡留 2GB 给模型权重以外的开销,剩下大概每卡 30GB 能分给 KV cache。单卡 KV cache 30GB,8 卡总共 240GB,看起来不算少,但 70B 模型有 80 层左右的 transformer block,每层都要存 KV,算下来能支持的并发序列数和上下文长度都非常有限。实际压测时一旦并发上来,很快就会出现显存不足导致的 preemption,时延直接崩掉。

所以我最终选了AWQ 4-bit 量化,把权重压到约 40GB,每卡只占 5GB。这样每卡能腾出 20GB 以上的空间给 KV cache,在 8k 上下文长度下可以支撑几十个并发序列,这才是真实业务能用的配置。

这里说下为什么选 AWQ 而不是 GPTQ。两者都是 4-bit 量化,但 vLLM 里 AWQ kernel 的成熟度更高,推理时反量化开销更小。V100 的硬件性能本来就紧张,任何多余的显存读取都是在浪费宝贵的带宽。AWQ 在同等量化精度下吞吐更好,尤其适合这种带宽受限的老卡环境。

2.2 全卡单实例还是多实例拆分

还有一个部署拓扑的抉择:8 张卡是跑一个 TP=8 的大模型实例,还是拆成两个 TP=4 的实例分别服务不同业务?

我把两种方案都评估了一遍。TP=8 跑 70B 的好处是显存充裕,每卡权重只有 5GB,KV cache 空间巨大,单实例的调度也简单。坏处是 TP=8 每次 forward 都要做 8 卡之间的 allreduce,NVLink 1.0 的带宽会成为瓶颈之一。

拆两个 TP=4 实例跑 32B 模型的好处是能同时服务两路业务,吞吐总量可能更高。但两个实例会互相争抢同一个 NVLink 域的带宽,而且 32B 模型的生成质量不一定能满足我们对 70B 级别的业务预期。

最终我选了全卡 TP=8 单实例跑 70B。核心原因是这个业务对单次请求的推理质量要求更高,且请求长度偏长,KV cache 容量比多实例更值钱。V100 的 NVLink 1.0 做 TP=8 虽然不如 Ampere 芯片顺畅,但 70B 权重分散后每卡的通信量其实可控,实测比预想的好。

这个选择的权衡逻辑可以用一张表说清楚:

方案显存余量通信压力适用场景
TP=8 跑 70B-AWQ大,KV cache 充足8 卡 allreduce,NVLink 1.0 吃紧长上下文、高并发、追求强生成能力
双实例 TP=4 跑 32B中,每实例各有缓存4 卡通信,相对小多路业务隔离、追求整体 RPS

3. vLLM 在 V100 上的配置实战:参数怎么调才不白烧卡

3.1 编译、启动和必须踩的架构坑

vLLM 对 V100 是支持的,但安装阶段就有讲究。默认 pip 安装的包按常见架构(包括 7.5、8.0、8.6、9.0)编译,V100 是 7.0,很可能装完 import 正常,一跑就报no kernel image is available for execution on the device。解决方法是安装前先指定架构:

export TORCH_CUDA_ARCH_LIST="7.0" pip install vllm

这里提醒一句,如果用的是较新的 vLLM 版本,CUDA 版本最好对应 11.8 或 12.1,我之前用 CUDA 12.0 配某些版本会出现奇怪的对齐报错,重装成 11.8 之后就稳定了。

启动命令我最终调成的样子:

python -m vllm.entrypoints.openai.api_server \ --model /data/models/xxx-70b-awq \ --quantization awq \ --dtype float16 \ --tensor-parallel-size 8 \ --gpu-memory-utilization 0.92 \ --max-model-len 8192 \ --max-num-seqs 32 \ --enable-chunked-prefill \ --port 8000

每一个参数都不是随便写的,下面逐个展开。

3.2 dtype、显存利用率与 KV cache 的三角平衡

--dtype float16这个参数在 V100 上不是可选项,而是必选项。如果不显式指定,vLLM 可能根据模型配置文件走 bf16 路径,V100 上轻则精度异常,重则直接 core dump。这一点在启动日志里不容易察觉,压测时才发现生成内容质量不对,排查了很久。

--gpu-memory-utilization 0.92也是反复试出来的。第一版我按新卡习惯设了 0.95,结果在加载阶段就遇到 KV cache 分配失败。原因很简单,vLLM 会按max-model-len一次性预留 KV cache 的显存,如果预留后发现放不下,直接报错退出,不会降级到少一点的缓存。V100 每卡只有 32G,权重加激活函数加中间态占用比新卡更大,0.92 是稳定和容量之间的一个平衡点。

说到 KV cache,这就是max-model-len和并发能力之间的三角关系。KV cache 总容量越大,能支撑的序列数和上下文长度越长,但它占用的显存也是和模型权重抢资源。70B-AWQ 权重约 40GB 分散到 8 卡后,每卡还能腾出约 24GB 给 KV cache,在这块 24GB 里,8192 的max-model-len和 32 的max-num-seqs是能共存的。如果继续拉长max-model-len到 16384,KV cache 单序列开销翻倍,max-num-seqs就得往下砍,压测并发一上来一样崩。

这里给个经验值:在 V100 上跑 70B-AWQ,max-model-len超过 8192 之后收益就很小了,因为长上下文请求的 prefill 时间本身就长,decode 吞吐又受带宽限制,模型根本来不及发挥长上下文优势就碰到显存墙了。

3.3 老卡要不要开 CUDA graph 和 chunked prefill

vLLM 默认会尝试 CUDA graph 捕获,V100 上也支持,但我实测下来遇到过两次 graph capture 失败。症状是启动时报CUDA error: operation not permitted或者卡在Graph capturing阶段不动。如果遇到这种情况,可以在启动参数里加--enforce-eager,强制走 eager 模式。代价是每个 token 的调度开销变大,实测吞吐下降大约 15% 到 20%。如果 CUDA graph 能正常捕获,就不用开这个参数。

--enable-chunked-prefill我建议开启。V100 的 prefill 和 decode 是共享同一批算力和带宽的,如果一个大请求的 prefill 独占 8 张卡,后面所有排队请求的首 token 时延都会暴涨。chunked prefill 会把这个长 prefill 切成小块,穿插到 decode 的空隙里执行,短请求的整体体验提升非常明显。这个参数对老卡的效果比新卡更显著,因为它本质上是把稀缺的带宽资源在时间上做了切片。

4. 业务吞吐压测:真实请求模式下的数据

4.1 压测场景定义与工具

压测不能拿官方 benchmark 的那套固定长度请求硬套。真实业务里请求长度是分布的,有的短,有的长,混在一起才会暴露调度问题。

我定义了三类负载:

  • 短问答:输入 100 到 300 token,输出 200 到 500 token。类似客服回复、知识库抽取。
  • 长总结:输入 2000 到 3000 token,输出 500 到 800 token。类似文档摘要、对话记录压缩。
  • 混合负载:短问答占 60%,长总结占 40%。

压测工具用了 vLLM 自带的benchmark_serving.py,也写了一个简单的 Python 脚本控制并发数。脚本逻辑很直接:固定并发数,随机生成符合前面分布的请求,记录每个请求的 TTFT、端到端时延、生成 token 数,再聚合出整体 RPS 和输出吞吐。

import asyncio import aiohttp import random import time async def send_one(session, endpoint, input_len, output_len): prompt = "e" * input_len payload = { "prompt": prompt, "max_tokens": output_len, "temperature": 0.7, "stream": True, # 流式返回,和线上一致 } t0 = time.time() first_token_time = None async with session.post(endpoint, json=payload) as resp: async for line in resp.content: if line and first_token_time is None: first_token_time = time.time() - t0 total_time = time.time() - t0 return first_token_time, total_time

这里特别说一个细节:压测时一定要开stream: True,因为线上业务走的是流式输出,首 token 时延和单 token 间隔更能反映用户真实体感。不开流式的话,vLLM 内部调度路径有一点差异,测出来的 TTFT 会偏乐观。

4.2 并发与吞吐数据:甜点区间在哪里

跑完两轮压测之后,数据非常有说服力。下面这个表是混合负载下的实测数字,每个并发档位稳定跑 10 分钟取的结果:

并发数QPS(请求/秒)总输出吞吐(token/s)TTFT 均值TTFT P95端到端均值端到端 P95单 token 间隔均值
80.28850.9s1.5s32.8s52s118ms
160.421261.2s2.4s38.2s58s142ms
240.441321.6s3.6s54.6s82s182ms
320.481452.1s4.8s67.4s105s243ms

先看吞吐曲线。并发从 8 升到 16,输出 token 吞吐从 85 涨到 126,大约提升了 48%。但并发从 16 升到 32,吞吐只从 126 涨到 145,提升了 15% 左右,明显开始触顶。这和我们前面按显存带宽算出来的理论天花板大致吻合——8 卡聚合 7.2TB/s 带宽,decode 上限大约在 170 到 180 token/s,145 token/s 已经达到 80% 左右的利用率。

这说明一个关键结论:并发 16 基本就是这个配置的甜点。继续加并发,QPS 还能微微涨一点,但代价是端到端时延全线恶化。

4.3 时延指标解读:为什么 32 并发时用户体感会明显变差

端到端均值的变化最直观。并发 8 时每个请求平均 32.8 秒完成,并发 32 时涨到 67.4 秒,翻了一倍还多。原因并不复杂:生成 300 到 800 token 的总量不变,但并发越多,每个请求能分到的生成速度就越慢。看起来 QPS 从 0.28 涨到 0.48,好像吞吐变大了 70%,但单个用户等的时间却从 33 秒拉到 67 秒,这个体验在很多业务里是不能接受的。

TTFT 同样值得关注。并发 8 时 TTFT P95 是 1.5 秒,到了并发 32 涨到 4.8 秒。TTFT 超过 3 秒,用户通常会认为服务已经卡住了。所以虽然 QPS 数据变好看了,实际业务侧的口碑是崩掉的。

我还单独测过只跑短问答负载的情况。短请求的 output 短,单个请求占用带宽的时间少,同样并发下能维持更高的 QPS,约 0.5 到 0.6 请求/秒。但一旦混入长总结请求,短请求的 TTFT 立刻被拉高,P95 从 1.5 秒跳到 2.8 秒。这个现象说明在 V100 这种带宽紧张的环境下,长短请求混跑是最典型的负面场景,后面优化部分我会给出对应解法。

5. 生产环境里的坑与微调:从能跑到跑得好

5.1 踩坑记录:这些问题都真实发生过

第一个坑是权重精度和 dtype 不匹配。最开始我直接加载了一个 bf16 权重的模型目录,启动时没报错,但生成的 token 全是乱码。排查了很久才发现是 V100 不支持 bf16,vLLM 在加载时做了某种 fallback,结果精度全毁。最后用转换脚本把权重统一转成 fp16 并配套 AWQ 量化后才恢复正常。这个坑特别隐蔽,强烈建议部署前先跑一个固定 prompt 的冒烟测试,确认输出语义正常再上压测。

第二个坑是CUDA graph capture 失败。症状是启动后服务看着正常,第一个请求进来就报Cannot find cudagraph之类的错误。我当时的处理是加--enforce-eager,先保证服务可用,后来排查发现是模型里某些动态 shape 操作导致 graph 捕获不完整。如果当时换一个 vLLM 版本,或者调整--max-num-seqs,也有几率跳过,但生产环境没时间折腾,直接 eager 更稳。

第三个坑是显存分配太激进导致的直接崩溃。把gpu_memory_utilization从 0.92 调到 0.95 之后,服务在加载阶段直接 OOM,连降级机会都没有。vLLM 的显存管理是按预设值一次性分配 KV cache,不像某些推理框架可以按实际负载动态伸缩。老卡的显存余量本来就小,这个参数必须保守。

第四个坑是并发过高时的 preemption 风暴。压测并发超过 32 之后,日志里大量出现Sequence group is preempted,TPOT 数据偶发跳到 500ms 以上。原因是 KV cache 不够用,vLLM 把旧的 sequence group 整体交换到 CPU 内存,再在下一轮换回来。这个行为对延迟是毁灭性的。解决方法是把--max-num-seqs控制在 32 以内,同时减少max-model-len到 6144,给 KV cache 留更多余量。

5.2 上线前我做的优化:限流、拆路、缓存

压测阶段的数据告诉我们,在 V100 上追求高并发是不现实的,所以生产架构上要顺着硬件的脾气设计。

我做的第一个调整是网关限流。vLLM 服务前面加了一层请求队列,在线并发总数限制在 16 左右。超出的请求在网关层排队,而不是直接怼到推理服务上。这样做的效果是服务端永远工作在甜点区间,TTFT 均值稳定在 1.2 秒左右,P95 也能控制在 3 秒以内。表面上看丢了几个 QPS,但用户感知的稳定性大幅提升。

第二个调整是长短请求分离。因为 8 卡是单实例部署,没法完全隔离,我在应用层做了超时分级:短请求限制在 30 秒内必须返回首 token,长请求允许 60 秒。调度层面再把一部分长总结任务放到低峰期异步跑。这样至少保证了短交易类请求不被长任务堵死。

第三个调整是prompt 缓存。业务里有一部分固定模板的请求,重复计算 prefill 是在浪费带宽。vLLM 支持 prefix caching,开启之后命中缓存时能跳过 prefill 阶段。实测对短问答场景能省掉大约 20% 的 TTFT,虽然总吞吐没变,但对用户体验的帮助非常直接。这个功能在老卡上尤其值得开,因为 V100 的 prefill 资源就那么点,能省一点是一点。

5.3 最终数据与判断:这套方案能支撑什么样的业务

优化之后我把混合负载的并发锁定在 16,重新跑了一轮稳态压测,最终指标如下:

指标优化前优化后
QPS0.420.42
TTFT P952.4s1.8s
端到端 P9558s49s
在线并发上限3216
长任务占比40%30%

优化后 QPS 没有提升,但延迟稳定性明显变好。这个方案的定位是:支撑对延迟有一定容忍度、但需要高生成质量的内部业务,比如异步内容生成、离线摘要、批量质检。如果业务本身是强交互客服,平均 30 多秒的生成时间无论怎么调都救不回来,那就确实要考虑换卡或换更小模型了。

个人体会,在 V100 上跑 70B 这件事,本质上是一场显存、带宽和调度策略之间的三方博弈。量化解决了显存问题,TP=8 和 KV cache 分配解决了并发空间问题,限流和 preemption 控制则守住了延迟底线。每一步都是在对 V100 的硬件脾气让步,但换来的是一个能用、稳定、可维护的实际业务入口。

如果你也守着几台 V100,可以按这个路径先复现一遍,再结合自己的业务请求分布去调并发和 max-model-len。最后提醒一句,多看看服务日志里的 preemption 和 TTFT 指标,这两个比 QPS 更能反映老卡跑大模型时的真实健康状况。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 12:24:59

AI Agent工程化实践指南:从系统设计到多智能体协作与安全落地

这两年AI Agent的变化,说实话比大模型本身的迭代还要大。大家一开始聊Agent,基本就是“给大模型套个循环,让它多调几次工具”;现在再看,已经变成了完整的一套系统设计:记忆怎么管、任务怎么拆、工具怎么接、多个Agent之间怎么协作、出了问题怎么追溯。这个领域已经从“跑通Demo…

作者头像 李华
网站建设 2026/10/20 12:23:43

DHCP不只是自动分配IP:企业级网络中的地址管理与故障排查

1. 为什么今天还要深挖DHCP——它远不止是“自动分配IP”那么简单你可能在刚接触网络时就听过DHCP这个词,老师说:“打开电脑就上网,靠的就是DHCP自动给你的IP地址。”后来自己配路由器,点开“LAN设置”,看到“启用DHCP…

作者头像 李华
网站建设 2026/10/20 12:23:20

CSS选择器实战指南:优先级、组合器与性能陷阱

你可能觉得CSS选择器很简单,不就是.和#吗?直到某天线上样式出了bug,你面对一长串选择器苦苦排查了几个小时,才意识到自己根本没有真正理解它的匹配逻辑。我从接手过几个中大型前端项目后,几乎把选择器相关的坑踩了个遍…

作者头像 李华
网站建设 2026/10/20 12:23:16

继续教育毕业论文写作难?十款AI论文工具按环节拆解与完整工作流

周末晚上十一点,某同学在群里发了一条消息:“谁能救救我,文献综述写了三天,导师说完全没有逻辑。”下面跟了一串同病相怜的回复。继续教育学生的毕业论文,向来不是学术能力问题,而是时间、方法和工具的三重…

作者头像 李华
网站建设 2026/10/20 12:22:39

交叉验证全攻略:从数据切分到嵌套CV的避坑指南

交叉验证是机器学习模型评估里绕不开的环节,很多人一上来就是cross_val_score,几行代码跑完拿个平均分就结束。但实际业务中,数据分布不均衡、样本存在分组、评估指标要同时看好几个、甚至要在调参时保持“公平公正”,单靠这一个函…

作者头像 李华
网站建设 2026/10/20 12:21:30

基于Django的宠物美容医院预约管理系统设计与实现

1. 项目背景与需求分析1.1 为什么想到做宠物美容医院预约系统先说说这个项目的来路。去年有个做宠物行业的朋友找到我,说他们那几家连锁宠物美容门店每天光是接预约电话就要耗费大量人力。顾客想预约上午洗澡,前台得翻纸质本子看哪个时间段还空着&#x…

作者头像 李华