“你这 27B 跑得像 PPT。”
我当时正盯着终端等回复,风扇已经开始起飞,光标还在一闪一闪装深沉。对方又补了一句:“别跟我说换模型,我就想要它回答快一点。”
那一刻我才意识到,很多人以为自己在“调速度”,其实是在赌运气:今天刚好快了就放行,明天慢了再骂模型。更糟的是,你还不知道它到底慢在哪个环节,所以每一次改参数都像在黑屋里摸开关。
所以这篇我不打算劝你“开 MTP 就完了”。我更想把这件事拆成一套能复用、能回滚的测速流程——你不是缺模型,你缺的是“速度可控的方法”。MTP(推测解码 / multi-token prediction)确实可能让体感突然“像换了台机器”,但前提是:你先会测,再敢开。
你的本地 27B 为什么体感卡?
大多数“卡”,不是一个指标坏了,而是三件事搅在一起——首 token 延迟、吞吐(tok/s)、长上下文退化。你不拆开测,就会在错误的地方下药。
我见过最常见的误判是:只看 tok/s 很高,就以为“很快”;结果用户依然觉得慢,因为首 token 要等 6–10 秒,体验像在敲门没人开。
下面这张表你可以先当作“症状→病因”的速查:
| 你感受到的慢 | 更可能对应的指标 | 常见原因(本地 27B) |
|---|---|---|
| 你问一句,它沉默很久 | 首 token 延迟(TTFT) | 首次编译/加载、KV cache 初始化、CPU/GPU 切换、上下文太长、采样参数太重 |
| 开始输出后像挤牙膏 | 吞吐(tok/s) | 量化不合适、显存不够频繁换页、batch/并行参数不合理、backend 没吃满 GPU |
| 越聊越慢、聊到后面崩 | 长上下文退化 | KV cache 爆显存、RoPE/长上下文配置、prefix cache 没开或命中率低 |
怎么测才算“测对了”?(给你一套可复制测速法)
先给一个你能直接贴给同事的规范版本:
把本地 Qwen 3.6 27B 的速度测清楚,只需要统一三件事:固定同一段 prompt、分别记录首 token 延迟(TTFT)和稳定输出阶段的 tok/s、再用 8k/16k/32k 三档上下文重复一遍看退化曲线。每次改配置只动一个变量,并把结果写进同一张记录表;一旦发现质量或稳定性变差,按“关 MTP→降上下文→换量化/降模型”的顺序回滚。
你需要记录的 3 个数
- TTFT(Time To First Token):从你点发送到它吐出第一个 token 的时间
- tok/s:进入稳定输出后每秒 token 数(别把开头那一段算进去)
- Context 退化:同样设置下,8k→16k→32k 的 tok/s 下滑多少、TTFT 上涨多少
记录表模板(复制就能用)
别只在脑子里“感觉更快了”,用表格把结论钉死:
| 日期 | 模型/量化 | Backend | 上下文长度 | MTP 档位 | TTFT(s) | tok/s | 质量备注 | 稳定性备注 |
|---|---|---|---|---|---|---|---|---|
| 2026-05-10 | Qwen3.6-27B / ? | ? | 8192 | off | ||||
| 2026-05-10 | Qwen3.6-27B / ? | ? | 8192 | conservative | ||||
| 2026-05-10 | Qwen3.6-27B / ? | ? | 16384 | conservative |
你会发现:表一旦开始填,“调参”就从玄学变成工程。
可复制的测速 prompt(尽量贴近真实场景)
同一个 prompt 才能对比。建议用“摘要 + 结构化输出”这种既像真实工作、又能稳定复现的任务:
- 输入:一段 1k–2k 中文材料 + 要求输出 10 条要点 + 每条 1 句理由
- 输出长度:固定,比如 300–500 token(太短测不出吞吐,太长浪费时间)
MTP 到底加速了什么?它的代价是什么?
MTP(Multi-Token Prediction)可以粗暴理解成:模型不再“一次只猜一个 token”,而是尝试“一口气先猜好几个”,再用校验把不对的踢掉。这类方法常被归到推测解码(speculative decoding)那一类。
它主要加速的是“稳定输出阶段”的吞吐(tok/s),对 TTFT 的帮助不一定大——有些实现甚至会让 TTFT 变差一点点,因为启动阶段多了额外工作。
代价我把话说重一点:MTP 是“用显存/算力换速度”,不是白嫖。你要盯的副作用一般在这三类:
| 你得到的 | 你付出的 | 你怎么判断值不值 |
|---|---|---|
| tok/s 上去,长回答更爽 | 显存占用可能上升 | 显存刚刚好的人,先看是否开始换页/掉速 |
| 平均吞吐提高 | 输出质量可能轻微波动 | 用固定 prompt 对比:事实性、格式稳定性是否变差 |
| 更接近“可用” | 某些 backend/版本不稳定 | 看日志有没有异常重试、NaN、偶发卡死 |
MTP 怎么开、怎么调?(从保守到激进三档)
这里先坦白一句:不同推理框架对 MTP 的开关名字不一样,支持程度也不完全一致。我自己也遇到过“文档里写支持、实际一开就没差异”的情况(最后发现是版本/编译选项没对上)。所以我不写“某某命令一定有效”,我写你能落地的通用流程:
- 先确认你用的推理框架“确实支持 MTP/推测解码”
- 再用同一套测速表,跑“off → 保守 → 中等 → 激进”
- 任何异常,按固定顺序回滚(下面给你)
官方信息优先看这里(少看二手贴):
- Qwen(通义千问)官方仓库:https://github.com/QwenLM
- vLLM 官方文档:https://docs.vllm.ai/
- llama.cpp 官方仓库(如果你走 GGUF 路线):https://github.com/ggerganov/llama.cpp
三档参数策略(按“旋钮”思路调,不按“背 flag”思路调)
我把它写成“旋钮”而不是具体 flag:你用的是 vLLM/llama.cpp/其他框架都没那么重要,重要的是变量要拆开、结果要可回放。
-
保守档(conservative)
- 目标:先验证“能提速且不影响质量/稳定”
- 做法:MTP 只预测少量 token;并发/批处理先别动太多
- 通过标准:tok/s 有提升;TTFT 不明显恶化;连续跑 10 次无异常
-
中等档(balanced)
- 目标:冲到“体感明显变快”的区间
- 做法:提高 MTP 预测 token 数;适度提高 batch/并行,让 GPU 更饱满
- 通过标准:tok/s 提升明显;长上下文退化曲线别恶化太多;输出格式保持稳定
-
激进档(aggressive)
- 目标:冲极限数据(适合你写 benchmark、或你很清楚自己在干嘛)
- 做法:更高 MTP 预测 token 数 + 更激进并行;通常需要更高显存余量
- 通过标准:除了速度,还必须满足“错误率/卡死率没有上升”(否则就是假赢)
你的回滚策略(真的救命)
我建议你把回滚顺序写死,遇到任何“不确定”就按这个走:
- 先把 MTP 关掉(回到基线)
- 上下文长度降一档(比如 16k→8k),看退化是不是上下文引发的
- 再考虑换量化(更省显存的量化通常更稳),或者降模型到 14B/7B 做业务兜底
你会发现:有回滚路径的加速,才敢用在真正赚钱/省时间的活里。
你怎么判断“2.5x faster”在你机器上是真是假?
别信别人的数字,信你的记录表。你只要跑完“off + 三档”,通常 2 小时内就能知道你机器能吃到多少提升;吃不到,问题多半在显存余量、backend 支持、或者你瓶颈根本不在解码阶段。
还有个很现实的坑:很多人看到“2.5x faster inference with MTP”就兴奋,结果自己机器上提升只有一点点,甚至变慢。常见原因一般是这些:
- 你的慢主要在 TTFT(启动/上下文/IO),MTP 改的是吞吐
- 你显存不够,MTP 一开触发换页,吞吐掉下去
- 你的 backend 没真正启用(或启用后走到 CPU/低效路径)
最小化测速脚本(Mac/Linux & Windows)
这段“最小测速流程”只做一件事:重复同一个 prompt,记录时间。你只需要把 ENDPOINT 换成你自己的本地推理服务地址(不管你用 vLLM 还是其他,只要能 HTTP 调用就行)。
说明:这段脚本不负责“跑模型”,只负责“测你现在跑出来的东西到底多快”。可复用的其实是这部分。
bash# Mac/Linux (bash)
# 依赖:curl, python3
export ENDPOINT="http://127.0.0.1:8000/v1/chat/completions"
python3 - <<'PY'
import os, time, json, subprocess, statistics
endpoint = os.environ["ENDPOINT"]
prompt = "请把下面这段材料总结成10条要点,每条一句理由,并给出一个标题:\n" + ("这是测试材料。" * 400)
def run_once():
payload = {
"model": "local-model",
"messages": [{"role":"user","content": prompt}],
"temperature": 0.2,
"max_tokens": 400,
"stream": False
}
t0 = time.time()
p = subprocess.run(
["curl","-s", endpoint, "-H","Content-Type: application/json", "-d", json.dumps(payload)],
capture_output=True, text=True
)
t1 = time.time()
ok = (p.returncode == 0 and len(p.stdout) > 0)
return (t1 - t0), ok, len(p.stdout)
times = []
oks = 0
for i in range(10):
dt, ok, size = run_once()
times.append(dt)
oks += int(ok)
print(f"run#{i+1}: {dt:.2f}s ok={ok} bytes={size}")
print("ok_rate:", oks/10)
print("p50:", statistics.median(times))
print("p90:", sorted(times)[int(len(times)*0.9)-1])
PY
Windows 用 PowerShell 跑同样逻辑也行。我一般图省事,直接放 WSL 里跑;如果你坚决不用 WSL,那就用 Invoke-RestMethod 包一层,记录 10 次请求耗时即可,核心是“同 prompt + 同参数 + 同统计口径”。
上线前检查清单:什么时候开 MTP,什么时候别硬开?
你可以把这段当“发车前绕车一圈”。
- 你先搞清楚瓶颈在哪
- TTFT 高:先解决加载/上下文/IO(MTP 不一定救你)
- tok/s 低:MTP 可能很有效(但先看显存余量)
- 你有显存余量吗?
- 显存刚好贴边:优先换量化/降上下文
- 显存宽裕:再谈 MTP 的中等/激进档
- 你能接受质量轻微波动吗?
- 产出要“格式绝对稳定”(比如写代码、生成 JSON):保守档甚至先别开
- 你做的是长文草稿/头脑风暴:更适合开
- 你有回滚按钮吗?
- 没有回滚=别开激进档
- 有回滚=你就敢把速度当成可运营指标去优化
FAQ
Q: 我只想让它“快一点”,先动哪个旋钮?
A: 先用记录表分清 TTFT vs tok/s。TTFT 高就先降上下文/优化加载;tok/s 低再考虑开 MTP 或调并行。
Q: 开了 MTP 但感觉没变快,是不是我开错了?
A: 可能是瓶颈不在解码吞吐,也可能 backend 没真正启用。用“off/保守/中等”三档跑同一张表,没差异就回到显存与上下文排查。
Q: 我应该开 MTP 还是直接换小模型/更激进量化?
A: 我更偏向“稳定优先”。显存贴边或要求严格格式输出,先量化/降模型;显存宽裕且主要慢在长输出吞吐,再考虑 MTP。
如果你今天只做一件事:把这篇里的“记录表模板”复制出来,先跑一轮 off + conservative,把 TTFT 和 tok/s 填上去。填完你再回来看,你会很直观地知道:你真正缺的是显存、是上下文预算,还是确实该开 MTP。
你现在的瓶颈更像哪一种——“沉默很久才开口”,还是“开口了但吐字慢”?我也很想看看,不同机器在这张表上的差异到底有多离谱。