工具大全
亲手实测作者:Coocon2026年10月3日16 次阅读约 11 分钟阅读

llama.cpp 部署 llama-server 还是用 ollama?Mac mini 24GB 同一个 GGUF 实测:ollama 底下跑的就是 llama-server

问题背景

在 Mac 上跑本地模型,绕不开一个选择:直接用 llama.cpp 的 llama-server,还是装 ollama?

网上的对比大多停留在「ollama 简单,llama.cpp 灵活」。真部署过就会发现,影响结果的往往不是哪个更快,而是一些默认值:上下文有多长、超长了会怎样、几个请求能同时跑、内存占多少。这些默认值不同,同一个模型在两边的表现就会完全不同。

这次在一台 Mac mini M4 24GB 上,让两边加载同一个 GGUF 文件,用同一套脚本、走同一个接口(OpenAI 兼容的 /v1/chat/completions,流式)逐项测:部署、接口、速度、默认上下文、长文、并发、内存,以及 27B 模型在 24GB 机器上的表现。

问题分析

开测前先看了一眼进程,结论在第一步就出来了:ollama 0.35.1 的推理进程,就是它安装包里自带的 llama-server。

ollama serve 的子进程是 bin/ollama/llama-server,参数里有 -c 4096 -np 1 图:测速脚本在 ollama 运行时采到的进程表(路径已缩写)。上面是 ollama 官方库里的 qwen3:4b,下面是我用 ollama create 导入的 Qwen3.8 GGUF。

ollama 解压后的目录里能看到 llama-server(--version 显示 0.5.0-dev (build 1, commit 6f767fe96)),还有 mlx_metal_v3/v4 等 MLX 相关文件。加载 GGUF 模型时,ollama serve 会拉起这个 llama-server 当 runner,传给它的关键参数是:

ollama 传的参数 含义
-c 4096 -np 1 上下文 4096,只有 1 个并发槽
--no-jinja --chat-template chatml 官方库模型由 ollama 自己套模板(导入的 GGUF 没有这两个参数)
--context-shift --keep 4 生成写满上下文时丢掉旧 token 继续生成
-b 512 -ub 512 逻辑批大小 512,llama-server 默认是 -b 2048 -ub 512
--no-webui 关掉 llama-server 自带的网页界面

所以这道题不是「两个引擎谁快」,而是同一个 llama-server,ollama 替你填了哪套参数、包了哪层管理。下面的实测基本都在验证这件事。

技术方案与选型

  • 版本:llama.cpp 官方 release b11376(2026-10-03,macos-arm64 预编译包);ollama 官方 release v0.35.1(2026-09-29,ollama-darwin.tgz)。两个都解压到临时目录运行,没有动本机原来装的 brew ollama 0.19。
  • 模型,两边都是同一个文件:
    • 4B:ollama 官方库 qwen3:4b 的 GGUF blob(2.5GB,Q4_K_M,元数据名为 Qwen3 4B Thinking 2507)。llama-server 直接 -m 加载这个 blob(硬链接,不另占空间)。
    • 27B:本机已有的 Qwen3.8-27B-UD-Q4_K_S.gguf(15.4GB)。ollama 用 ollama create 从这个文件导入,llama-server 直接加载。
  • 测法(Python 脚本,标准库实现):每个 case 起一个服务,依次测冷启动、顺序解码(长文写作,max_tokens 300 / 27B 为 200,各 5 轮 / 3 轮)、预填充(约 2,500 token 的值班记录,各 5 / 3 轮)、长文找针(开头埋一句「暗号是青鸟-7421」,后面堆满无关文字,结尾提问;4B 约 15.7k token,27B 约 7.6k token)、并发(4B 4 路 / 27B 2 路同时发)、内存(macOS footprint)。每个 prompt 前加随机 UUID,避免命中前缀缓存。temperature 0。
  • 读数口径:解码速度 =(completion_tokens − 1)÷(最后一个流式片段时间 − 第一个片段时间);预填充速度 = prompt_tokens ÷ 首 token 时间,属于客户端计时,含 HTTP 开销。内存用的是 footprint,不包含 mmap 映射的权重文件,看的是 KV 缓存、计算缓冲区这类额外开销;ollama 计入 serve 和 runner 两个进程之和。

实测过程

1. 部署:都是一条命令,差别在默认值

# llama.cpp:下载 release 包解压即可运行;模型加载完才开始监听
./llama-server -m Qwen3.8-27B-UD-Q4_K_S.gguf --host 127.0.0.1 --port 8080

# ollama:先起服务,模型在第一次请求时才加载
ollama serve                         # 默认监听 127.0.0.1:11434
ollama create qwen38-ud -f Modelfile # Modelfile 只有一行:FROM /path/to/Qwen3.8-27B-UD-Q4_K_S.gguf

两点实测观察:

  • ollama 导入 GGUF 会复制一份。ollama create 用了 17.5 秒,把 15GB 文件拷进了它自己的模型目录,同一个模型会占两份磁盘。导入后 ollama show 正确识别出了 tools、thinking 能力,说明它从 GGUF 里读出了模板。

  • 反过来不一定行。拿 ollama 官方库 qwen3.5:27b 的 blob 给上游 llama-server 加载,直接报错退出:

    error loading model hyperparameters: key qwen35.rope.dimension_sections has wrong array length; expected 4, got 3
    

    qwen3:4b 的 blob 没问题。ollama 自带的 llama-server 和上游不是同一个 commit,GGUF 元数据约定有出入,所以「把 ollama 下好的模型拿给 llama.cpp 用」不是总能成功。

启动时间:模型文件已在系统缓存里时,llama-server 4B 用 -c 4096 0.6 秒就绪,用默认配置约 3 秒(要先分配 14GB 的 KV 缓存)。ollama 服务 0.20.6 秒就能响应,但模型在首个请求时才加载,4B 首 token 1.21.4 秒。27B 的 15GB 文件如果不在缓存里,两边首次加载都要 14~16 秒。另外,新下载的二进制第一次运行明显更慢(llama-server 17 秒、ollama 9.5 秒,第二次分别是 3.8 秒和 0.3 秒),应是 macOS 对新二进制的首次运行检查(推测),和引擎无关,测启动时间要剔除第一次。

2. 接口:都兼容 OpenAI,细节不同

两边都提供 /v1/chat/completions,同一个客户端改个 base URL 就能切换。我的脚本对两边发的是完全相同的请求体(stream: true + stream_options.include_usage),两边都返回了 usage。差别:

  • llama-server 用 --alias 给模型起名,请求里填这个名字(本次填的是 m);流式最后一个片段带 timings(服务端统计的 prompt/生成速度),压测很方便。
  • ollama 的 model 填模型列表里的名字(如 qwen3:4b),服务会按名字加载对应模型;OpenAI 兼容接口的流式响应里没有 timings,要看服务端耗时得用原生接口 /api/chat。
  • ollama 默认把模型在内存里保留 5 分钟后卸载(官方 FAQ:By default models are kept in memory for 5 minutes),之后的请求要重新加载。llama-server 进程活着,模型就一直在内存里。

3. 速度:同参数下一模一样

两边同一个 GGUF 的速度、上下文、长文、并发与内存读数汇总 图:全部 case 的读数汇总。解码为 5 轮 / 3 轮中位数。4B 默认配置那两行的长文与内存读数来自单独的长文 case(B1/B2)。

模型 解码 tok/s(llama-server / ollama) 预填充 tok/s,客户端计时
4B,各自默认 36.5 / 36.3 376 / 367
27B,各自默认 6.3 / 6.3 56 / 57
27B,都设 16k 单槽 6.3 / 6.3 57 / 57

27B 的 6.3 tok/s 和本站之前测 Qwen3.8-27B 纯自回归基线的 6.06.5 tok/s 一致。4B 预填充上 ollama 慢约 2%,我把 llama-server 改成 ollama 同款 -c 4096 -np 1 后,服务端统计是 384389 tok/s,仍略高于 ollama。剩下的差别可能来自 ollama 传的 -b 512(llama-server 默认 -b 2048),这一点我没有单独测,属于推测。总之差距在噪声边缘,选哪个都不会因为速度后悔。

4. 默认上下文:4096 和「能开多大开多大」

这是差别最大的一项。

  • ollama 默认 4096。官方 FAQ 写的是 By default, Ollama uses a context window size of 4096 tokens,服务日志里也有一行 vram-based default context total_vram="17.8 GiB" default_num_ctx=4096,说明这个默认值是按显存档位算出来的,这台机器落在 4096 档。
  • llama-server 默认 -c 0,也就是用模型自己的上下文长度,再由默认开启的 --fit 按显存往下压。这份 4B 模型自带 262,144 的上下文,--fit 压到 101,120,开 4 个槽共享。27B 被压到 30,208。

于是同一条约 1.57 万 token 的长 prompt,两边结果完全不同:

ollama 静默截断到 2050 token;llama-server 同参数下返回 400;ollama 跑导入模型时 400 原样透传 图:同样是 4096 上下文,三种处理方式。黄色那行是 ollama 服务日志里唯一的提示。

  • ollama + 官方库模型:HTTP 200,usage.prompt_tokens 只有 2050。开头的暗号被截掉了,模型输出了 1,333 个 token,没有给出暗号。服务日志里只有一行 level=WARN msg="truncating input prompt" limit=2050 prompt=15742 keep=4 new=2050,API 调用方完全感知不到。
  • llama-server 用同样的 -c 4096 -np 1:直接 400,request (15742 tokens) exceeds the available context size (4096 tokens), try increasing it。
  • ollama + 自己导入的 GGUF(27B,7,596 token):不截断,400 原样透传给调用方。原因在上面那张参数表里:官方库模型由 ollama 自己套模板(--no-jinja --chat-template chatml),截断也是 ollama 在这一层做的;导入的模型交给 llama-server 处理,就走了 llama-server 的报错逻辑。

把上下文开够以后,两边都能找到针:llama-server 默认配置、llama-server -c 32768、ollama OLLAMA_CONTEXT_LENGTH=32768、27B 两边都设 16k,全部答对了「青鸟-7421」。

同一个 ollama,换个模型来源,超长 prompt 的行为就从「静默截断」变成「报错」。 用 ollama 做 RAG、代码库问答、长文总结时,最容易出问题的就是前一种:结果看起来正常,其实模型只看了一半。

5. 并发:默认 4 槽并行 vs 默认排队

同时发 4 个写作请求(4B,每个 200 token):

每路首 token(秒) 每路解码 tok/s 总吞吐 tok/s
llama-server 默认(4 槽) 2.36 / 2.37 / 2.37 / 2.37 11.6 40.9
ollama 默认(1 槽) 0.27 / 6.0 / 11.7 / 17.4 36.3 34.9
llama-server -c 32768(4 槽共享) 1.02 × 4 11.6 44.0
ollama OLLAMA_CONTEXT_LENGTH=32768 + OLLAMA_NUM_PARALLEL=4 7.53 × 4 11.8 32.7

llama-server 的并行在 4B 上换来约 17% 的总吞吐,代价是每一路都慢到三分之一。ollama 默认排队,第 4 个请求要等 17 秒才出第一个字。27B 上并行的收益更小:2 路总吞吐 6.86 对 5.9 tok/s,每一路掉到 3.8 tok/s。

一个人用、一次只发一个请求,排队反而让单个请求最快;多个 agent / 多人共用时,才需要并行槽。

6. 内存:同样写「32k + 4 并发」,内存差了 3 倍

配置(4B) 实际传给 llama-server 的参数 footprint
ollama 默认 -c 4096 -np 1 0.7 GB
ollama OLLAMA_CONTEXT_LENGTH=32768 -c 32768 -np 1 4.7 GB
ollama 32k + OLLAMA_NUM_PARALLEL=4 -c 131072 -np 4 21.0 GB
llama-server -c 32768(自动 4 槽) -c 32768,kv_unified = 'true' 6.9 GB
llama-server 默认 -c 0 → fit 到 101120,4 槽 14.1 GB(只跑完长文 case 时) / 18.3 GB(跑完整套测试后)

两个容易踩的点:

  • ollama 的 OLLAMA_CONTEXT_LENGTH 是每一路的长度,开并发会按路数相乘(官方 FAQ:Required RAM will scale by OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH)。llama-server 的 -c 是总量,开了统一 KV(kv_unified)后由各槽共享。同样写「32k、4 并发」,ollama 保证每路都有 32k,llama-server 是 4 路一共 32k。语义不同,内存差了 3 倍。
  • llama-server 默认配置在 24GB 机器上会占掉大半内存,哪怕模型只有 2.5GB。原因有两个:--fit 把 KV 开到显存上限附近(默认只留 1024 MiB 余量),以及默认 --cache-ram 8192 会在内存里缓存最多 8GB 旧 prompt 的状态,请求跑得越多占得越多。同一个默认配置,只跑完长文 case 时是 14.1GB,跑完整套测试后是 18.3GB。

27B 的情况:llama-server 默认(30,208 上下文)7.9GB,ollama 默认 5.0GB,两边都设 16k 时都是 6.8GB。Qwen3.8 是混合注意力,每 4 层里只有 1 层是全注意力,KV 缓存比 4B 那种全注意力模型小得多,所以 27B 的额外开销反而比 4B 默认配置还低。

顺带一个反例:ollama 官方库的 qwen3.5:27b(/api/ps 报 17.2GB,runner 额外挂了 --mmproj 视觉模块)在这台机器上,/api/ps 显示 size_vram 15.6GB,也就是约 1.6GB 放在了 CPU 上(/api/ps 的字节数换算,十进制 GB),解码 5.8 tok/s,footprint 21.1GB。17GB 级的模型放在 24GB 机器上已经到边缘了;同为 27B 的 UD-Q4_K_S(15.4GB)能全部放进 GPU。

7. 自带网页界面:llama-server 有,ollama 关掉了

llama-server 默认就带 Web UI,浏览器打开 http://127.0.0.1:8080 就能对话,还会显示 token 数和速度:

llama-server 自带 Web UI,回答下方显示 856 tokens、24s、35.03 t/s 图:llama-server b11376 自带的 Web UI,模型为 4B(-c 8192 -np 1)。截图停在思考阶段;这个 4B 模型在思考里把 llama-server 和 ollama 的关系猜错了,仅作界面演示。

ollama 启动 runner 时显式传了 --no-webui,命令行版本没有网页界面(macOS 桌面 app 自带聊天窗口,不在这次测试范围)。想给 ollama 配网页界面,需要另装 Open WebUI 这类前端。

实践效果

怎么选

你的情况 选择
想要「装上就能用」、模型从官方库拉、经常切换模型 ollama,但先把 OLLAMA_CONTEXT_LENGTH 调大
自己管 GGUF 文件、要精确控制上下文 / 并发 / 内存,或者想要自带网页界面 llama-server
给 Claude Code 类工具、RAG、长文总结当后端 两个都行,关键是上下文要显式设够;ollama 官方库模型超长会静默截断,必须注意
多个 agent 或多人同时调用 llama-server 默认就有并行槽;ollama 要设 OLLAMA_NUM_PARALLEL,并且按「每路长度 × 路数」算内存

24GB Mac 上的推荐启动参数

# llama-server:显式设上下文和槽数,别让 --fit 和 prompt 缓存吃满内存
./llama-server -m model.gguf -c 16384 -np 1 --cache-ram 2048 \
  --host 127.0.0.1 --port 8080

# ollama:服务端设默认上下文(命令行启动的写法)
OLLAMA_CONTEXT_LENGTH=16384 ollama serve

27B 用 -c 16384 -np 1 时,两边的 footprint 都是 6.8GB,加上约 14.3GB(15.4GB 文件)映射的权重,总共接近 21GB。24GB 机器上余量不多,跑之前最好关掉吃内存的大应用。

--cache-ram 2048 这个值我没有单独测,是按「默认 8GB 在 24GB 机器上太大」给的保守值。想完全关掉 prompt 缓存可以设 0(官方说明:0 - disable)。ollama 桌面 app 不经过你的 shell,环境变量要按官方 FAQ 用 launchctl setenv 设置后重启 app。单次请求也可以在原生接口里用 options.num_ctx 覆盖默认上下文(官方 FAQ 的写法)。

安全提醒

  • llama-server 启动时会打印 security: no API key is set and CORS allows all origins。默认只听 127.0.0.1 没问题;如果改成 --host 0.0.0.0 对局域网开放,记得加 --api-key。
  • ollama 默认绑定 127.0.0.1:11434(官方 FAQ)。按教程设 OLLAMA_HOST=0.0.0.0 之后,局域网里谁都能调用,它本身没有鉴权。

踩坑

  • ollama 静默截断只在官方库模型上发生。 同一个 ollama,导入的 GGUF 超长时返回 400,官方库模型返回 200 加一半的 prompt。排查「模型怎么没看到前面的内容」时,先看 usage.prompt_tokens 对不对,再看 ollama 服务日志里有没有 truncating input prompt。
  • OLLAMA_NUM_PARALLEL 会让内存乘以路数。 4B 设 32k 加 4 并发就是 21GB;换成 27B,24GB 的机器不可能装得下。
  • llama-server 默认配置会把内存占满。 4B 模型 footprint 14.1~18.3GB。在共享的 Mac 上跑,记得显式设 -c 和 --cache-ram。
  • ollama 的 blob 不保证能给上游 llama.cpp 用。 qwen3.5:27b 实测报 rope.dimension_sections 长度不符。要两边共用文件,用上游 GGUF 让 ollama 导入。
  • 这份 qwen3:4b 是 Thinking 版,/no_think 不起作用。 第一轮长文测试我只给了 32 个 token 的输出预算,全部花在思考上,两边都答不出来,差点误判成「两边都截断了」。改成 2048 才看清真实结果。
  • 实验本身的坑:有一组 llama-server 在长文请求上返回 400,测速脚本没接住异常直接退出,留下的 llama-server 成了孤儿进程,占着 8080 端口和十几 GB 内存。下一组 ollama 在内存被占的情况下跑,再下一组 llama-server 的就绪检查直接连到了这个孤儿进程。这三组读数全部作废(保留原始记录),脚本改成 try/finally 收尾加启动前检查端口后重跑。另外,footprint 不含 mmap 的权重,llama-server 的 prompt 缓存又会随测试推进增长,内存对比必须在相同阶段采样,上表已注明。

相关阅读

这类实测,每周六汇总一封

订阅码农早餐:每天 8:00 一封 AI 编程早报,每周六另附本周 Claude Code / Codex / 本地模型的实测和踩坑汇总。

相关文章

果蝇大脑怎么下载?13.9 万个神经元,我在 Mac mini 上 33 秒跑完一次实验

果蝇大脑怎么下载?13.9 万个神经元,我在 Mac mini 上 33 秒跑完一次实验

「果蝇大脑下载」下载的其实是两个文件:一张 13.9 万个神经元的清单,和一张 1,509 万行的连接表——把表里的突触数加起来正好是 Nature 论文里的 5,450 万。我照官方仓库从零走了一遍:克隆 57 秒、装依赖 21 秒、刺激糖味觉神经元的示例 33 秒跑完,伸喙运动神经元 MN9 放电 80.2 Hz。路上踩到两个坑:README 教你换成公开版 v783 数据,但示例里的 21 个神经元有 1 个在 v783 里已经不存在,直接 KeyError;输出目录不存在则要等模拟跑完才报错。最后用「逐个沉默神经元」做了一次真实果蝇上很难做的实验:沉默一个下行神经元 DNge031,进食输出反而升了 30%。

开源实测+6
hands-on2026年9月28日8 min
167
把 13.9 万个果蝇神经元搬进 Mac mini:不给任何输入,它会自己动起来吗?

把 13.9 万个果蝇神经元搬进 Mac mini:不给任何输入,它会自己动起来吗?

Eon Systems 说他们『上传』了一只果蝇。我用同一套开源组件在 Mac mini 上把它拼了出来:13.9 万神经元、约 5,450 万突触的全脑 LIF 模型接上 MuJoCo 物理身体。给输入时它能做事——腿尝到糖 0.66 秒后开始进食,看到逼近的黑球 1.89 秒触发巨纤维后退。但不给任何输入时,原模型一个脉冲都不发。补上膜噪声和放电适应后,它确实自己『动』了起来:60 秒里出现静止、前进、后退、梳理、惊跳 140 个片段;可节律像时钟(阵发间隔 CV 仅 0.05),82% 的前进片段恰好等于解码器下限 0.3 秒。第二版把适应时间常数改成异质、读突触输入而不是脉冲,间隔 CV 升到 1.2,前进片段最长 4.6 秒。文章给出全部实测数字,也讲清楚哪些是连接组自己的,哪些是我加的。

实测flywire+7
hands-on2026年9月26日11 min
94
MiniMax T2A v2 vs Azure Neural TTS 实测:同一段中英文本,延迟差了 6~10 倍

MiniMax T2A v2 vs Azure Neural TTS 实测:同一段中英文本,延迟差了 6~10 倍

我的视频工厂里同时接了 MiniMax 和 Azure 两条 TTS 链路,一直凭感觉觉得 MiniMax 更快,这次把它坐实:同一段中英文本、各 5 轮、统一输出 24kHz/单声道/16bit,MiniMax 合成中位延迟 1.0~2.2 秒、RTF 0.08~0.18(比实时快 5~12 倍),Azure 中位 6~21 秒、RTF 逼近 1.0,且长尾抖到 27 秒。两个原因都有证据:一是 Azure Neural 本身的合成节奏就接近实时,二是从大陆直连它的 eastasia 端点要跨太平洋,握手 TLS 抖到 0.8 秒。文末给出真实抓包、波形对照,以及『什么时候该忍 Azure』的判断。

ttsrtf+6
hands-on2026年9月5日8 min
155

24GB Mac mini 能跑多大的本地大模型?内存账、实测速度与提速方案汇总

一台 24GB 统一内存的 Mac mini 到底能跑什么模型?答案是:27B 的 4-bit 量化就是天花板,且我们真的把 Qwen3.8-27B 在 M4 丐版上完整跑通了——峰值内存 19.4GB,投机解码后 11.7-12.2 tok/s。本文汇总这台机器上的全部实测:各参数量级的内存账、速度预期、三条提速路线的真实效果(DFlash 2 / 原生 MTP / MLX vs llama.cpp),以及量化档位怎么选。

qwendflash+6
ai-tutorials2026年9月4日5 min
592