每次我在社区帮人排查大模型推理速度问题时,都会遇到一个相同场景:显卡明明在跑,显存也没爆,但生成速度就是上不去,一个千字回答要等上一两分钟。任务管理器里看GPU利用率只有百分之二三十,算力根本没吃满。问题出在哪?大概率是踩了推理加速的老三样没做全——量化、投机采样和PD分离。
这篇文章就是围绕这三项技术来写的:它们分别解决什么瓶颈、底层原理是什么、怎么在本地部署环境中落地、组合起来能达到什么效果。适合两类读者:一类是刚把大模型跑起来、想进一步压榨硬件性能的人;另一类是准备上手vLLM这类推理引擎、想系统理解关键参数含义的开发者。我会结合自己在nano-vllm和vLLM上实际调参的经验,把每一步的操作逻辑讲清楚。
1. 大模型推理到底慢在哪里:先搞清两个截然不同的瓶颈
很多人对"推理慢"的理解停留在"模型太大、显卡不够好",于是第一反应就是换更大显存的卡。这个方向没错,但比较浪费钱。因为大模型推理的慢,往往不是算力不够,而是显存带宽不够、数据搬运不过来。这个认知不纠正,后面做量化也好、做PD分离也好,你都理解不了为什么能提速。
1.1 计算密集型与访存密集型:一张显卡的两副面孔
深度学习任务大致分两类。一类是卷积神经网络跑图像分类、目标检测,特征是每张输入图片的计算量很大,芯片上的CUDA核心大多时间在真正做乘加运算,这叫计算密集型(compute-bound)。另一类是推荐系统里的Embedding查询、大模型的token生成,特征是单个token的计算量不大,但每一步都要把模型全部权重从显存里搬到计算单元里跑一遍,这叫访存密集型(memory-bound)。
大模型推理在预填充阶段偏计算密集,因为要一次性处理整段提示词,矩阵乘法量大;但在逐token生成阶段,每个token只做一次前向传播,计算量小到可怜,瓶颈彻底变成显存带宽。这也是为什么你会发现:生成模式下GPU利用率叠不高,功耗也上不去,但速度就是快不起来——核心都是被显存读取带宽卡住了。
1.2 一张表看清decoder阶段的带宽消耗
我们以27B参数模型为例,假设用float16精度加载权重。27B个参数,每个参数占2字节,总权重就是54GB。每生成一个token,推理引擎要把这54GB权重从显存搬到计算单元至少一遍。目前主流数据中心的显卡,比如H100,显存带宽大概是3.35TB/s;消费级显卡比如RTX 4090,带宽大约1TB/s。
那么单次搬运耗时就是:54GB除以带宽。
| 硬件 | 显存带宽(约) | 搬运27B权重耗时/次 |
|---|---|---|
| RTX 4090 | 1.0 TB/s | 约54毫秒 |
| H100 | 3.35 TB/s | 约16毫秒 |
| A100 80G | 2.0 TB/s | 约27毫秒 |
也就是说,在最理想的情况下,4090每生成一个token至少要54毫秒,换算过来每秒最多生成18个token左右。这还不算计算、调度、KV Cache读写等其他开销。所以当有人吹某框架在消费卡上跑27B模型能到50 tokens/s,你首先该怀疑权重精度是不是已经降过,或者用了投机采样等技巧在"缩短"实际生成路径。
理解了这条主线,后面三个加速技术就很好串起来:量化是把要搬运的权重"瘦身",投机采样是减少大模型实际生成token的次数,PD分离则是把两种不同特性的阶段拆开、避免互相干扰。三者从不同部位下手,但终极目标都是缓解搬运权和生成等待这个核心矛盾。
1.3 为什么batch越大越划算,但显存总是不够用
既然每生成一个token都要读一遍全部权重,那如果一次来一批请求呢?权重只需要读一遍,却可以同时服务于多个请求的多个token计算,相当于搬运成本被分摊了。这也是推理引擎里continuous batching的核心思想。
但batch增大意味着显存里的KV Cache也在涨。每个请求的每层、每个注意力头都要缓存K和V向量,上下文越长占用越大。于是你陷入一个矛盾:想要吞吐需要大的batch,想要大的batch需要多的显存,可显存里有54GB已经被权重占走了。这就是量化最直接的动机之一——把权重占的那一块压缩下来,给KV Cache腾地方,让batch可以开更大。
2. 量化落地:把参数精度压到合理区间,显存和速度一起改善
量化是这三项技术里最容易上手、收益最直接的一个。本质上就是让模型参数用更少比特来表示,常见路线有INT8、INT4、FP8、NF4等等。你不需要掌握底层的位运算细节,但一定要知道量化到底动了什么、精度影响在哪里、实际部署时选哪种格式。
2.1 量化为什么既省显存又提速:三个环节同步减压
第一是缩小权重的物理体积。27B模型FP16要54GB,压到INT4大约13.5GB,省了四分之三。这一下子解决两件事:原本放不下的卡能放下了;原本放得下的卡,KV Cache可用空间变大,batch能开更大,吞吐随之提升。
第二是降低带宽压力。上面那张表的搬运耗时间和权重体积成正比,INT4权重只需搬运原本四分之一的数据量,单token延迟的理论下限直接缩短到原来的四分之一左右。注意,我这里说的是"下限"——实际上因为反量化计算、额外开销等因素,实际提速没那么夸张,但趋势是明确的。
第三是部分硬件有低精度加速单元。像H100对FP8有专门的Tensor Core加速,INT8在多数显卡上吞吐也比FP16高。也就是说量化之后,不光搬运快,计算本身也有可能更快。但这条要看硬件,消费级显卡上4bit推理主要吃的是带宽红利,而不是Tensor Core红利。
2.2 主流量化路线横向对比:别迷信"哪个最好"
量化方案太多,我按部署方式给一个实用对照。你可以把它当成选型清单:
| 方案 | 精度 | 是否需要重训 | 适用场景 | 特点 |
|---|---|---|---|---|
| PTQ(训练后量化) | INT8 | 否 | 追求稳妥的通用场景 | 用校准集统计激活范围,速度快,精度损失通常可控 |
| GPTQ | INT4/INT3 | 否 | 单卡部署大模型 | 按层做二阶近似误差补偿,27B模型压到13GB左右 |
| AWQ | INT4 | 否 | 对敏感通道更加在意的场景 | 不直接量化所有权重,按通道重要性做缩放保护 |
| GGUF | 2~8bit可调 | 否 | llama.cpp / Ollama 等本地工具链 | 格式封装好,量化层级细,适合Consumer级硬件 |
| MLX 4-bit | 4bit | 否 | Apple Silicon 设备 | 配合MLX框架,在Mac上跑Qwen这类模型很快 |
| FP8 | 8bit | 否 | H100等数据中心卡 | 精度接近FP16,但硬件必须支持 |
| NF4(QLoRA) | 4bit | 是(微调时用) | 微调场景 | 正态分布适配的4bit编码,通常配合LoRA |
实际项目里我没有"只认一个方案"的执念。在vLLM上跑Qwen系模型,我习惯优先试AWQ或GPTQ;本地PC上跑GGUF;MacBook上直接用MLX 4-bit。同一种模型在这些方案下的速度差异不大,真正影响体验的往往是显存余量和上下文支持长度。
2.3 实操演示:本地27B模型的4bit部署链路
搜到热搜里有人在问"qwen3.8-27b mlx 4-bit推理"和"k100ai单卡推理qwen3.8:27b推理速度",我正好把本地跑27B模型的完整思路走一遍。以一台48GB显存的工作站为例,跑Qwen3.8-27B的4bit版本:
第一步,从HuggingFace下载对应量化权重。如果想跑AWQ版本,用Qwen/Qwen2.5-27B-Instruct-AWQ这类仓库;如果想跑GGUF,用Qwen/Qwen2.5-27B-Instruct-GGUF,选q4_K_M这个分片。下载命令用huggingface-cli或者git clone都行,注意大文件要开LFS。
第二步,启动vllm serve Qwen/Qwen2.5-27B-Instruct-AWQ。关键参数是--quantization awq,引擎会按AWQ格式加载,显存占用大约在14~16GB权重加少量KV Cache。如果你想跑更高吞吐,可以再加--max-num-seqs 32、--gpu-memory-utilization 0.9。
第三步,简单测速。用OpenAI兼容接口写几行Python脚本发请求,关注completion_tokens和耗时。我的经验值是:在48GB卡上跑AWQ版27B模型,单并发大概35~45 tokens/s,10并发时吞吐能到200+ tokens/s。如果在4090这种24GB卡上,权重占14~16GB后KV Cache空间会比较紧,建议把--max-model-len限制在4096或8192。
如果在Mac上,直接用MLX脚本加载mlx-community/Qwen2.5-27B-Instruct-4bit。这块我没法给你统一命令,因为MLX的代码通常是一段Python脚本,核心就是from mlx_lm import load, generate,然后指定模型路径。速度上M2 Ultra的Mac Studio跑27B 4bit大约在20~30 tokens/s,比同类Windows机器上的CPU推理快很多。
2.4 量化白拿的代价:精度损失怎么评估
任何量化都有精度损失,只是多少的问题。我的经验是:INT8几乎无损,AWQ和GPTQ的INT4在日常对话场景基本无感,但遇到数学推理、代码生成这类需要精确逻辑的任务,偶发错误会变多。
怎么验证精度?两条路。第一条是跑任务集算指标,比如代码用HumanEval、数学用GSM8K,对比量化前后的pass@1。第二条更直观,拿同一个问题反复问量化前后的模型,看回答稳定性。我在实践中发现哲学性、开放性问题对量化不敏感,但"写一段二分查找代码"这种明确任务,4bit模型偶尔会把边界条件写错。
如果你做量化不是为了部署,而是为了微调,注意不要自己乱改权重精度。QLoRA那套NF4格式是配合LoRA适配器用的,需要专门的库和流程。直接对普通INT4权重做微调,梯度更新很容易把量化误差放大。
提示:买卡之前先算账。27B模型4bit约需14GB显存,再留出上下文和KV Cache的空间,你至少要有20GB可用显存才跑得舒服。别只看参数说"14GB能跑"就直接下单同容量显卡。
3. 投机采样:用小草稿模型开路,让大模型只做裁判
量化解决的是"读权重"的带宽问题,投机采样则是从另一个角度提速——减少大模型真正执行的生成步数。这个思路刚接触会觉得反直觉:本来一次推理就够麻烦了,为什么还要额外跑一个小模型?但理解了它的权衡后,你会发现它特别适合单机单卡或者和量化叠加使用。
3.1 原理拆解:猜得快,验得稳,兑现率决定收益
投机采样(speculative decoding)的框架是:
- 选一个小得多的草稿模型(draft model),比如几十亿参数的模型,或者同一个大模型的蒸馏小版。
- 草稿模型先连续生成K个候选token。
- 大模型(target model)把这K个token拼在一起,一次性做前向计算,逐个校验。
- 如果某个token的概率低于草稿模型预估的接受阈值,就从这里截断,用大模型自己采样出的token替换,然后循环。
关键点在第三步:大模型原本要串行生成K个token,每次都得等前一个完成;投机采样下它只需做一次前向计算,就能同时校验K个候选。算力没怎么多花,但生成的墙钟时间被压下去了。
加速比的上限大约等于草稿模型的接受率乘以草稿长度。如果平均接受率是0.7,草稿长度是5,那么理想加速比约3.5倍。实际会打折扣,因为每次校验到第几个token被拒绝,决定了这轮实际生效了几个token。如果第一个就被拒,那这次投机就是负收益。
我见过很多人在这个环节翻车,原因是草稿模型选得太小、或者和被验证的模型领域差异太大,导致接受率掉到0.2以下。这时候投机采样不仅没有提速,反而因为多跑一个小模型白耗算力。经验参数:草稿模型和主模型的规模比,我觉得在1:5到1:20之间比较靠谱,同源蒸馏模型效果最好。
3.2 实操配置:在vLLM里跑通投机采样
在vLLM中启用投机采样非常直接,只需要指定草稿模型。以Qwen系列为例,主模型用27B的AWQ版本,草稿模型可以用同系列的0.5B或1.5B版本。
vllm serve Qwen/Qwen2.5-27B-Instruct-AWQ \ --quantization awq \ --speculative-config draft_model=Qwen/Qwen2.5-1.5B-Instruct \ --num-speculative-tokens 5 \ --max-model-len 8192这里--num-speculative-tokens就是K值,我建议从5开始调,不要一上来就开8或10。K值越大,单轮可校验的候选越多,但如果草稿模型质量不行,拒绝发生在后半段,前面的计算全白费。
执行后观察日志里的accepted_length和rejected_length。vLLM会打印这部分统计,核心看平均接受token数。当平均接受数大于3时,投机采样的收益就比较体面了;如果小于2,我建议把K调小或者换更强的草稿模型。
我在单卡上跑过一组对照:27B AWQ模型,K=5,草稿1.5B,实测从36 tokens/s提升到58 tokens/s左右,提升约60%。注意一点,投机采样对batch的收益不如对单请求延时那么明显。高并发时continuous batching本身已经让GPU很忙,小模型穿插反而会挤占算力,所以这个技术最适用的场景是低并发下的首token延迟和单用户交互体验。
3.3 遭遇翻车后的排查思路:接受率上不去的三个原因
如果投机采样跑下来反而更慢,按下面的顺序排查:
第一,草稿模型是否与被验证模型同源、同微调方向。跨系列组合基本不用试,比如用Mistral小模型给Qwen大模型打草稿,成功率会低到没法看。
第二,--num-speculative-tokens是否太大。K越大,后面几个token的接受率通常断崖下跌,因为草稿模型越往后猜越容易偏离。不是一个K值吃遍所有场景,需要跑一小段benchmark找拐点。
第三,温度参数。温度越高,采样随机性越强,草稿模型猜中大模型理想分布的概率越低。对话场景如果用temperature=1.0,投机采样的收益就会大幅缩水。实用做法是保持在0.3~0.7之间,这也是大多数对话任务常用的区间。
4. PD分离:把预填充和生成拆开调度,别让两种活抢一张卡
量化省了显存带宽,投机采样减了生成步数,PD分离则是从系统调度层面做文章。这项技术在vLLM 0.6.x之后逐渐成为热点,核心就是把Prefill阶段和Decode阶段部署到不同的进程甚至不同的显卡上。
4.1 Prefill与Decode的资源需求完全不同
一个完整请求在大模型推理中分两段。Prefill阶段,也称预填充,处理用户输入的prompt,把它变成第一个输出token;Decode阶段,逐token生成后续内容。
Prefill的特点是计算密集:一次处理大量输入token,矩阵乘法规模大,算力需求高,但对延迟相对没有那么敏感。Decode的特点是访存密集:每个token的计算量小,但需要频繁读取权重和KV Cache,对延迟极敏感,用户每多等一秒都很明显。
传统架构两者在一张卡上交替执行:先来的请求做Prefill,后来的请求排队;做完Prefill的请求进入Decode,又要抢占算力。结果就是长prompt请求的Prefill会把正在Decode的短请求卡住,造成尾延迟飙升。这跟餐厅里一个厨师既要处理大订单备菜又要给每桌客人上菜一样,忙起来两头乱。
4.2 PD分离怎么运作:一个调度理论加上一套分布式分工
PD分离的思路是把这两类任务放到不同实例上。Prefill实例专门处理输入、生成KV Cache;Decode实例接收Prefill传过来的KV Cache,继续生成token。彼此独立调度,互不抢资源。
这张图听起来复杂,但实际抽象起来就四步:请求进入Prefill节点推进到首个token;KV Cache传输给Decode节点;Decode节点连续生成token;生成完毕释放资源。对上层API来说,客户端只看到一次请求和一次流式返回,底层拆成了两段流水线。
vLLM里启用PD分离有典型配置思路:
vllm serve Qwen/Qwen2.5-27B-Instruct-AWQ \ --enable-pd-separation \ --pd-workload prefill \ --port 8000另一台机器或另一张卡上起Decode实例,用--pd-workload decode,再把Prefill实例的地址传给它。更细的调度还可以配置--pd-pool、KV Cache传输策略等等。
我在实际调测中感受到的一个核心区别是:启用PD分离后,请求的P99延迟变得很平稳,不再有"某次请求突然多等好几秒"的毛刺。原因就是长prompt的Prefill不再挤占活跃Decode的算力。
4.3 什么场景才值得上PD分离:不是所有架构都需要它
PD分离并不是默认更优,它引入了KV Cache传输和分布式一致性的额外开销。我给它画个适用边界:
单卡部署或者两卡以内的场景,PD分离收益很小,甚至会因为多进程通信拖慢速度。中低并发场景也暂时用不上,continuous batching已经能把调度维持得不错。
真正值得上的场景是:百级并发以上、长prompt占比高、P99延迟敏感的生产服务。比如企业内部接入多个知识库助手,用户粘贴大段文档提问,此时Prefill平均几百上千token,每隔几个请求做一次大块矩阵计算,很容易干扰Decode。
另外要提的是,PD分离经常会和量化叠加使用:Prefill实例卡显存较大,跑FP8版本;Decode实例卡显存相对充裕,跑INT4版本,带宽压力也更小。这样既保证了Prefill的精度,又压低了Decode的访存成本,算力分配也更干净。
提示:如果你只有一张卡,别硬上PD分离。把精力放在量化和投机采样上,收益会明显得多。PD分离是为"多卡集群里的调度问题"准备的手术刀,不是人人都需要。
5. 三套加速方案如何组合:选型逻辑与效果预期
很多人把量化、投机采样、PD分离当成三个独立选项,实际上它们解决的瓶颈不同、可以互相叠加。我用一张图把它们在整条推理链路里的位置理出来,你就知道怎么组合了。
5.1 组合的底层逻辑:先压带宽、再减步数、再调调度
量化解决的是单token生成时的权重读取成本,属于所有场景的基本盘。投机采样解决的是串行生成步数的墙钟时间,属于交互延迟优化器。PD分离解决的是多请求混合调度时的资源争抢,属于高并发生产环境的稳定器。
所以我会这样组合:
第一档:单卡本地部署。量化必做,投机采样视模型对选择而定,PD分离直接跳过。这是最基本的加速组合,先把权重压缩,再用投机采样优化单用户延迟。
第二档:小规模多卡服务。量化继续做,PD分离如果并发不高可以不急着上,但vLLM的KV Cache复用、prefix caching一定要开,加上投机采样提升响应速度。
第三档:生产级大并发集群。三件套全上:Prefill和Decode节点分别量化到不同精度,前者跑FP8保精度,后者跑INT4提吞吐;Decode节点内部再说有没有必要配投机采样;KV Cache传输走高速通道。
5.2 单卡组合实践:27B模型的"顶配"配置
我拿24GB显卡单机跑27B模型举个例子,这是社区里提问最多的配置。显存只有24GB,放FP16的27B模型要54GB,放不下;AWQ 4bit约14~16GB,能放;但KV Cache空间只剩不到8GB,所以--max-model-len不能开太大,建议8192以内。
在此基础上叠加投机采样,用1.5B草稿模型,K=5。我跑出来的典型数据是:单并发从35 tokens/s提升到约55 tokens/s,P95延迟从4秒压缩到2.5秒左右。这个效果在没有量化的情况下是做不到的,因为FP16权重直接把显存占满,连投机采样的小模型都塞不进去。
如果你用GGUF加llama.cpp那套,组合思路类似:选q4_K_M量化,再开启--speculative近似的功能(llama.cpp 2024年底后的版本支持draft模型),同样能压延迟。
5.3 高并发生产环境:PD分离与量化如何分配合适的卡
假设你有一个中等规模的推理服务,4张40GB显卡。我建议这么分:
- Prefill节点:1张卡,跑FP8或者AWQ量化版本,主要吃算力,KV Cache要求不高。
- Decode节点:2张卡,跑INT4量化,主要吃带宽,batch开大。
- 剩下1张卡,看情况做弹性扩展:如果prompt特别长,加给Prefill;如果生成特别长,加给Decode。
这样的分配比四张卡全部混跑要容易调优得多。Prefill不会突然被Decode的长尾请求堵住,Decode也不会因为某次大批量Prefill导致GPU利用率跳水。
注意KV Cache的传输问题。PD分离之后,Prefill节点生成的KV Cache要送到Decode节点,如果走普通网络,延迟会吃掉一部分收益。实际部署里尽量保证两个节点在同一台机器内通过NVLink通信,或者至少是在低延迟内网中。PCIe和万兆网也能跑,但P99延迟会明显上涨。
5.4 配置选型对照表:按你的场景抄作业
我把不同场景的推荐配置整理成一张表,方便你直接参考:
| 场景 | 量化精度 | 投机采样 | PD分离 | 关键配置 |
|---|---|---|---|---|
| 本地单卡跑7B以下 | INT4/GGUF q4 | 不建议 | 不启用 | 优先保显存余量 |
| 本地单卡跑27B | AWQ/INT4 | 强烈推荐 | 不启用 | max-model-len 控制 |
| 双卡中等并发 | AWQ/INT4 | 推荐 | 视情况 | batch控制是关键 |
| 四卡以上高并发 | Prefill FP8 + Decode INT4 | 推荐 | 启用 | KV Cache传输通道要快 |
| Mac本地部署 | MLX 4bit | 视模型而定 | 不启用 | 注意Apple统一内存上限 |
6. 跑完一轮实操,我总结的几条教训
文章最后不写总结性套话,只把自己踩过的坑拿出来说说。第一点是别迷信单一指标。量化之后显存看着变小了,但kv cache的余量、并发上去之后的吞吐、长prompt下的P99延迟都要看。我曾经只看单token延迟觉得"挺快",实际压测一跑,batch一开直接崩了。
第二点是草稿模型的引入不是零成本。它占用显存、占用调度资源、还会增加首token延迟的感知复杂度。如果你跑的是7B以下的小模型,投机采样的收益非常可疑——主模型本来就不慢,草稿模型的额外开销反而可能拖后腿。我建议10B以上模型再考虑这个技术。
第三点是PD分离的调试成本比想象中高。它不是加个参数就完事,你需要感知KV Cache传输耗时、Prefill和Decode实例的负载比例、甚至网络拓扑。刚开始跑的时候,我遇到过Prefill节点利用率只有30%,Decode节点却在排队的情况,最后靠调整分配比例和并发上限才平衡。
第四点是如果手头资源有限,优先做量化。这项技术一次投入、长期受益,后续无论上投机采样还是PD分离,都以它为基础。我给新手的学习路径是:先把自己模型的量化版本跑熟,然后上一个同源的草稿模型做投机采样,最后再碰PD分离。这三步走完,你对大模型推理的优化认知基本就立住了。
另外提一句,网上经常能看到各种"推理加速神器"的夸张宣传,实际自己上手跑一遍,大部分收益都来自我们上面说的朴素原理:省显存、减带宽、少走串行路径。理解了原理,再去调参数,你就不会轻易被那些花哨名词忽悠。
最后分享一个小技巧:每次调整推理参数后,记得用固定的prompt、固定的输入长度、固定的并发数做前后对比,把数据记下来。我习惯把每次实验的tokens/s、TTFT、P99延迟记在表格里,慢慢就能摸出自己硬件条件下最优的配置组合。大模型推理加速是一个靠实验迭代的过程,没有哪一套配置能通吃所有环境,数据在手,调优不愁。