返回博客

故障排查指南

llama.cpp 提示词处理慢 35 倍?修复 q5_1 KV 缓存

Flash attention 加上 q5_1 KV 缓存可能把注意力推到 CPU 上。如何确认、用 q8_0 修复,以及何时用 API 更简单。

作者:CheapTokens Team发布于 5 分钟阅读

终端显示 llama.cpp 提示词处理期间 GPU 占用 0%,CPU 占用 930%
本页内容
  1. 屏幕上是什么样子
  2. 为什么更小的缓存反而慢这么多
  3. 两分钟检查
  4. 修复 1:使用 q8_0(或重新构建)
  5. 什么时候调优不再值得
  6. 按什么顺序处理

你的本地模型回答得不错,但现在每个长提示词都要等上一分半钟,而 GPU 却闲着没事干。如果你用 -fa on 和 q5_1 KV 缓存运行 llama.cpp,这很可能就是原因:默认的 CUDA 构建没有针对该缓存类型的 flash-attention 内核,所以注意力这一步悄悄跑在了 CPU 上。把缓存换成 q8_0 后,一位报告者的提示词处理速度从 38 tokens/s 提升到了 1348 tokens/s。

下面的数字来自 llama.cpp issue #28455(2026 年 9 月 5 日创建,构建 10791,2026 年 10 月 28 日核查)。一位报告者、一块 RTX 4080、一个 27B 模型,所以请把它们当作一个实例,而不是定律。他们的根因分析也是他们自己的:一位维护者称其为“已知问题”,并在第二天关闭了该 issue。

屏幕上是什么样子#

配置很普通。有人想在单块 16 GB 显卡上跑 131072 token 的上下文,于是把 KV 缓存缩小到了 q5_1:

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q5_1 --cache-type-v q5_1

然后他们发送了一个 3.6K token 的提示词。提示词处理速度为 38.4 tokens/s,解码速度为 6.5 tokens/s。算一下:3,600 ÷ 38.4 = 93.75,所以大约 94 秒后才出现第一个词。用 q8_0 时,同样的提示词跑到了 1348 tokens/s:3,600 ÷ 1,348 = 2.67,不到三秒。

线索在任务管理器里。慢速运行期间,GPU 占用接近 0%,而 CPU 显示约 930%(九到十个核心在忙)。当你“在 GPU 上运行”时 GPU 却闲着,这就是要找的症状。

q5_1 和 q8_0 KV 缓存下的提示词处理速度
3.6K token 提示词的提示词评估,RTX 4080,llama.cpp issue #28455(2026 年 9 月 5 日)

为什么更小的缓存反而慢这么多#

Flash attention 是一个融合的 GPU 内核。默认的 CUDA 构建只为部分 KV 缓存类型对提供了它。对于 q4_0、q4_1、q5_0 和 q5_1,没有对应的内核,所以 llama.cpp 不会报错停止。它把该操作移到 CPU 上继续运行。一切仍然能工作,只是提示词处理大约慢了 35 倍(1348 ÷ 38.4 = 35.1)。

那个“悄悄”才是整个问题所在。答案里没有任何东西告诉你。提示词处理下降最多(约 35 倍),生成也会变慢:同一 issue 中为 6.5 tokens/s,而 q8_0 是 30.1 tokens/s。

两分钟检查#

用你在服务器中使用的相同缓存类型运行 llama-bench,每种变体跑一次,并在另一个终端里观察 GPU:

bash
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q8_0 -ctv q8_0 -p 512 -n 0
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q5_1 -ctv q5_1 -p 512 -n 0
nvidia-smi -l 1

报告者在 Qwen 27B Q5_K_XL 上的 pp512 结果:q8_0/q8_0 为 1511.92 tokens/s,默认构建下 q5_1/q5_1 约为 197。如果你两行结果出现这样的差距,并且 nvidia-smi 显示第二行运行时 GPU 空闲,那你就找到了。

在修复任何东西之前,先把看到的现象与可能的原因对应起来:

配对

将每个症状对应到其原因

  • 长提示词期间 GPU 约 0%,CPU 约 930%注意力在 CPU 上运行
  • 解码降到约 6.5 tokens/s,而 q8_0 是 30.1生成也变慢,不只是提示词处理
  • 同样的参数用 q8_0 很快该缓存类型对在默认构建中没有 GPU flash-attention 内核

修复 1:使用 q8_0(或重新构建)#

最快的修复是改两个参数,其余保持不变:

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q8_0 --cache-type-v q8_0

在该 issue 中,这让提示词处理达到 1348 tokens/s,解码达到 30.1 tokens/s(从 6.5 提升)。代价是内存:q8_0 缓存比 q5_1 更大,所以检查你的上下文长度是否仍能放进显存。

如果你出于内存原因必须保留 q5_1,请重新构建并编译进所有 flash-attention 缓存组合:

bash
cmake -B build -DGGML_CUDA=ON -DGGML_CUDA_FA_ALL_QUANTS=ON
cmake --build build --config Release -j

该构建在 pp512 上以 1509.70 tokens/s 运行 q5_1,在 3.6K token 提示词上为 1162 tokens/s,解码为 38.5 tokens/s。该 issue 的一位评论者指出,在后端之间移动工作可能会稍微改变数值结果,并建议在不同构建之间做困惑度比较。如果你在意精确输出,就跑一个。

比较

哪种修复适合你的显卡?

q8_0 缓存

把 --cache-type-k 和 --cache-type-v 改为 q8_0。无需重新构建。比 q5_1 占用更多显存,所以重新检查你能容纳的上下文大小。

用 FA_ALL_QUANTS 重新构建

保留 q5_1 并用 -DGGML_CUDA_FA_ALL_QUANTS=ON 构建。显存占用与之前相同,但你需要维护自己的构建,而且编译时间更长。

维护者的“已知问题”回复没有说明当前 llama.cpp 版本是否改变了这一点。诚实的检查就是上面的 llama-bench 对:如果两行接近,你就没问题。

什么时候调优不再值得#

假设你让修复生效了,现在你在权衡下一轮:更多参数、第二块 GPU、更小的量化。如果任务是几千 token 提示词上的摘要、代码审查或提取,问问一个请求在时间和精力上花你多少,而不只是瓦数。

这里有一个明确的工作负载可供比较。假设一个摘要任务发送 3,600 个输入 token,返回 500 个,共 1,000 次。在 CheapTokens 上,DeepSeek-V4-Flash(380K 上下文,每次回答最多 32,768 个输出 token,任何时段同一价格)约为 $0.104,GLM-5.3-Flash(390K 上下文)约为 $0.092。表格把输入和输出一起计算,并附有一列 OpenRouter 用于比较:

模型 每次请求 1,000 次请求 OpenRouter,1,000 次请求
GLM-5.3-Flash $0.000092 $0.092 $0.187
DeepSeek-V4-Flash $0.0001 $0.104 $0.249
MiniMax-M2.7 $0.00026 $0.255 $1.68

每次请求 3,600 个输入 token 和 500 个输出 token。每 100 万 token 的价格为当前价格,会随我们的资费一同变动;OpenRouter 列显示相同工作负载的参考价格。

API 不在乎你的缓存类型、显存或构建参数;没有什么需要回退到 CPU。先发送几个你真实的提示词,自己判断答案。这一步很小:该端点使用 OpenAI Chat Completions 格式,所以大多数本地客户端只需要一个新的 base URL。

bash
curl https://api.cheaptokens.dev/v1/chat/completions \
  -H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "deepseek-ai/DeepSeek-V4-Flash-0731", "messages": [{"role": "user", "content": "Summarize the text below in 5 bullets: ..."}], "max_tokens": 500}'

完整设置见 CheapTokens 文档,当前各模型价格在定价页面。想更深入地了解硬件与 API 的对比,请参阅在本地运行 DeepSeek 还是使用 API。如果你想了解输入和输出如何分别计费,输入 vs 输出 token 有相关说明。

按什么顺序处理#

排序

将故障排查步骤排序

  1. 对 q8_0 和 q5_1 运行 llama-bench,并观察 nvidia-smi
  2. 将两种缓存类型都切换为 q8_0
  3. 检查上下文长度是否仍能放进显存
  4. 只有在你需要 q5_1 时才用 GGML_CUDA_FA_ALL_QUANTS=ON 重新构建
  5. 如果本地配置仍不值得投入,就在 API 上比较同样的提示词

关于来源还有一点提醒:整个诊断基于一个公开 issue 和一块 GPU。把 1348 和 38.4 当作那里发生的情况,在相信任何一个之前先测量你自己的显卡。