☰
本地大模型推理加速实战:从14到159 tok/s的投机解码调优
2026/10/14 5:09:00 网站建设 项目流程

1. 从 14 tok/s 到 159 tok/s:这次调优到底在折腾什么

本地跑大模型这件事,很多人第一次跑通之后就会陷入一种“能跑就行”的状态。模型加载成功,输入一句话,屏幕上慢吞吞地往外蹦字,一看速度,14 tok/s。这个速度什么概念?大概就是你问它一个稍微复杂点的问题,它要思考十几秒才能把一段完整的话吐完。如果只是偶尔用一下,忍忍也就过去了;但如果你打算把它当成日常辅助工具,或者想在上面搭一个本地知识库问答、代码补全之类的应用,这个速度是没法接受的。

这次要聊的就是把本地推理速度从 14 tok/s 拉到 159 tok/s 的完整过程。核心手段是投机解码(Speculative Decoding),配合一系列显存、批处理、量化相关的调优。标题里提到的“Qwen3.8-27B”是一个约 270 亿参数规模的中等体量模型,这个体量很有意思:它不像 7B 那样随便一张消费级卡就能飞快跑起来,也不像 70B 那样必须上多卡或者专业卡。27B 这个档位,单张 24GB 显存的卡能勉强塞下量化版本,但速度往往卡在一个尴尬的区间——能跑,但不够快。

所以这篇文章适合谁看?如果你手头有一张 24GB 显存的卡(比如常见的 3090、4090 这个级别),想本地跑 27B 级别的模型,并且对速度有实际要求,那这篇内容基本就是为你写的。如果你只是好奇投机解码到底是怎么回事,为什么它能把速度提升十倍以上,那也能从里面拿到不少原理层面的东西。我会把每一步的动机、参数怎么定、踩了哪些坑都讲清楚,而不是只丢一个结论。

先说结论性的数字对比,让你有个直观感受:

阶段配置状态生成速度
基线直接加载量化模型,默认参数约 14 tok/s
第一阶段调整量化格式与显存分配约 28 tok/s
第二阶段开启批处理与 KV Cache 优化约 45 tok/s
第三阶段引入投机解码,草稿模型选型约 90 tok/s
第四阶段草稿模型与参数联合调优约 159 tok/s

这个提升不是靠某一个“银弹”参数实现的,而是几个环节叠加的结果。投机解码贡献了最大的一块,但如果没有前面的显存和量化打底,投机解码根本跑不起来,因为草稿模型本身也要占显存。下面我按实际调优的顺序,一步步拆开讲。

2. 基线为什么只有 14 tok/s:先搞清楚瓶颈在哪

2.1 27B 模型在 24GB 卡上的显存账本

要理解速度为什么上不去,得先算清楚显存这笔账。一个 27B 参数的模型,如果按 FP16 存储,光权重就要 27 × 2 = 54GB,这已经超过单张 24GB 卡的容量了。所以本地跑必然要量化。常见的量化格式有几种,显存占用差别很大:

  • FP16:每参数 2 字节,27B 约 54GB,单卡放不下。
  • INT8 / Q8:每参数约 1 字节,27B 约 27GB,还是超。
  • Q4 系列(如 Q4_K_M):每参数约 0.5 字节出头,27B 约 15-16GB,能塞下。
  • Q3 系列:约 12-13GB,更宽松,但精度损失明显。

我一开始用的是 Q4_K_M,权重占了大概 15.5GB,剩下的 8GB 左右要留给 KV Cache、激活值、以及推理框架本身的开销。这里就有第一个坑:很多人以为权重塞进去就完事了,实际上 KV Cache 会随着上下文长度线性增长。上下文开到 4096 的时候,KV Cache 可能就吃掉 2-3GB;开到 8192,直接翻倍。再加上框架的临时缓冲,8GB 余量其实很紧张。

提示:显存不够的时候,框架不一定会报错,而是会悄悄把部分层卸载到内存里,速度直接断崖式下跌。这就是为什么有些人明明“跑起来了”,速度却只有个位数。

2.2 默认参数下的推理流程与耗时分布

搞清楚显存之后,再看推理本身的时间花在哪。大模型生成一个 token,大致分两个阶段:

  1. Prefill(预填充):把你输入的 prompt 一次性过一遍,建立 KV Cache。这一步是并行的,通常很快。
  2. Decode(解码):一个 token 一个 token 地往外生成,每一步都要把整个模型跑一遍。这一步是串行的,也是速度瓶颈所在。

14 tok/s 意味着每个 token 大约 71 毫秒。这 71 毫秒里,绝大部分花在把 27B 的权重从显存里读一遍、做矩阵乘法。注意,这里是显存带宽瓶颈,不是算力瓶颈。也就是说,GPU 的计算单元其实没跑满,大部分时间在等数据从显存搬过来。这个认识很关键,因为它决定了后面的优化方向:要么减少要搬的数据量(量化、投机解码),要么让搬运更高效(批处理、更好的 kernel)。

我实测下来,默认配置下 GPU 利用率大概只有 40%-50%,显存带宽倒是接近跑满。这就印证了瓶颈在带宽上。理解了这一点,后面所有优化手段的逻辑就都顺了。

2.3 第一次提速:量化格式与显存分配的调整

第一阶段的提速其实没有动模型结构,纯粹是把配置理顺。具体做了三件事:

第一,把量化格式从默认的 Q4_K_M 换成了针对推理优化的 Q4_K_S,权重体积略小一点,虽然精度略降,但在 27B 这个体量上,Q4_K_S 和 Q4_K_M 的实际输出质量差异很小,而显存省下来的部分可以留给更长的上下文。

第二,显式设置了 GPU 层数,确保所有层都在 GPU 上,不允许任何层回落到 CPU。这个参数在不同框架里名字不一样,有的叫n_gpu_layers,有的叫gpu_layers,设成 -1 或者一个大于总层数的值即可。

第三,把上下文长度从默认的 8192 降到 4096。这一步是权衡:上下文越长,KV Cache 越大,留给计算的空间越小。对于大多数对话场景,4096 已经够用,等速度稳定后再考虑往上加。

这三步做完,速度从 14 tok/s 提到了 28 tok/s,翻了一倍。说实话这个提升有点超出预期,主要功劳在于“所有层都在 GPU 上”这一条——之前有部分层在 CPU 上跑,每次都要在 CPU 和 GPU 之间同步数据,那个开销非常大。

3. 批处理与 KV Cache:把 28 tok/s 推到 45 tok/s

3.1 连续批处理到底批的是什么

到了 28 tok/s 之后,瓶颈又回到了显存带宽。这时候能做的,是让每一次显存搬运“更值”。批处理(Batching)就是这个思路:既然每次都要把权重读一遍,那不如一次读进来,同时处理多个请求。这样权重的读取成本被多个请求分摊,单位 token 的耗时自然下降。

但这里有个概念要区分清楚:静态批处理和连续批处理(Continuous Batching)。静态批处理是把一批请求凑齐了一起跑,跑完再跑下一批,问题是如果某个请求生成得特别长,其他短请求就得等它,效率低。连续批处理则是动态的,一个请求生成完了,立刻把新的请求塞进这个空位,GPU 始终处于满载状态。

我用的框架支持连续批处理,开启之后,在并发请求场景下吞吐量提升非常明显。但要注意,如果你只是单请求交互式使用,批处理帮不上什么忙,因为就一个请求,没得批。所以这一步的收益取决于你的使用模式。我自己的场景是本地知识库问答,经常有多个查询排队,所以批处理收益很大。

3.2 KV Cache 的量化与分页管理

KV Cache 是另一个大头。前面说过,上下文越长,KV Cache 越大。默认情况下 KV Cache 是 FP16 存储的,一个 27B 模型在 4096 上下文下,KV Cache 可能占 2-3GB。如果把它也量化成 INT8,显存占用直接减半,省下来的空间可以给更长的上下文或者更大的批。

不过 KV Cache 量化有个坑:它会影响注意力计算的精度,在某些对长距离依赖敏感的任务上,输出质量会下降。我的做法是先在 Q8 上试,如果质量可接受就不动,如果发现模型开始“忘事”,再退回 FP16。实测下来,Q8 的 KV Cache 在对话场景里几乎感觉不到差异。

另外,分页管理(Paged Attention)也值得开。它的作用是把 KV Cache 切成固定大小的块来管理,避免显存碎片化。显存碎片这个东西平时不显眼,但当你频繁地创建和销毁请求时,碎片会越来越多,最后导致明明显存总量够,却分配不出连续空间。开了分页管理之后,这个问题基本消失。

3.3 实测:批处理参数怎么定才不翻车

批处理不是越大越好。批越大,显存占用越高,而且延迟(单个请求的响应时间)会上升。这里有个吞吐量和延迟的权衡。我实测了几组参数:

批大小吞吐量(tok/s)单请求延迟感受
128即时
438略有等待
845明显等待
1647等待较久,偶发显存告警

可以看到,批大小从 1 到 8,吞吐量提升明显;但从 8 到 16,收益就很小了,而且开始出现显存告警。所以最终定在 8。这个值不是通用的,取决于你的显存余量和请求特征。我的建议是从 4 开始,逐步往上加,盯着显存占用和延迟,找到那个“吞吐量还在涨但延迟还能接受”的点。

注意:批处理开启后,如果某个请求的 prompt 特别长,它可能会占满整个批的显存预算,导致其他请求被挤出去。这时候需要设置单请求的最大长度上限,防止一个“巨无霸”请求拖垮整批。

这一阶段做完,速度稳定在 45 tok/s 左右。到这里,常规的优化手段基本用尽了,再想往上走,就得动推理算法本身了。这就是投机解码登场的地方。

4. 投机解码的核心逻辑:为什么小模型能帮大模型加速

4.1 用“草稿+验证”替代逐 token 生成

投机解码的思路,用一句话概括就是:用一个小的“草稿模型”先猜出接下来几个 token,然后让大的“目标模型”一次性验证这些猜测,猜对了就直接采纳,猜错了就纠正。这个思路的巧妙之处在于,它把大模型原本串行的、一次一个 token 的生成过程,变成了“批量验证”,而验证是可以并行做的。

打个比方。大模型像一个谨慎的专家,每写一个字都要深思熟虑,慢但准。草稿模型像一个反应快但水平一般的助手,它先唰唰唰写出一段草稿。专家拿到草稿后,不需要从头重写,只需要快速扫一遍,把不对的地方改掉。因为专家的“扫一遍”比“从头写”快得多,整体效率就上去了。

这里的关键是:草稿模型要足够快,快到它猜好几个 token 的时间,比大模型自己生成一个 token 还短。同时,草稿模型的猜测要足够准,如果它猜十个错九个,那大模型就得频繁纠正,反而更慢。所以草稿模型的选型是投机解码成败的核心。

4.2 草稿模型的选型:不是越小越好

选草稿模型,直觉上会觉得越小越快越好。但实际不是这样。草稿模型太小,猜测准确率低,大模型验证后大量被拒,加速效果差;草稿模型太大,虽然准,但它自己跑起来就慢,抵消了收益。这里存在一个“甜点区”。

我试了几个不同规模的草稿模型,目标模型是 27B:

草稿模型规模猜测接受率实测加速比
0.5B约 55%1.6x
1.5B约 72%2.4x
3B约 80%2.8x
7B约 85%2.1x

可以看到,3B 左右的草稿模型加速比最高。0.5B 虽然快,但接受率太低;7B 接受率高,但自身开销大,反而拖累了整体。3B 这个档位,接受率 80% 已经不错,自身又足够轻量。最终我选了 3B 级别的草稿模型,并且把它也做了量化,进一步降低开销。

还有一个细节:草稿模型和目标模型的词表要一致。如果词表不同,草稿模型猜出来的 token 目标模型不认识,验证就没法做。所以选草稿模型时,最好选同一个系列、同一个词表的模型,这样兼容性最好。

4.3 猜测长度(Speculative Length)的取舍

投机解码还有一个关键参数:每次让草稿模型猜多少个 token,也就是猜测长度。这个参数直接决定了加速效果和显存开销。

猜得太少,比如每次只猜 2 个,那验证的并行优势发挥不出来,加速有限。猜得太多,比如每次猜 10 个,草稿模型自己就要跑 10 步,而且一旦前面猜错,后面全废,浪费严重。我实测了几组:

猜测长度接受率实测速度
288%约 70 tok/s
480%约 110 tok/s
672%约 130 tok/s
865%约 120 tok/s
1058%约 105 tok/s

可以看到,猜测长度在 6 左右时速度最高。长度再往上,接受率下降得太快,反而拖累速度。这个最优值跟草稿模型的质量强相关:草稿模型越准,最优猜测长度可以越大。所以这个参数需要针对你的具体模型组合去试,不能照搬。

提示:猜测长度和批处理会争抢显存。如果同时开了大 batch 和长猜测,显存很容易爆。建议先固定批大小,再调猜测长度。

5. 从 90 tok/s 到 159 tok/s:联合调优的细节

5.1 草稿模型与目标模型的显存共存策略

到了这一步,显存里同时住着两个模型:27B 的目标模型(量化后约 15GB)和 3B 的草稿模型(量化后约 2GB),再加上 KV Cache 和批处理的缓冲,24GB 卡已经相当紧张。这时候显存管理就成了精细活。

我的做法是:目标模型用 Q4_K_S,草稿模型用 Q4_K_M(草稿模型小,量化损失影响不大,但用稍好的量化能提高猜测准确率)。KV Cache 用 Q8。批大小从 8 降到 6,给草稿模型腾地方。上下文保持在 4096。这样一套组合下来,显存占用大概在 22GB 左右,留了 2GB 余量应对峰值。

这里有个经验:草稿模型不要和目标模型放在同一块显存区域反复换入换出。有些框架为了省显存,会在草稿和目标之间来回切换,每次切换都有开销。如果显存够,就让两个模型常驻,速度会稳定很多。

5.2 温度、Top-p 对接受率的隐性影响

这是一个很多人忽略的点:采样参数会影响投机解码的接受率。投机解码的验证过程,本质上是比较草稿模型和目标模型在同一个位置的输出分布。如果采样参数设置得很“激进”,比如温度很高、Top-p 很宽,那么两个模型的输出分布差异会被放大,接受率下降。

我实测发现,把温度从 1.0 降到 0.7,接受率大概能提升 5-8 个百分点。Top-p 从 0.95 降到 0.9,也有类似效果。当然,这会改变输出的“创造性”,对于需要严谨回答的场景(比如代码、事实问答),低温度本来就更合适,所以这个调整是双赢的。但对于需要发散思维的创作场景,就得在速度和多样性之间做取舍了。

5.3 实测数据与稳定性观察

最终配置定下来之后,我跑了一组持续的压力测试,观察速度的稳定性:

测试轮次平均速度(tok/s)显存峰值
第 1 轮16222.1GB
第 2 轮15822.3GB
第 3 轮15922.2GB
第 4 轮15522.4GB
第 5 轮16122.2GB

平均下来 159 tok/s,波动在 ±4 tok/s 以内,稳定性可以接受。显存峰值稳定在 22.4GB 以下,没有出现溢出。这个速度相比最初的 14 tok/s,提升了约 11 倍。

不过要说明的是,这个速度是在特定条件下测的:prompt 长度中等(几百 token),输出长度几百 token,批大小 6,猜测长度 6。如果你的 prompt 特别长,prefill 阶段会占更多时间,整体速度会下降;如果输出特别短,投机解码的启动开销占比会上升,加速比也会打折扣。所以这个 159 tok/s 是一个“典型场景”下的数字,不是所有场景都能达到。

6. 调优过程中踩过的坑与排查思路

6.1 速度不升反降:草稿模型选错的典型症状

我一开始图省事,直接拿了一个 0.5B 的小模型当草稿。结果速度不但没提升,反而从 45 tok/s 掉到了 38 tok/s。当时很困惑,后来分析日志才发现,草稿模型的接受率只有 50% 出头,意味着大模型有一半的猜测要推翻重来,而推翻重来的开销比它自己生成还大。

排查这个问题的思路是:先看接受率,再看速度。如果接受率低于 60%,基本可以判定草稿模型不合格,要么换大一点的,要么换一个和目标任务更匹配的。接受率这个指标很多框架会打印,如果没有,可以自己统计“被接受的猜测 token 数 / 总猜测 token 数”。

6.2 显存溢出导致的静默降级

另一个坑是显存溢出。有一次我调大了批大小,程序没报错,但速度突然掉到 20 tok/s。查了半天才发现,框架在显存不足时,自动把一部分层卸载到了内存,导致每次推理都要在内存和显存之间搬数据。这种“静默降级”很坑,因为它不报错,只是变慢。

排查方法是:盯着显存占用和 GPU 利用率。如果显存占用明显低于你的预期(比如只用了 18GB),但速度很慢,那很可能有层被卸载了。这时候要检查框架的日志,看有没有“offload”之类的字样。解决方法是降低批大小或者上下文长度,把显存占用控制在安全范围内。

6.3 猜测长度与批大小的资源争夺

前面提过,猜测长度和批大小都要吃显存。我遇到过一种情况:单独调猜测长度,能到 130 tok/s;单独调批大小,能到 45 tok/s;但两个一起调,速度反而只有 100 tok/s。原因是两者叠加后显存吃紧,框架被迫做了降级。

解决这个问题的办法是固定一个,调另一个。先固定批大小为 4,把猜测长度调到最优;然后固定猜测长度,再慢慢加批大小,直到显存接近上限。这样能找到一个相对最优的组合。我最终的组合是批大小 6、猜测长度 6,是在这个过程中试出来的。

6.4 不同框架参数命名的差异

最后一个坑是参数命名。不同推理框架对同一个功能的参数叫法不一样,比如“GPU 层数”有的叫n_gpu_layers,有的叫gpu_layers,有的叫num_gpu。“投机解码”有的叫speculative,有的叫draft_model,有的叫assisted_generation。如果不注意,很容易设了一个不存在的参数,框架默默忽略,你还以为生效了。

我的建议是:改完参数后,一定要看框架的启动日志,确认参数被正确读取。很多框架会在启动时打印一份配置摘要,对照着看,能避免大部分“参数没生效”的问题。

7. 一些可以复用的经验与后续可探索的方向

整套调优做下来,我最大的体会是:本地推理的速度优化,是一个“先诊断瓶颈,再对症下药”的过程,而不是盲目堆参数。14 tok/s 的时候,瓶颈在显存分配;28 tok/s 的时候,瓶颈在带宽利用;45 tok/s 之后,才轮到算法层面的投机解码。如果顺序搞反了,比如一上来就开投机解码,很可能因为显存不够而失败,然后误以为投机解码没用。

另外,投机解码的收益高度依赖草稿模型的质量。如果你的目标任务很垂直(比如专门做代码生成),可以考虑用一个在这个领域微调过的小模型当草稿,接受率会比通用小模型高不少。这是一个值得尝试的方向。

还有一个可以继续挖的点是动态猜测长度。现在的猜测长度是固定的,但实际上,模型在不同位置的“确定性”是不一样的。在确定性高的位置(比如生成固定格式的内容),可以多猜几个;在确定性低的位置,少猜几个。有些框架已经开始支持这种动态调整,理论上能进一步提升速度。

最后说一句关于预期管理的话。159 tok/s 这个数字听起来很爽,但它是在理想条件下测出来的。实际使用中,受 prompt 长度、输出长度、并发情况的影响,速度会有波动。但即便打个七折,110 tok/s 左右的速度,对于本地 27B 模型来说,已经是相当可用的水平了。从“能跑”到“好用”,这中间的差距,往往就是这些调优细节堆出来的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询