📚 本文是《AIGC 人话系列》第 10 篇
系列目录:01 内容理解|02 RAG|03 三招选型|04 工具清单|05 文档问答|06 踩坑|07 行业观察|08 本地跑模型|09 Token 计费|10 推理加速(本篇)上一篇算的是云端的钱,这篇解决上一篇遗留的问题:本地模型为什么这么慢。
同一台机器,两次跑,速度差了四倍
第 8 篇发出去之后,后台收到最多的一类问题是:我的怎么这么慢?
有意思的是,有人说 3 字/秒,有人说 12 字/秒,一问机器配置,差不多——都是 8 核 CPU、16G 内存,都跑 8B 的量化模型。
差在哪?差在那几个没人告诉你、默认值又不太合适的参数上。
我拿自己的旧笔记本把能调的参数一个个试了一遍。结论先放这儿:六个参数里,只有三个真有用,另外三个纯属浪费时间。
第一步不是调参,是分清「哪种慢」
很多人一上来就搜「大模型推理加速」,然后被灌一堆 vLLM、TensorRT、FlashAttention。这些东西有用,但对你一台没独显的笔记本,一个都用不上。
先把「慢」拆开看,三种慢,病因完全不同:
| 症状 | 表现 | 病因 |
|---|---|---|
| 启动慢 | 敲完命令,等十几秒才出现提示符 | 模型要读进内存(冷启动) |
| 首字慢 | 回车之后,很久才蹦出第一个字 | 在算你发的那一大段上下文 |
| 生成慢 | 字一个一个往外蹦,慢得能看见 | 模型逐 token 推理,硬件上限 |
大部分人抱怨的「慢」,其实是第三种,但真正能立刻改善体验的,往往是第一和第二种。
因为从 10 秒降到 1 秒,感受上是从「不能用」到「能用」;而从 3 字/秒提到 4 字/秒,你几乎感觉不到。
六个参数,我一个个试了
先说结论,再讲过程:
| 参数 | 管什么 | 有没有用 |
|---|---|---|
num_gpu | 多少层算到显卡上 | 有显卡才谈,核显基本无效 |
num_thread | 用几个 CPU 线程 | 有用,但只能调对一次 |
num_ctx | 上下文窗口多大 | 最有用,也最容易被忽略 |
| 量化等级 | q4 还是 q8 | 有取舍,不是纯优化 |
keep_alive | 模型在内存里待多久 | 有用,专治启动慢 |
| 换小模型 | 8B 换 4B | 有用,但那是降级不是优化 |
第一个:num_thread,先调对一次就够了。
它决定用几个 CPU 线程推理。默认值有时候会按逻辑核心数给,而你机器上的「16 核」可能是 8 物理核 + 超线程——超线程对这类计算帮助很小,甚至因为抢缓存变慢。
改成物理核心数(不是逻辑核心数):
ollama run qwen3:8b
>>> /set parameter num_thread 8
我这边从 3.2 字/秒提到 4.6 字/秒,约 1.4 倍。调完就不用再管了,没有持续收益,只有一次性纠正。
第二个:num_ctx,最被低估的一个。
上下文窗口不是「能装多少」,是「每次都要为这么多算一遍」。你把它从 2048 调到 16384,内存涨一截,速度还掉——因为注意力计算量随长度增长。
我对比过同一段提问:
| 上下文设置 | 内存占用 | 生成速度 |
|---|---|---|
| 2048 | 约 5.2G | 6.1 字/秒 |
| 4096(默认附近) | 约 5.6G | 4.8 字/秒 |
| 16384 | 约 7.4G | 2.9 字/秒 |
上下文的收益是「能塞多长的文档」,代价是「每句话都变慢」。 只做短问答的场景,直接压到 2048,白捡一倍速度。
需要长文档时再临时开大,别一直挂着。
第三个:keep_alive,专治「启动慢」。
默认模型在内存里待 5 分钟就卸载。你隔十分钟再问一次,它重新加载一遍,又是十几秒。
让它在内存里常驻:
# 方式一:启动时指定
OLLAMA_KEEP_ALIVE=-1 ollama serve
# 方式二:写进请求里
curl http://localhost:11434/api/generate -d '{
"model": "qwen3:8b",
"prompt": "你好",
"keep_alive": -1
}'
首字等待从七八秒降到零点几秒。这是唯一一个「改了立刻有爽感」的参数。
代价也直白:模型一直占着 5G 内存,别的程序就少了 5G。16G 的机器上,别同时开三个模型。
第四个:num_gpu,没独显就别折腾。
这个参数控制把多少层模型卸载到显卡。有独显(尤其是 8G 以上显存)时,调对了能快好几倍;但如果你只有核显,或者显存太小装不下几层,调它反而更慢——因为数据要在内存和显存之间来回搬。
判断方法:先看 ollama ps 里显示的 GPU 占用比例,如果是 0%,说明压根没用上。
第五、第六个:量化等级和模型大小,这俩不算优化。
- 从 q4 换到 q8,质量好一点,速度慢 30%~40%,内存多占一倍
- 从 8B 换到 4B,速度几乎翻倍,但复杂任务明显变笨
这是取舍,不是加速。选哪个取决于你干的是「改写抽取」还是「推理判断」。
调完之后,速度是这样变的
同一台机器,同一个模型,一步步调下来:
3.2 → 4.6 → 6.1 → 11.5 字/秒,前三步全是配置,最后一步是换模型
前两步(线程 + 上下文)是纯赚的,没牺牲任何质量。第三步换 4B 模型属于降级,谨慎。
三个我试过但没用的招
写出来是为了让你别再花时间:
加内存。 以为内存大就快,结果从 16G 加到 32G,速度一点没变——因为 16G 已经够装下模型了,瓶颈不在容量上。
换更快的硬盘。 只影响第一次加载模型的时间,加载完之后推理全在内存里,跟硬盘没关系。
关杀毒软件。 理论上有点用,实测在噪声范围内,感觉不出来。
规律很简单:推理是算力瓶颈,不是 IO 瓶颈。 所有「加快读写」的办法,对生成速度基本无效。
速查表:明天就能用
# 看模型有没有真的跑起来、占了哪块硬件
ollama ps
# 调线程(填物理核心数)
>>> /set parameter num_thread 8
# 压上下文(短问答场景)
>>> /set parameter num_ctx 2048
# 让模型常驻,别反复冷启动
OLLAMA_KEEP_ALIVE=-1 ollama serve
# 看这次生成的耗时明细(eval 才是纯生成速度)
curl http://localhost:11434/api/generate -d '{"model":"qwen3:8b","prompt":"你好"}' | jq '.eval_duration, .eval_count'
最后一行那个接口返回里,eval_duration 和 eval_count 才是真实的生成速度,total_duration 里混了加载和首字时间——拿 total_duration 算速度,会把自己的调优成果浪费掉。
泼点冷水
第一,没有独显,天花板就在那儿。 上面所有调优,最多让你从 3 字/秒到 6 字/秒。想再快,只有三条路:加显卡、换 Mac 的统一内存机型、或者别在本地跑。
第二,别为了速度把上下文压死。 压到 2048 之后,长文档会静默截断,你以为是模型变笨了,其实是它压根没看到后半段。
第三,调参的收益远小于「换对模型」。 你花两小时调参数,不如花两分钟想想:这个任务是不是本来就该用 4B 模型干。
上一篇:Token 是怎么算的?为什么你的 API 账单总比预期贵一倍
👋 这个系列叫《AIGC 人话系列》,目标是把 AIGC 讲成人话:不背术语、不堆榜单,每篇只讲清一件事,配一个能直接抄走的配置或代码。对你有用的话点个关注。
你本地跑多少字每秒?调过 num_thread 和 num_ctx 吗?评论区报一下机器配置和速度,我整理成一个真实的横向对照。