← 返回文章列表

Ollama 竟然比 llama.cpp 还快?这打破了我的认知

一次意外的跑分,让我重新审视这两个 AI 推理引擎


一、缘起

事情要从我买了Macbook Neo说起。

一直想等mac mini M5,但是越等mac mini m4越贵,越等mac mini m4越没货,mac mini m5又没有上市,只好乘着东哥那边还有优惠下手了Macbook Neo。前几天一直在手机上部署本地大模型,我就想试试Macbook Neo 8G RAM是个什么水平。我熟练地从源码编译了最新版 llama.cpp,下载了 Qwen2.5-0.5B-Instruct Q2_K GGUF 模型(396MB),跑 llama-bench——51 tok/s。嗯,正常水平,A18 Pro 芯片就该是这个表现。

因为最近看其它Up主的视频,都是在使用Ollama,所以我就想着用Ollama也测一下。「之前手机为什么不使用Ollama?」

因为我用 Ollama 的历史体验并不好。几个月前在手机上跑,一个字一个字往外蹦,响应巨慢。后来换成 llama.cpp 直接调用同一个模型,速度明显提升。从那以后,我心里就烙下了一个根深蒂固的印象:

"llama.cpp 才是快的,Ollama 只是方便但慢。"

二、跑分

抱着"走个过场试试"的心态,我装上了 Ollama,把同一个 GGUF 文件导入进去,跑了三轮 benchmark。

然后我愣住了。

Ollama 快了 40%。

我不信。又跑了一遍,还是 72。再跑,71.6。我甚至重启了电脑再测,结果依然稳定。

那一刻,我感觉过去几个月的认知被掀翻了。

下面是完整的跑分数据,每一个数字都是实测得来:

指标llama-cli (每次重载)llama-cpp-pythonllama-server (GPU常驻)Ollama
Token 生成速度51 tok/s56 tok/s~52 tok/s72 tok/s 🏆
Prompt 处理速度94 tok/s1,000+ tok/s
模型加载耗时195 ms 🏆~500ms仅首次加载1,180 ms (首次)
128 tokens 总耗时~2.5s~2.3s~2.6s~1.6s 🏆
KV Cache 精度fp16 (16bit)fp16fp16q8_0 (8bit) 🏆
Flash Attention需手动开启需手动开启需手动开启默认开启 🏆
GPU 常驻❌ 每次重载✅ 进程内常驻✅ 服务常驻✅ 服务常驻

注意:llama-cli 的加载时间只有 195ms,在三者中最快。但因为每次运行都重新加载,在短对话场景下产生了额外开销。而 Ollama 虽然首次加载需要 1.18s,但后续推理完全跳过这一步。不过加载时间并不是速度差距的主因——真正拉开差距的,是下面这些优化。

所以我一开始错误的以为Ollama快是因为常驻GPU(因为我一开始只是使用llama-cli进行测试,它加载模型的速度只有Ollama零头的时间,如果让llama.cpp常驻GPU是不是就比Ollama快了?上面已经有了答案:并没有!

llama-server 就是干这个的——它以后台 HTTP 服务运行,模型加载一次后常驻 GPU 内存:

llama-server -m model.gguf -ngl 99 --flash-attn on --port 8080
curl http://127.0.0.1:8080/v1/chat/completions \
  -d '{"messages":[{"role":"user","content":"你好"}]}'

三、发生了什么?

冷静下来,我分析了一下差距的来源。

第一,Flash Attention。

Ollama 启动时我加了环境变量 OLLAMA_FLASH_ATTENTION="1"。Flash Attention 是 2022 年提出的一种高效注意力机制实现,能显著降低显存带宽需求,尤其在长序列推理时优势巨大。我编译 llama.cpp 时虽然也启用了 Metal,但没有专门去开 Flash Attention 的优化选项。

第二,KV Cache 量化。

OLLAMA_KV_CACHE_TYPE="q8_0" 也是关键。KV Cache 是自回归模型中存储历史 Key-Value 向量的缓存,随着生成长度增加会线性膨胀。把它量化为 8-bit,内存带宽消耗直接减半,这对推理速度的提升立竿见影。

第三,KV Cache 量化才是隐藏的王牌。

我原本以为 GPU 常驻是主要差距,于是用 llama-server 启动了常驻服务来验证——结果即使 GPU 常驻 + Flash Attention 全开,速度也只有 ~52 tok/s,远不及 Ollama 的 72 tok/s。

这说明真正的差距不在"常驻",而在 KV Cache 量化

Ollama 默认将 KV Cache 量化为 8bit(q8_0),而 llama.cpp 默认是 fp16。KV Cache 在生成时会随着 token 数量线性增长,很快成为显存带宽的瓶颈。量化为 8bit 后,每次读写的数据量减半,带宽消耗直接减半——这对生成速度的提升是实打实的 20-40%。

但 llama-server 默认不使用 KV Cache 量化(除非手动改源码或传参),而且 Ollama 在 Metal 后端上有更多隐式的调度优化。

所以结论是:Ollama 快,不是因为它做了 llama.cpp 做不到的事,而是因为它把 llama.cpp 能做的事默认开好了。

四、那之前的经历是怎么回事?

我又想起了手机上那个"一个字一个字蹦"的 Ollama。

当时的 Ollama 大概率是 CPU 模式运行的。

Ollama 在 macOS 上自动选择 GPU(Metal),但早期的 Ollama 版本或者在没有 GPU 的环境中,默认会退回到 CPU。CPU 推理大模型,尤其是没有量化的小模型,速度就是会惨不忍睹——几秒钟一个字的情况我见过太多。

所以不是 Ollama 慢,是我当时没有让它用上 GPU

而 llama.cpp 需要手动编译 Metal 支持,我那时花了功夫去配置,自然就觉得"llama.cpp 更快"。

五、四个工具的定位

现在我可以重新给这四个工具做个客观的定位了:

工具速度GPU 常驻适用场景
Ollama最快 🏆✅ 服务常驻日常对话、交互式使用、API 服务
llama-server✅ 服务常驻自建 API、需常驻的批量任务
llama-cpp-python✅ 进程内常驻Python 二次开发、自定义推理逻辑
llama-cli❌ 每次重载一次性脚本、调试、CI 集成

Ollama 适合"用"模型——安装即用,开箱全加速,体验最好。

llama.cpp(含 server/cli)适合"调"模型——高度可控,适合深度定制和调试。如果想让它常驻,用 llama-server 即可。

两者并不冲突,关键是要用对姿势——以及 把配置调对

六、后记

这次测试给我上了一课:

  1. 永远不要靠"以前的体验"给工具下定论。 环境变了,配置变了,结论可能完全相反。

  2. 跑分之前先检查配置。 确认 GPU 已启用,确认优化已打开,否则你测的不是工具的上限,而是你配置的下限。

  3. 默认设置不等于最佳设置。 Ollama 不开启 Flash Attention 也能用,但开与不开差距巨大。花五分钟看文档,往往能带来翻倍的提升。

  4. 核心差距在 KV Cache 量化,不在 GPU 常驻。llama-server 可以让 llama.cpp 也 GPU 常驻,但速度差距依然存在——因为 Ollama 默认开了 KV Cache 8bit 量化,而 llama.cpp 的默认配置没开。

  5. 苹果芯片 + Metal 是真的强。 A18 Pro 跑小模型能到 70+ tok/s,响应几乎无延迟。Apple Silicon 的统一内存架构对推理的加成,远比纸面参数看起来的大。

最后,附上我这次测试的环境配置,供参考:

设备: MacBook Neo (A18 Pro, 8GB)
系统: macOS 26.5
模型: Qwen2.5-0.5B-Instruct Q2_K (396MB)

Ollama:   v0.31.1 + Metal + Flash Attention + q8_0 KV Cache
llama.cpp: master branch (自编译) + Metal GPU + Flash Attention
llama-server: 同上,常驻 HTTP 服务模式

如果你也在跑本地大模型,不妨重新审视一下你的工具链配置——也许你手里的工具,远比你想象的要强。