
把一个大模型真正跑起来之后你会发现最难的不是调 API也不是整理数据而是推理速度。我在本地部署一个 7B 模型的时候每秒十几个 token 的生成速度勉强能忍一旦换到 13B 甚至 70B 量级的模型生成一句话要等上十几秒。这种体验基本没法落地尤其在上线服务之后用户的耐心全是按毫秒计的。所以过去一段时间我集中研究了推理加速这条链路把最核心的三板斧——量化、投机采样、PD 分离——从原理到部署完整过了一遍。这篇文章就是我梳理出来的学习笔记和踩坑记录适合正在做模型部署、想优化线上推理耗时、或者准备深入研究推理引擎的朋友参考。这条路不只是跑实验室里的 benchmark它直接决定你能不能把模型低成本地用在真实业务里。先给个结论量化解决的是“模型能不能塞进显存”和“带宽能不能跟上”的问题投机采样解决的是“token 出得太慢”的问题PD 分离解决的是“服务一忙就互相卡死”的问题。三者互相独立但实际部署时经常组合使用。下面我按思路拆开讲。1. 先聊清楚大模型推理为什么慢加速到底在加速什么1.1 Prefill 和 Decode 是两种完全不同的瓶颈大模型生成回答的过程分成两个阶段。第一个阶段叫 Prefill也就是预填充用户把问题发过来模型把整段输入一次性跑一遍计算出中间的 KV Cache键值缓存。第二个阶段叫 Decode也就是解码模型一个一个地生成 token每一步都依赖前一步输出的结果这个串行逻辑没法直接并行。这两个阶段的瓶颈完全不一样。Prefill 阶段因为输入的 token 可以并行计算考验的是 GPU 的算力说白了就是看你有多少 TOPS/TFLOPS。而 Decode 阶段每一步都只生成一个 token计算量不大但要把模型全部的权重读一遍还要读写不断增长的 KV Cache所以瓶颈在显存带宽上。我一开始没意识到这个区别直到用工具跑 profiling 才发现同样是生成 100 个 tokenDecode 阶段的显存占用看着不高但时间占比远大于 Prefill。这也是为什么后面所有加速手段本质上都在跟这两个瓶颈做斗争要么把要搬的数据变小要么把串行的过程找机会并行一下。1.2 三类加速技术各自解决的痛点用一句话归纳三类技术的分工量化是给模型“减重”投机采样是给解码“找一个能并行加速的替身”PD 分离则是把两个瓶颈分开各自用最适合的硬件和调度方式处理。把这三点放在一起看正好对应推理服务的三层问题显存放不下、token 出得慢、并发一高就抖动。很多文章喜欢把这三个技术摆在同一个页面上讲但实际做部署的人需要明白它们是不同层级的手段。量化通常在加载模型时就要决定属于“静态”的优化投机采样在推理引擎层面控制解码流程PD 分离则是架构层面的调度优化影响到整个服务集群的拓扑。理解了这一点就不会犯那种“我量化完但速度没提升”的困惑——因为量化主要解决显存和带宽压力不一定直接拉高 token 生成速率。真正的加速组合拳通常需要三者配合。2. 量化把模型的“体重”降下来让带宽压力小下去2.1 量化省下的显存有多少算一笔账就清楚了大模型权重的默认精度一般是 FP16也就是每个参数用 16 位浮点数存储占 2 字节。一个 7B 参数的模型光权重就占约 14GB 显存。如果换成 INT8每个参数占 1 字节权重降到约 7GB换成 INT4只要 0.5 字节权重只剩约 3.5GB。所谓“量化”就是把模型参数从高精度浮点数映射到低精度整数上再用这些低精度数字去完成尽可能接近原精度的计算。很多人第一反应是“精度不够效果崩了怎么办”。实际场景中训练后量化PTQ在 7B 甚至更大的模型上配合校准数据已经能把精度损失控制在很小的范围内。以 INT8 来说大部分任务的回答质量几乎无损INT4 会在某些复杂推理任务上出现轻微下降但换来的显存节约是实打实的。举个例子我最初在一张 24GB 显存的卡上跑 13B 模型FP16 权重占 26GB刚好超出一张卡的承载量。量化到 INT4 后权重只要约 7GB还能留出大量空间给 KV Cache 和中间激活跑起来非常从容。量化在这里的核心收益不是“让 token 飞起来”而是“让你能在更小的卡上跑更大的模型”同时因为每次读取权重的字节数减少Decode 阶段受带宽影响的耗时也会明显改善。2.2 主流量化方案选型GPTQ、AWQ、GGUF 和 FP8我自己实测下来选量化方案主要看你的部署环境。这里把常见方案分成三类第一类是 GPTQ它属于权重量化原理是逐层对权重做最小二乘近似用二阶信息补偿量化误差。GPTQ 在 GPU 上推理表现稳定常用 4bit 配置。第二类是 AWQ它的核心是“保护重要权重”基于激活值的分布找出哪些权重通道更敏感量化时对敏感通道给更高的精度。AWQ 在激活分布极端的情况下比 GPTQ 稍微稳一点。第三类是 GGUF 格式也就是 llama.cpp 生态里的量化格式支持从 q2_k 到 q8_0 的多个档位。它的好处是 CPU、GPU 混合运行时特别好使量化和推理工具链非常成熟。还有一个必须提的是 KV Cache 量化。我以前只量化权重跑长上下文时显存依旧不够。后来才意识到KV Cache 随着对话长度线性增长在 32k 甚至 128k 上下文的大模型服务里KV Cache 的显存开销经常超过权重本身。现在主流推理引擎一般支持 KV Cache 做 FP8 量化甚至 INT4 量化效果立竿见影。我自己的做法是长对话场景优先开 KV Cache 量化权重量化放在第二步。你选哪个方案不能只看 benchmark 里那零点几个百分点的精度差。关键要看你用什么推理引擎。用 vLLM、SGLang 这种服务型引擎GPTQ/AWQ 支持得最好用 llama.cpp 或者 Ollama 跑本地单机GGUF 最省心。FP8 量化则是 H 系列 GPU 上的新方向它保留了较高的精度显存和带宽开销又明显低于 FP16适合对质量要求高的生产环境。2.3 量化落地的几个关键参数与实操心得我在实际操作中使用 llama.cpp 比较多就拿 GGUF 来说。下载模型后先用工具做格式转换再执行量化命令。以 7B 模型为例常见的命令是llama-quantize ./model-fp16.gguf ./model-q4_k_m.gguf Q4_K_M这里 Q4_K_M 表示 4bit 量化的一个中间档位它在质量与体积之间比较均衡。如果你是第一次量化我的建议是先用 q8_0 验证流程确认没有 bug 后再试 q4_k_m不要一开始就压到 q2_k否则质量下降明显排查问题时会分不清是量化的问题还是代码的问题。显存估算有一个经验公式。以 INT4 为例模型加载所需显存大致是权重显存 参数量(亿) × 0.5GB / 10也就是说7B 模型 INT4 权重约 3.5GB13B 约 6.5GB70B 约 35GB。这只是权重还需要额外预留 KV Cache 空间和推理时的临时激活空间。如果一个模型的上下文长度很长KV Cache 的预留量可能远超权重本身这一点经常被忽略。实操中我也踩过坑。第一次量化 13B 模型时跑出来的回答明显胡言乱语十句话里有三句语法都不通。排查到最后才发现是校准数据集太小模型没见过足够多的真实对话模式量化后的权重分布就偏了。后来我老老实实准备了几百条和实际任务分布相近的样本做校准质量立刻恢复正常。这里想重点强调校准数据的质量比数量重要得多。每一条都应该是“你实际业务里会收到的输入”而不是从网上随便抓一段新闻。3. 投机采样让小模型“打草稿”大模型“做审批”3.1 核心思路用并行换串行前面说了 Decode 阶段一个 token 一个 token 生成每一步都依赖上一步的结果这在推理引擎那里是一个串行过程。投机采样Speculative Decoding的思路非常巧它不再让大模型一步一步地憋字而是先让一个速度快得多的小模型“草稿模型”一次性生成 k 个候选 token然后把这一串候选 token 拼到原始输入后面交给大模型一次验证。这里的关键在于大模型同时验证多个 token 时是并行计算的就像批处理一样耗时长不了多少。如果大模型验证后发现草稿模型的 k 个 token 全部正确那就白赚了 k 个 token 的解码时间如果前面几个正确后面某个出错了就保留正确的部分从错误处重新生成。这个逻辑既保证输出分布理论上和被验证模型保持一致又大大减少了 Decode 阶段的串行步数。我把这个过程理解成实习生先起草一份方案专家拿过来快速审一遍如果整页纸都没问题直接签发如果哪一段有问题只改那段之后的内容。审一页纸的时间远小于自己从头写一页纸这就是加速的来源。3.2 接受率、草稿模型大小和收益的权衡投机采样有没有收益取决于一个核心指标接受率Accept Rate也就是大模型最终接受草稿模型 token 的比例。这个比例越高收益越大。典型情况下一个好的草稿模型在相同数据分布上能达到 0.7 到 0.9 的接受率。如果接受率太低比如只有 0.3那么大模型大部分时间都在否决草稿模型等于白干活整体速度反而比直接解码更慢。草稿模型的大小选择特别讲究。草稿模型太弱生成出来全是错的接受率低草稿模型太强它的生成速度优势不明显整个系统的吞吐反而被拖慢。我的实践经验是70B 级别的模型配 1B 到 4B 的草稿模型比较合适13B 级别的模型配 0.5B 到 1B 的草稿模型7B 级别则可以考虑 0.1B 到 0.5B。这个比例不是绝对的得用真实业务数据实测测出来的接受率才有参考价值。另外草稿 token 的候选数量 k 也是一个需要调节的参数。k 太小并行收益有限k 太大大模型一次要验证的 token 过多延长单次验证的耗时并且草稿模型生成 k 个 token 也需要时间。我测试常用区间是 k4 到 k8具体要看模型对接受的敏感程度和显存余量。3.3 在 vLLM 里打开投机采样的配置示例现在主流引擎基本都内置了投机采样支持。vLLM 里的配置非常简单加载模型时传入额外参数类似这样from vllm import LLM llm LLM( modelQwen/Qwen2.5-7B-Instruct, draft_modelQwen/Qwen2.5-0.5B-Instruct, speculative_config{ num_speculative_tokens: 6, draft_model_step_worker: DefaultDraftWorker, } )这里draft_model就是草稿模型路径num_speculative_tokens对应前文提到的 k 值。第一次配置时我直接用默认的 5后来发现配合长输入任务时把 k 调到 8 反而更好因为草稿模型在长上下文上的接受率更高多给几个候选 token 能让收益放大。你可以理解为“办事员更靠谱时让他多准备几份备选方案”。还需要提醒一个容易踩的坑投机采样对系统负载非常敏感。在并发请求很高的时候GPU 的算力已经被多个请求占满这时草稿模型本身也会抢占计算资源加速效果会被稀释。所以在线服务如果已经达到较高吞吐增加投机采样不一定有正收益。我自己一般会在压测环境先对比“开”和“不开”两种模式下的真实每秒 token 数数据说话而不是凭感觉。4. PD 分离把 Prefill 和 Decode 放到不同节点上各干各的4.1 为什么混在一起跑会产生“互相打架”上线的推理服务不可能只有一个用户在请求系统通常并发处理多个请求。经典调度方式里Prefill 和 Decode 共用同一批 GPU请求进来先做 Prefill再进入 Decode。表面看没有问题实际一压测就会暴露当一个长文本请求进入 Prefill 阶段时它会占用大量算力导致正在 Decode 的所有请求一下子变卡整个服务的 token 输出速率剧烈抖动。原因很好理解Prefill 阶段是算力密集型一个长 Prompt 的 Prefill 能把 GPU 算力占满而 Decode 阶段是带宽密集型需要的是稳定、持续地从显存里搬数据。如果把这两种不同节奏的任务混在同一个 GPU 上算力和带宽互相争抢最直观的后果是“耗时尖刺”。我跑过一次压测在没有分离的情况下P99 token 延迟在 Prefill 请求多的时候能飙升 3 到 5 倍。这时候用户体感就是某一秒钟输出飞快下一秒卡死体验很不稳定。PD 分离的核心思路就是不要让它们打架把 Prefill 请求分配给一组专门处理“大计算”的节点把 Decode 请求分配给另一组专门跑“高带宽”的节点两者通过 KV Cache 的迁移衔接。这样 Prefill 慢一点也不会阻塞 DecodeDecode 可以稳定输出不会因为一个请求的 Prefill 而抖动。4.2 分离之后KV Cache 怎么传是核心问题PD 分离听起来简单实际操作里最大的技术难点是 KV Cache 的转移延迟。用户输入在 Prefill 节点处理完后整个中间状态的 KV Cache 要传给 Decode 节点Decode 节点才能继续往下生成。KV Cache 的大小跟输入 token 数量直接相关一个 2k 长度输入在 7B 模型上可能产生几十 MB 的 KV Cache如果要通过网络传输少则几十毫秒多则上百毫秒。这个延迟在单次请求中占比可能很小但在高并发场景下会形成累积。所以主流的实现都做了两个关键优化一是用高性能的 RDMA 网络直接传输 GPU 显存数据绕开 CPU 内存的中转二是把 KV Cache 切分成 chunk边传边解码不需要等全部传输完才开始生成这样网络延迟被隐藏在解码过程里用户几乎感知不到额外的等待。4.3 动态分离与静态分离的选型差异实际部署时PD 分离有两种落地方式静态分离和动态分离。静态分离就是在集群里固定划出几台机器做 Prefill几台机器做 Decode配置简单但负载不平衡时容易造成浪费。比如 Prefill 机器空闲而 Decode 机器排队静态配置没法临时调度。动态分离则允许 GPU 在不同的时间段承担不同的角色请求高峰期灵活调整很多现代推理框架已经在往这个方向演进。从我自己的落地经验看如果你的并发规模在几十路以内并且请求长度比较稳定静态分离完全够用收益已经很可观。如果服务流量波动大输入长度长短不一那么动态分离更合适。这里有一个始终不变的判断原则如果单机显存足够、并发不算高优先把 Prefill 和 Decode 合在一起跑因为省去了 KV Cache 传输的开销只有当服务规模大到出现明显的互相阻塞或者需要单独扩容 Decode 容量时分离才值得。还有一个小技巧如果想让 PD 分离的效果更明显可以同时开启 Continuous Batching连续批处理。它保证了 GPU 上有请求排队时新的请求能立刻插入空闲的运算单元而不是死板地等当前批次全部结束。配合 PD 分离之后整体吞吐能再上一个台阶。5. 三者组合怎么选以及部署中的常见问题与排查逻辑5.1 不同硬件条件下的组合策略把三种技术组合起来需要看清自己的硬件底牌。我遇到过很多人直接问“最优配置是什么”但这完全取决于你有几张卡、什么型号、什么网络。这里给出几套实测过思路的组合方案第一套单卡跑 7B 到 13B 模型目标是本地体验或小并发服务。优先上 INT4 量化把权重压到 4GB 左右显存有余量时开投机采样草稿模型选 0.5B 级别PD 分离在这种规模基本不需要。这套方案的典型效果是13B 模型在一张 24GB 卡上可以流畅跑完 32k 上下文token 速度比 FP16 原始版本快 2 到 3 倍。第二套多卡跑 70B 模型面向生产环境。权重用 INT8 或 AWQ INT4KV Cache 开量化然后根据压测结果决定是否引入 PD 分离。如果并发上来了P99 延迟开始抖动就引入动态分离把两个 Prefill 节点和六个 Decode 节点分开调度。第三套超大规模集群服务。这时候量化精度损失会影响输出质量通常在权重上保留 FP8 或 BF16KV Cache 量化按需开启投机采样和 PD 分离同时启用配合深度优化的调度器追求吞吐和延迟的平衡。我这三套结构不是死的真正的组合原则只有一个先用量化把显存问题解决再用压测判断要不要上投机采样和 PD 分离不要一开始就全上。5.2 高频问题速查表与几处隐蔽的坑我把实际部署中容易遇到的几个问题整理成一个速查表方便排查现象可能原因排查方向量化后回答开始胡言乱语校准数据不足或分布偏离扩展校准数据回退到更高精度档位开了投机采样但速度反而变慢草稿模型接受率太低或 k 值过大统计接受率调低 k 值换更大的草稿模型并发高时 token 输出一卡一卡Prefill 和 Decode 互相干扰压测 P99 延迟考虑 PD 分离KV Cache 显存超预期上下文很长且未开 KV Cache 量化开启 KV Cache 量化或限制最大长度多节点 PD 分离后延迟飙升KV Cache 传输没有走直连检查 RDMA 网络改用 chunk 流式传输有一个隐蔽的坑必须单独提投机采样之后日志里每秒 token 数看起来很高但用户体感还是慢。这种情况多半是“首个 token 延迟”没有优化投机采样只加速了 Decode 阶段的后续输出而没有解决从用户发请求到第一个 token 出现的 Prefill 等待时间。所以在线服务优化时不能只看吞吐指标还要盯住 TTFTTime To First Token首个 token 延迟。如果你发现 TTFT 占总耗时的比例很高重点优化 Prefill 调度而不是继续调投机采样参数。另一个坑是关于量化精度的“无感”。有些任务比如代码生成和数学推理对权重的微小扰动特别敏感INT4 量化之后正确率可能会掉。遇到这类任务我的做法是对比一出测试集在 FP16 和 INT4 下各跑一遍计算答案完全一致的比率在哪一个可接受范围。如果比例掉得厉害就果断换 INT8 或 AWQ不要为了追求极致的显存压缩牺牲用户看得见的正确性。最后PD 分离的显存规划也需要单独处理。Decode 节点因为只跑解码显存中 KV Cache 会迅速上涨它的显存需求主要由并发数和上下文长度决定。千万别拿 Prefill 节点的显存规划逻辑直接套到 Decode 节点上否则 KV Cache 溢出是必然的。我在实际部署中的体会是这三项技术并没有严格的“最优组合”只有“当前资源下最合适的组合”。先量化解显存愁再跑压测摸摸真实延迟分布最后根据瓶颈出现在 Prefill 还是 Decode再决定要不要动投机采样和 PD 分离的组合方式。最后再分享一个小技巧无论你采用哪种方案都记得保留一组未加速的对照配置随时跑同一批测试数据。推理加速的调优必须有对照组否则你很难分辨速度提升到底是量化带来的、投机采样带来的还是只是压测过程中偶然的波动。这个习惯我保持了很久帮我省掉了很多无效调参的时间。