大模型推理加速这事儿我最近大半年一直在跟。从最初老老实实跑 FP16到给模型做量化再到折腾投机采样和 PD 分离一路踩坑一路补课总算把这套组合拳给打明白了。先给结论这三项技术不冲突它们分别从数据体积、解码步数、资源调度三个角度下手条件允许的情况下完全可以叠加使用。这篇文章不是论文复述而是我实际部署过程中的理解和方法总结。适合已经在用 vLLM、llama.cpp 这类工具跑过模型、但还想往底层多挖一层的人也适合正在为线上推理延迟和吞吐量发愁、准备优化部署方案的人。我会把每个技术讲清楚原理给出选型和参数上的参考再把我踩过的坑原原本本摆出来希望能帮你少走弯路。1. 先把推理慢这件事拆开看1.1 自回归解码一次一个 token 的“挤牙膏”大模型生成文本的方式很多人以为它像人一样“想好整段再说”实际上它是一个字一个字往外蹦的。每生成一个新 token模型都要基于已经生成的全部内容做一次前向计算然后从词表概率分布里挑一个 token 输出。这个过程叫自回归解码天生是串行的。这意味着一个很现实的问题生成 100 个 token就要做 100 次前向计算。每次前向计算虽然是“增量”的利用 KV cache 避免重复算历史 token但依然要读取全部模型权重、更新缓存、输出概率分布。你把模型想象成只知道“下一个字是什么”的助手你说一句它猜一个字然后把这个字记下来再猜下一个字。这个“只能一步一步来”的解码方式就是大模型推理延迟的根本来源。推理过程内部还分成两个截然不同的阶段。Prefill预填阶段处理用户输入的那段 prompt所有位置的 token 可以并行计算Decode解码阶段则是逐 token 生成只能串行。这两个阶段对算力和带宽的需求完全不同后面讲 PD 分离时还会重点展开。1.2 显存带宽才是真正的瓶颈很多人第一反应是大模型慢是因为“计算量太大”但实测下来尤其是 Decode 阶段真正的瓶颈是显存带宽。拿一张 A100 80GB 举例它的 HBM 带宽大概是 2TB/s。一个 7B 模型按 FP16 存权重大约 14GB。每生成一个 token至少要访问一遍这 14GB 的权重光是搬运这些数据就需要 14GB 除以 2TB/s算下来约 7 毫秒。GPU 的计算单元大部分时间都在等数据从显存搬进寄存器算力再高也没用数据喂不进来。这个估算很粗糙实际还会叠加 KV cache、batch、碎片化读取等因素但它揭示了一条重要规律Decode 阶段的耗时基本由“每生成一个 token 需要从显存读多少字节”决定。只要能减少每次读取的数据量Decode 速度就能近乎线性地提升。这也是为什么量化能成为最直接的加速手段——它把每次搬运的数据体积直接砍掉一半甚至四分之三。1.3 三条加速路线背后的共同主线理解了瓶颈再看三条主流路线的逻辑就非常清晰了量化减小每次读取的数据体积把权重从 FP16 压到 INT8/INT4顺带把显存占用降下来让更大的 batch、更大的模型能塞进单卡。投机采样减少目标模型被调用的次数。用一个草稿模型连续猜一串 token让大模型一次前向验证多个 token相当于一次计算换来了好几个 token 的产出。PD 分离把 Prefill 和 Decode 拆到不同的 GPU 资源池避免两类任务互相干扰让整体调度更稳定延迟指标更可控。这三条路线并不互斥因为它们动的是不同的变量量化改数据体积投机采样改解码步数PD 分离改任务调度方式。把这层主线理清楚后面每一个技术细节都不会再被看成孤立的 trick而是同一盘棋上的不同落子。2. 量化给模型做减法收益最直接的加速2.1 量化在做什么精度、数值范围与缩放因子量化的本质是降低数值精度。训练好的模型权重大多数是 FP16 或 BF16每个数占 2 字节量化成 INT8 变成 1 字节INT4 变成 0.5 字节。显存占用直接按比例下降每 token 需要搬运的字节数也跟着下降Decode 速度自然就上来了。但“降精度”不是简单地把高精度数值截断成低精度整数中间要解决两个问题数值范围怎么对齐精度损失怎么控制。模型权重的值通常集中在零点附近的小范围内但偶尔会冒出一些绝对值很大的 outlier。如果直接把整个 tensor 的数值范围映射到 INT8 的 [-128, 127]大多数普通数值会损失大量精度。所以实际操作中要引入 scale缩放因子和 zero point零点把原始数值映射到目标精度能表达的范围内。映射的粒度越细精度损失越小——按整个 tensor 量化最粗糙按 channel 量化好一些按 group比如每 128 个权重一组量化效果更好但反量化开销也更大。我自己的理解是量化本质上是在“数值表达的精细度”和“数据体积”之间做权衡。group size 从 128 降到 64 或 32生成质量通常会变好但对应的计算开销上升推理速度可能反而下降。这不是玄学而是明摆着的工程取舍实际使用时要针对你的模型和硬件跑一组对比再定。2.2 主流量化方案怎么选目前实际部署中用得最多的几类方案我把它们整理成一张表方便对照选型方案精度特点适用场景GPTQINT4 / INT8基于二阶信息逐层量化需要校准集单卡部署大模型vLLM 支持成熟AWQINT4按激活值重要性保护关键权重通道与 GPTQ 同场景通常精度更稳SmoothQuantINT8 权重激活把激活值里的 outlier 迁移到权重侧需要同时量化激活的在线服务FP8FP8E4M3/E5M2训练/推理同精度H100 等新卡支持好高端显卡、训练后直接部署GGUF Q4_K_MINT4 混合llama.cpp 生态量化格式成熟本地 CPU / 边缘设备 / 个人部署我个人的选型经验如果模型要在 vLLM 上跑优先看 GPTQ 和 AWQ如果走 llama.cpp 或者需要在 Mac、CPU 上跑直接上 GGUFFP8 是高配集群里很自然的选择因为不用做太复杂的校准部署链路最省事。各方案之间的差异没有网上说的那么大很多时候决定成败的是校准数据质量而不是量化算法本身。2.3 量化提速与显存收益的实测参考说几个我在 A100 40GB 上、7B 模型、batch 为 1 的条件下的实测数据供参考FP16 基线Decode 约 20-25 tokens/s。INT8约 35-40 tokens/s。INT4GPTQ约 55-65 tokens/s。需要说明的是不同框架、不同 GPU 后端的数字差异会很大但这些数据至少说明一件事量化在 Decode 阶段的收益是实打实的。Prefill 阶段提速效果通常没那么夸张因为 Prefill 是计算密集型任务INT4/INT8 的反量化开销在某些硬件上反而会让 Prefill 变慢。这也是为什么一些部署方案会做“选择性量化”只优化 Decode 而不动 Prefill。显存收益同样关键。7B 模型 FP16 要占约 14GBINT4 只要 4-5GB省下来的显存可以留给更长的上下文、更大的 batch甚至直接换一个更大的模型。这个收益在成本上的价值比速度收益更直接尤其是你按显存租卡的时候同一张卡能“装下”的模型和并发量直接翻倍。2.4 量化最容易踩的四个坑第一个坑是校准数据选得不对。GPTQ、AWQ 都需要一小段文本做校准用来统计权重分布。如果校准数据和真实业务分布差异太大比如用英文通用语料校准线上全是代码或合同文本那么量化后效果会肉眼可见地变差。我的建议是至少从线上日志里捞 512 到 1024 条真实 prompt 做校准别偷懒用现成的 c4 样本。第二个坑是只看 perplexity 不看下游任务。perplexity 涨了 0.1 看起来不多但某些对格式敏感的生成任务——代码补全、JSON 输出、工具调用——可能直接崩。量化后一定要拿关键下游任务做回归尤其是结构化输出类场景。第三个坑是激活值量化比权重量化难得多。权重的分布相对稳定激活值会随输入剧烈变化尤其是存在大量 outlier 时。所以很多方案只做 weight-only 量化这不是偷懒而是激活量化通常需要 SmoothQuant 之类的预处理否则损失无法接受。第四个坑是 KV cache 量化。长上下文场景下 KV cache 占显存很大量化成 INT8 能省不少显存但误差会随着序列长度累积长上下文下质量衰减会很明显。建议先上权重量化KV cache 量化作为后续优化项并且务必在最长上下文长度上做质量回归。这几个坑我全踩过。最典型一次是量化完模型生成 JSON 总是缺字段排查半天发现校准集里全是散文后来换成业务真实 prompt问题立刻就消失了。3. 投机采样用小模型换大模型时间3.1 为什么一个“笨”模型能帮上忙投机采样Speculative Sampling的思路很有意思与其让大模型一个字一个字慢吞吞地蹦不如让一个小模型先按自己的判断连续写出一串 token然后让大模型一次性验证这一整串。因为前面说过大模型 Decode 是访存瓶颈验证一串 token 的耗时和验证一个 token 差不多所以只要小模型猜得够准大模型一次前向就能“白赚”好几个 token。为什么小模型“猜得准”这件事是可行的因为自然语言里大量 token 的预测根本不难。“床前明”后面大概率是“月光”“def pair”后面大概率跟变量名“根据以上分析结论是”这种套路化表达在模型输出里更是常见。大模型在这些“送分题”上花的时间和难题上差不多这本身就是一种浪费。让一个小模型把这些容易的 token 先顺下来大模型只负责把关等于把算力集中到真正难的地方。更关键的是数学保证只要草稿模型和目标模型对 token 分布的判断不是完全对立通过拒绝采样rejection sampling做验证最终输出分布可以做到和直接用大模型采样完全一致。也就是说投机采样是少数能做到“无损加速”的方案不会因为加速而牺牲生成质量。3.2 草稿验证拒绝采样的完整流程标准流程分成四步这里用实际例子说清楚草稿模型自回归生成 k 个候选 token。比如 k4小模型先写出“中国的首都是”接下来四个 token 它猜“北、京、是、一”。把这 4 个候选 token 接到大模型输入的末尾做一次前向计算。因为是一次处理 4 个位置大模型一次就能拿到这 4 个位置各自的目标分布。从第一个候选 token 开始逐个做拒绝采样验证如果目标分布对“北”的概率足够高通过随机数判断后接受继续看“京”如果某个 token 不达标就在这个位置用目标分布重新采样一个 token 作为修正后面未验证的候选全部丢弃。被接受的 token 全部作为输出从修正 token 的位置开始重新进入下一轮草稿生成。关于第 3 步要特别强调一点接受条件不是“草稿模型 top-1 等于目标模型 top-1”而是按概率接受。目标模型给出的分布中某个 token 被采样到的概率高就更容易被接受。这样即使草稿 token 不是目标模型最可能的 token只要它落在高概率区域仍然可能被保留从而保证最终输出分布不被扭曲。我在本地把整个流程跑通时感触最深的一点是草稿模型和目标模型的分歧越大接受率越低加速比越小如果两个模型完全一致每一轮都能验证全部 k 个 token速度会无限接近草稿模型本身——当然这种情况不太可能发生。3.3 加速比怎么估算假设每个草稿 token 的接受概率是 α为便于理解先按独立近似每轮草稿长度是 k那么每轮能产出的 token 数期望约为E ≈ (1 - α^(k1)) / (1 - α)这个公式看着唬人算一下就明白了。比如 α0.7k4那么 E ≈ (1 - 0.7^5) / 0.3 ≈ 2.77。意思是大模型每做一次前向平均能产出接近 2.77 个 token而传统解码一次前向只产出 1 个理想情况下端到端速度就是原来的 2.77 倍。但有两个现实修正必须考虑。第一草稿模型本身也要耗时如果草稿模型太大它生成 k 个 token 的时间会把收益吃掉大半。第二α 不是一个常数序列开头、中间、结尾的 token 难度差别很大遇到代码、公式、罕见词多的文本接受率会明显下降。所以实际加速比通常落在 1.5 到 3 倍之间宣称能到 4 倍以上的基本是特定数据集上的理想值。坦白说我第一次用 7B 主模型配 0.5B 草稿模型时实测只有 1.6 倍左右一度怀疑是自己实现有问题。后来换了同家族的草稿模型接受率从 0.5 左右涨到 0.7 附近加速比才到 2 倍以上。模型家族的匹配度比草稿模型本身的“聪明程度”更重要。3.4 草稿模型选型与调参心得草稿模型的选型是最关键的工程决策。我的经验是优先选和主模型同一家族、尺寸更小的模型比如 7B 主模型配 0.5B 或 1B 的同系列模型。原因有两个一是 tokenizer 一致不用处理词表映射二是同族模型的生成风格和分布更接近接受率更高。跨家族搭配不是不行但要额外处理 tokenizer 对齐问题中间能踩的坑非常多。几个调参建议k 值不是越大越好。k 太大草稿模型生成时间变长而且长串候选很容易在中间段被拒边际收益递减。我一般从 4 开始试往上调到 6 收益就明显变小了。采样参数必须匹配。如果目标模型用 temperature0贪心解码草稿模型也得用贪心否则两者的分布对不上接受率会暴跌。草稿模型同样建议量化。一个 1B 模型虽然不大但加上 KV cache 后显存成本也不可忽视INT8 量化后收益明显。另外如果你的场景是代码补全、RAG 检索结果生成这类“文本高度可预测”的任务可以试试 prompt lookup decoding——直接从上下文里找重复片段当草稿完全不需要额外的草稿模型零额外推理成本效果经常出奇地好。4. PD 分离把 Prefill 和 Decode 彻底拆开4.1 Prefill 和 Decode 是两种“性格不合”的任务前面多次提到 Prefill 和 Decode 两个阶段这里展开讲讲它们为什么“性格不合”。Prefill 阶段要并行处理整个 prompt。用户输入 2000 个 token模型要一次性把这些 token 都算一遍计算量巨大、访存量相对少属于典型的计算密集型任务。它的特点是可以用大 batch 把 GPU 的 Tensor Core 喂饱耗时随 prompt 长度近似线性增长但一次能同时算很多 token。Decode 阶段完全反过来。每生成一个 token只做增量计算主要开销是读取模型权重和 KV cache属于访存密集型任务。它需要的是高显存带宽而不是超高的浮点算力而且延迟极其敏感——用户对“一个字一个字往外蹦”的速度变化感知非常明显。对比一下硬件偏好就清楚了Prefill 想要更多算力Decode 想要更多带宽。这两种需求同时压在一张卡上本质上是让一张卡同时干两种不相干的活谁也别想干痛快。4.2 混在一起跑为什么会互相拖累单用户、单请求的情况下Prefill 和 Decode 自然串行问题不大。但线上服务几乎都是多并发的一个请求在 Prefill另外几个请求在 Decode。如果调度器把这两类任务塞进同一个 batchPrefill 任务会抢占大量计算资源导致 Decode 的 token 产出被拖慢用户感受到的就是“打字卡顿”。反过来Decode 任务也会挤压 Prefill 的算力导致首 token 时间TTFT变长。最典型的表现是并发一上来TTFT 和 TPOT每个 token 的产出间隔互相拉高谁也满足不了服务等级协议SLO。PD 分离的核心思想就是把 Prefill 和 Decode 拆到不同的 GPU 池甚至不同的机器上。Prefill 池用算力强的卡Decode 池用带宽高的卡中间通过传输 KV cache 完成交接。这样两类任务各干各的资源不互相抢各自的延迟指标都能稳住。4.3 工程落地拆池、调度与 KV cache 传输具体落地大致分为四步把 GPU 集群划分成 Prefill 池和 Decode 池各自跑独立的推理实例比如分别启动两个 vLLM 实例。Prefill 实例处理完整条 prompt 后得到该请求对应的 KV cache通过高速网络RDMA 优先传给 Decode 实例。Decode 实例加载 KV cache 后从第一个 token 开始逐 token 生成回复。由一个轻量调度器负责把请求分发给不同的池统一管理实例生命周期和负载。这个架构的难点集中在两个地方。第一个是 KV cache 传输延迟网络速度直接决定端到端延迟KV cache 越大传输越慢所以长上下文场景对网络要求更高。第二个是池间负载均衡Prefill 池太快会把请求全部倾泻给 Decode 池让 Decode 池过载太慢则会把 Decode 池饿死。工程上一般通过预分配缓存池、动态扩容、准入控制等手段来缓解。vLLM 等开源项目已经提供 PD 分离的实验性支持我在测试环境里跑通过配置步骤并不复杂。但要真正在集群里稳定运行网络、调度、容错的细节比单机推理多得多。如果是单机双卡或者个人研究环境真没必要一上来就上这套收益不明显还徒增运维复杂度。4.4 什么场景值得上 PD 分离我的判断标准很简单看你的性能目标里是否同时要求很低的 TTFT 和很低的 TPOT并且并发量足够大。如果只是个人部署、离线批量推理、或者在单块 GPU 上自用PD 分离的收益几乎为零如果是线上多租户服务、有明确的 SLO 承诺、GPU 规模在几十张以上PD 分离就是最值得做的架构改造之一。配套动作也要跟上prefix caching公共前缀复用 KV、动态 batch 大小、请求准入控制这些和 PD 分离配合起来才能发挥最大价值。PD 分离不是银弹它是把“资源隔离”做到极致的思路能不能发挥价值取决于你的流量模型是否足够“多而杂”。流量不够大、需求不够复杂的场景上了 PD 分离只会增加系统复杂度徒增排查问题时的难度。5. 三招组合起来用以及我的避坑记录5.1 组合优先级与叠加收益如果只允许我先做一件事我做量化。理由很实在收益直接、改动小、几乎适用于所有场景而且量化后的模型还能让后续的投机采样和 PD 分离都受益。第二件事做投机采样因为在 Decode 阶段它能再拉一到两倍而且对已有架构基本无侵入。最后才考虑 PD 分离因为它是架构级改动适合流量和规模都起来之后再动。组合使用时的收益是可以叠加的量化缩短每 token 的生成时间投机采样减少需要生成的 token 步数PD 分离让整体调度更稳定。三者乘起来效果很可观——量化带来 2 倍、投机采样再带来 2 倍、PD 分离解决稳定性问题叠加之后单卡能承接的并发和吞吐完全不在一个量级。不过我也要泼一盆冷水组合越多排查问题的难度越大。量化后的精度变化、投机采样的接受率波动、PD 分离下的网络延迟抖动任何一个环节出问题表现都可能是端到端延迟升高。如果你对系统的每一层都没有足够把握建议先把量化这一层做扎实再逐步叠加。5.2 基准测试别只盯着吞吐量跑 benchmark 时最容易犯的错是只看 throughputtokens/s忽略延迟分布和生成质量。我自己的习惯是同时记录四个指标缺一不可TTFT用户等待首 token 的时间Prefill 慢或者调度不稳定都会让它飙升。TPOT相邻两个 token 的产出间隔Decode 慢或者被其他任务抢占时它会变大。端到端吞吐一定并发下的总体 tokens/s反映系统整体产能。质量回归相同 prompt 集合下的输出对比哪怕只是人工抽检也要做。我遇到过的情况包括量化后速度上去了但输出开始出现格式错误投机采样开启后接受率低到几乎没收益PD 分离后吞吐好看但 TTFT 抖动变大。只测一个指标这些问题一个都发现不了。测延迟时也别只看平均值p95、p99 才能真正暴露调度和抢占问题。5.3 我的个人经验与建议最后分享一点经验。学习这三项技术最好的方式不是先啃论文而是先动手。把 vLLM 或 llama.cpp 跑起来对一个 7B 模型做 INT4 量化对比速度变化再加一个小草稿模型跑投机采样观察接受率最后有条件再尝试 PD 分离。每一步都亲眼看数据变化比读十篇综述都管用。我自己就是从“量化会不会严重损失质量”的怀疑开始到把三个技术串成一套完整部署方案前后用了不到一个月。最大的体会是这些技术没有一个要求你成为数学天才才能用但每一个都要求你理解瓶颈在哪。想不清楚瓶颈出了问题连从哪里排查都不知道想清楚了你会发现这些加速手段本质上都在做同一件事——让 GPU 每一次计算都花在刀刃上。
阅读完成 · 觉得有帮助?