直接结论
Ollama、vLLM 与 llama.cpp 的能力有交集,但优化目标不同。 Ollama 适合低摩擦的本地模型管理与开发工作流,vLLM 适合由调度器驱动的 GPU 服务和分布式扩展,llama.cpp 适合 GGUF 可移植部署与底层运行时控制。不存在脱离工作负载的统一冠军:满足模型质量、延迟、吞吐、硬件、安全与 API 契约的最小运维栈,才是合理选型。
本文只回答工作负载选型与评测。如果需要 Modelfile、原生 API、结构化输出和模型驻留细节,请阅读 Ollama 本地部署指南,避免两篇文章争夺同一搜索意图。
真正需要比较的六层契约
本地大模型服务是一条契约链,而不是一个推理二进制。没有固定整条链时,所谓框架对比往往实际比较了不同模型、模板或量化器。
六层边界分别是:
- 模型契约:权重修订、Tokenizer、Chat Template、Adapter、量化和上下文策略。
- 执行运行时:CPU/GPU 后端、Kernel、内存放置和设备拓扑。
- 服务调度器:排队、批处理、准入、抢占和 KV Cache 分配。
- 协议表面:原生接口、兼容接口、流式响应、usage、取消和错误语义。
- 运维边界:鉴权、TLS、限流、租户隔离、可观测与回滚。
- 负载契约:输入输出长度分布、到达模式、质量目标和服务等级目标。
这个拆分可以避免两类常见错误:把 GGUF 与更高精度的 Hugging Face 权重当成同一个模型,以及用核心 Token 生成速度代替端到端服务容量。
Ollama、vLLM 与 llama.cpp 对比
三者的关键差异是运维意图,而不是某个固定并发人数。
| 决策维度 | Ollama | vLLM | llama.cpp / llama-server |
|---|---|---|---|
| 主要定位 | 本地开发与小型自托管服务的集成工作流 | 面向吞吐的 GPU API 与分布式服务 | 可移植 GGUF 运行时、边缘/CPU 和底层控制 |
| 模型工作流 | Ollama 模型库与 Modelfile 生命周期 | 常见为 Hugging Face 风格模型与 Tokenizer 仓库 | GGUF 文件与 llama.cpp 转换工具链 |
| 并发服务 | 内存允许时并行请求,并由有界队列承接等待 | 调度器驱动的批处理与显式服务控制 | llama-server 并行 slot 与连续批处理 |
| 内存模型 | 上下文、并行度、驻留模型和缓存类型共同决定容量 | 分页 KV Cache、调度上限和并行拓扑共同决定容量 | 上下文、slot、batch、缓存类型和卸载共同决定容量 |
| 硬件范围 | 通过封装工作流支持 CPU、部分 GPU 与 Apple Silicon | 以加速器服务为核心,具体平台以当前官方文档为准 | 广泛 CPU/GPU 后端和设备相关构建 |
| 扩展方式 | 多模型驻留及可用 GPU 放置,需观察实际分配 | 张量并行、流水线并行与多节点部署 | 在支持的后端使用 layer、row 或 tensor split |
| 兼容接口 | 部分 OpenAI API 与 Ollama 原生 API | 多种 OpenAI 兼容服务接口 | OpenAI 兼容路由及其他已文档化路由 |
| 最强选型理由 | 本地工作流阻力最低 | 服务调度与生产指标更完整 | 可移植性与运行时控制最强 |
| 主要风险 | 把开发友好型本地服务直接暴露到生产网络 | 未定义 SLO 就过早调优复杂 GPU 服务 | 自行承担构建、模型、服务和平台细节 |
Ollama 并非只能顺序处理,llama.cpp 也不是只能自行封装单用户进程。当前官方文档分别说明了 Ollama 并行请求,以及 llama-server 的并行解码与连续批处理。反过来,vLLM 更强的调度能力也不保证在所有模型和负载下拥有更低延迟或成本。
按工作负载选型,而不是按用户数口号
选型应由主要约束和实际运维责任决定。
| 工作负载 | 建议起点 | 原因 | 采用前必须证明 |
|---|---|---|---|
| 笔记本 Prompt 调试与本地应用开发 | Ollama | 模型获取、生命周期和本地 API 已集成 | 模板、上下文、结构化输出和交互延迟正确 |
| CPU、Apple Silicon、边缘或自定义设备 | llama.cpp 或 Ollama | GGUF 与广泛本地后端是核心能力 | 目标设备上的质量、功耗、内存和温度 |
| 单 GPU 内部 API 与中等共享负载 | 同时测试 Ollama 与 vLLM | 两者都可能适用,排队与模型工作流可能更重要 | 尾延迟、过载行为、内存余量和运维成本 |
| 变长请求的共享 GPU 服务 | 优先把 vLLM 纳入候选 | 连续调度与分页缓存针对该类负载 | 真实到达率下的 Goodput、抢占与质量一致性 |
| 模型超出单个加速器容量 | vLLM 或经过验证的 llama.cpp 分片 | 需要显式多设备拓扑 | 模型装载、互联开销、启动日志和故障恢复 |
| 多节点推理服务 | 三者中优先评估 vLLM | 有明确张量/流水线并行路径 | 私有网络、拓扑基准、故障处理和回滚 |
| 固定 GGUF 制品的嵌入式产品 | llama.cpp | 部署面小且运行时控制直接 | 可复现构建、制品来源和端侧验收测试 |
不要把表格退化为“5 个用户就必须上 vLLM”。5 个长上下文请求可能比数百个短请求更耗内存;当到达率低于服务能力时,批处理甚至不会形成。容量规划应该看 Token 与时间,而不是“用户”这个标签。
并发、排队与 KV Cache
并发会增加常驻 Token 状态,因此上下文长度与并行请求必须一起规划。KV Cache 容量指南解释了为什么仅看模型权重无法预测服务容量。
Ollama 的并发边界
Ollama FAQ 明确说明,在内存允许时可以并行处理单个模型的请求。OLLAMA_NUM_PARALLEL 控制单模型最大并行请求数,内存需求随并行度与上下文长度的乘积增长;OLLAMA_MAX_QUEUE 控制过载拒绝前的排队上限。Ollama 还可以同时驻留多个模型;当模型无法装入单个 GPU 时,可能分布到可用 GPU。
这些能力不等于 vLLM 的调度器或分布式执行模型。必须在目标主机观察 ollama ps、请求时间、拒绝行为、处理器放置与真实常驻内存。
vLLM 的调度边界
vLLM 将服务调度器与分页 KV Cache 分配结合。其并行指南区分单 GPU、单节点张量并行,以及跨节点张量并行加流水线并行。启动日志会报告可用 KV Cache 容量与估算最大并发,但这是容量信号,不是应用 SLO。
PagedAttention 原论文报告的收益只适用于论文中对比的系统、模型与工作负载,不能转换成当前 vLLM 相对当前 Ollama 的固定倍数。合理做法是把分页视为减少碎片和重复的机制,再按真实序列分布测量效果。
llama.cpp 的服务边界
llama-server 官方文档包含并行解码、多用户、slot、连续批处理、缓存控制、指标、API Key 和多设备 split。llama-bench 则单独测量 Prompt Processing 与 Text Generation,并明确不包含 Tokenization 和 Sampling。前者用于服务评测,后者用于隔离运行时变化,不能混为同一个延迟指标。
先建立模型与量化等价性
如果换了模型后响应更快,这不是服务优化。对比前至少固定:
- 源模型与不可变权重修订;
- Tokenizer 文件与修订;
- Chat Template 与特殊 Token;
- Adapter 和权重合并状态;
- 量化方法、校准数据与制品校验和;
- 上下文与 RoPE 配置;
- 生成默认值、停止规则、随机种子和结构化输出策略。
Ollama 与 llama.cpp 常使用 GGUF,vLLM 常服务其他仓库格式与量化方案。即使制品来自同一个基础 Checkpoint,不同量化也可能改变指令遵循、工具调用、结构化输出、长上下文行为与 Token 概率。性能对比前必须先通过业务质量集。
模型量化指南说明了格式与精度权衡。不要引用统一的“保留 98% 质量”:结果始终依赖模型、量化器、校准、任务和指标。
OpenAI 兼容必须作为协议契约测试
“OpenAI 兼容”表示协议家族,不表示行为完全等价。Ollama 官方措辞是支持 OpenAI API 的部分能力;vLLM 文档列出了支持的 API、不支持或忽略的参数、Chat Template 要求和引擎扩展字段。llama-server 同样提供兼容路由,但支持范围与语义需要独立核对。
迁移契约至少覆盖:
| 契约区域 | 验证内容 |
|---|---|
| 服务发现 | 模型列表、服务模型名、别名与修订可见性 |
| 请求 | Endpoint、Role、内容类型、Stop、Tool、Response Format 与不支持参数 |
| 模板 | 最终渲染 Prompt、特殊 Token、缺少 Chat Template 时的行为 |
| 生成 | Temperature/Top-p 默认值、Seed、最大 Token 与服务端 Generation Config |
| 流式 | Event 帧、终止事件、usage 位置、断连与取消 |
| 响应 | Finish Reason、Tool Call、Logprobs、Token 计数和 Request ID |
| 失败 | 鉴权、非法输入、过载、超时、模型不存在与是否可重试 |
| 运维 | 健康/就绪、指标、日志脱敏、优雅退出与流量排空 |
只有这组用例通过后,改 base_url 才是可接受的最后一步。端点可访问并不能证明 Prompt、输出、计费或错误语义一致。
可复现的基准测试契约
有效的本地大模型基准必须复现生产到达过程,同时报告延迟与“按时完成的有效工作”。每份结果都应保存以下 Manifest:
benchmark:
hardware:
accelerator: "精确型号与数量"
interconnect: "PCIe、NVLink 或无"
cpu_ram_storage: "记录完整配置"
software:
os_driver_runtime: "不可变版本"
engine_revision: "Tag 或 Commit"
model:
source_revision: "不可变修订"
tokenizer_revision: "不可变修订"
artifact_sha256: "校验和"
quantization: "方法与参数"
chat_template_sha256: "校验和"
workload:
prompt_tokens: "分布,而非只有平均值"
output_tokens: "分布,而非只有上限"
prefix_reuse: "分布"
request_rate: "每秒请求数与到达模型"
max_concurrency: "客户端并发上限"
warmup_requests: "预热请求数"
generation:
temperature: 0
seed: 42
stop_policy: "固定策略"
slos:
ttft_ms_p95: "应用目标"
itl_ms_p95: "应用目标"
e2e_ms_p95: "应用目标"
至少测量:
- TTFT、ITL 或 TPOT、端到端延迟的 p50/p95/p99;
- 请求吞吐与输出 Token 吞吐;
- 成功、超时、取消和过载拒绝率;
- 满足 SLO 的 Goodput;
- 可用时记录排队、Prefill、Decode和抢占;
- 模型、KV Cache、Activation、主机内存与设备内存;
- 业务质量与结构化输出合法率;
- 将启动、模型加载与恢复作为独立冷路径指标。
vLLM 的 bench serve 可控制请求到达率、突发度、最大并发、预热、输入输出长度、分位指标与 Goodput SLO。Ollama 原生生成响应拆分模型加载、Prompt Evaluation 与 Token Generation 时间。llama-bench 拆分 Prompt Processing 与 Generation,但不含 Tokenization 与 Sampling。三者测量边界不同,因此公平比较仍需统一的外部压测器。
计算满足 SLO 的 Goodput
Goodput 只统计满足应用 SLO 的完成请求,避免系统通过接收大量迟到请求“做高吞吐”。下面的无依赖脚本读取归一化 JSONL Trace:
from __future__ import annotations
import json
import sys
from pathlib import Path
REQUIRED = ("ok", "ttft_ms", "itl_p95_ms", "e2e_ms")
def load_traces(path: Path) -> list[dict]:
traces = []
with path.open(encoding="utf-8") as handle:
for line_number, line in enumerate(handle, start=1):
if not line.strip():
continue
try:
trace = json.loads(line)
except json.JSONDecodeError as error:
raise ValueError(f"line {line_number}: invalid JSON") from error
missing = [key for key in REQUIRED if key not in trace]
if missing:
raise ValueError(f"line {line_number}: missing {missing}")
traces.append(trace)
if not traces:
raise ValueError("trace file is empty")
return traces
def goodput(traces: list[dict], duration_s: float) -> tuple[int, float]:
if duration_s <= 0:
raise ValueError("duration_s must be positive")
passed = sum(
bool(trace["ok"])
and float(trace["ttft_ms"]) <= 800
and float(trace["itl_p95_ms"]) <= 80
and float(trace["e2e_ms"]) <= 5000
for trace in traces
)
return passed, passed / duration_s
try:
source = Path(sys.argv[1])
duration = float(sys.argv[2])
count, requests_per_second = goodput(load_traces(source), duration)
print(f"SLO-passing requests: {count}")
print(f"Goodput: {requests_per_second:.2f} requests/s")
except (IndexError, OSError, ValueError) as error:
raise SystemExit(f"usage: python goodput.py TRACE.jsonl DURATION_S\n{error}") from error
所有引擎必须使用相同事件边界。服务原生指标可以补充 Trace,但不能悄悄改变 Arrival、First Token、Last Token 与 Completion 的定义。LLM 推理指南进一步解释了这些测量边界。
基线有效后再调优
调优应每次只改变一个受控维度。
Ollama 调优顺序
- 固定模型,并用
ollama ps查看真实处理器放置。 - 只配置业务需要的上下文长度。
- 逐步增加并行度,同时观察内存、排队与拒绝。
- 在支持的硬件上测试 Flash Attention 与 KV Cache 类型。
- 按复用率和加载延迟设置模型驻留时间。
vLLM 调优顺序
- 确认模型可装载,并记录启动日志中的 KV Cache 容量。
- 修改调度上限前,先建立请求到达率曲线。
- 长 Prompt 干扰 Decode 时再评估 Chunked Prefill。
- 只有在前缀复用分布真实时才评估 Prefix Caching。
- 根据模型装载与拓扑选择张量/流水线并行,并测量通信开销。
- 跟踪 Queue、KV Usage、TTFT、ITL、E2E、Preemption 与 Request Success。
llama.cpp 调优顺序
- 固定 Build Commit、Backend、GGUF 校验和、Thread 与设备放置。
- 用
llama-bench分开测试 Prompt Processing 与 Token Generation。 - 分别调整 Batch、Micro-batch、Cache Type、Context 与 Offload。
- 配置 Server Slot 与 Continuous Batching,再执行 HTTP 负载测试。
- 在边缘设备复查功耗、温度和持续性能。
投机解码、缓存量化与激进批处理都是实验变量,不是固定倍数。只有在不破坏质量与尾延迟门禁、且确实提高目标 Goodput 时才保留。
生产安全与运维边界
模型在本地运行只改变数据路径,不会自动满足隐私、安全或合规要求。生产边界仍需具备:
- 开发机只绑定 Loopback,服务端进入受控私有网络;
- 网关鉴权、授权、TLS、请求大小限制与限流;
- 租户感知队列、缓存隔离与日志脱敏;
- 模型制品 Allowlist、校验和、许可证与来源记录;
- 健康/就绪探针、Load Shedding、超时、取消和优雅排空;
- 默认不持久化敏感 Prompt 的审计事件;
- 固定镜像或 Commit、分阶段发布与经过演练的回滚;
- 多节点内部流量使用私有网络,因为分布式引擎流量不能默认安全地暴露到不可信网络。
Ollama 默认绑定本地地址,但修改绑定地址后,如果没有外部安全边界,就可能暴露未鉴权服务。llama-server 支持 API Key,但 Key 校验本身不等于租户隔离或传输安全。vLLM 指标很有价值,但指标名也有引擎生命周期,应与部署修订一起固定。
迁移与回滚门禁
只有模型行为、协议行为和运维行为全部通过,迁移才算完成。
- 冻结身份:记录源修订、Tokenizer、Template、Quantizer、Checksum 与生成默认值。
- 质量对齐:比较业务任务分数、结构化输出、工具调用、拒答与长上下文行为。
- API 对齐:重放成功、非法、流式、取消和过载请求。
- 负载验证:扫描到达率与上下文/输出分布,找到 SLO 饱和点。
- 运维验证:鉴权、指标、Trace、脱敏、Readiness、Drain、Restart 与模型加载。
- 灰度:按受控 Cohort 路由,为指标标记 Backend,禁止静默回退。
- 回滚:保留旧制品、配置、路由规则和恢复所需状态。
“开发用 Ollama、生产用 vLLM”的混合模式可以成立,但只有两端固定兼容模型契约时才不会产生环境漂移。若模板、量化或默认参数不同,快速本地原型就不是生产预演。
常见失败模式
最昂贵的故障通常来自无效对比或失控边界。
| 现象 | 常见根因 | 应补充的证据 |
|---|---|---|
| Token/s 很高但聊天体验差 | 排除了 Prompt Processing、Queue 或网络时间 | TTFT、ITL、E2E 与统一事件 Trace |
| 迁移后质量下降 | 制品、模板、Tokenizer、量化或默认值不同 | 身份 Manifest 与业务质量对齐集 |
| 只有并发时出现 OOM | 上下文乘并行序列超过缓存预算 | Resident Token Slots、Cache Usage 与队列上限 |
| 中位数正常但 p99 抖动 | 突发排队或长 Prefill 干扰 | Offered-load 曲线、Queue Time、Prompt 分布与抢占 |
| “兼容”客户端报错 | Field、Template、Stream、Usage 或 Error 不兼容 | 协议一致性用例 |
| 多 GPU 反而更慢 | 通信与拓扑开销占主导 | 分拓扑基准与设备/互联 Trace |
| 本地端点泄漏数据 | 服务无网关、TLS 或日志脱敏 | 网络清单、鉴权测试与日志审计 |
一手资料
- Ollama FAQ:并行请求、队列、模型驻留、GPU 放置、网络、Flash Attention 与 KV Cache 配置。
- Ollama OpenAI 兼容说明:官方文档化的兼容接口范围。
- Ollama Generate API:原生响应的耗时与 Token 计数字段。
- vLLM 并行与扩展:单 GPU、张量并行、流水线并行与多节点边界。
- vLLM OpenAI 兼容服务:Endpoint、参数、Template 与 Generation Config 行为。
- vLLM 生产指标:Queue、Cache、Prefill、Decode、TTFT、ITL、E2E 与请求信号。
- vLLM bench serve:到达负载、并发、分位数、Trace 与 Goodput 控制。
- llama.cpp Server:并行解码、连续批处理、路由、指标、Key、Slot 与设备控制。
- llama-bench:核心 Prompt Processing 与 Generation 测量边界。
- PagedAttention 原论文:分页 KV Cache 机制与原始实验范围。
常见问题
vLLM 一定比 Ollama 快吗?
不一定。vLLM 面向高吞吐加速器服务,但实际性能取决于模型、量化、硬件、请求长度、到达负载和 SLO。Ollama 在本地工作流中可能有更低运维成本。应在质量通过后比较 Goodput 和尾延迟,而不是引用孤立 Token/s。
Ollama 能并行处理请求吗?
可以。Ollama 通过 OLLAMA_NUM_PARALLEL 在内存允许时为单模型配置并行请求,并提供有界队列。并行度会放大上下文相关内存,因此必须测试具体模型、上下文和主机,不能假设固定并发上限。
llama.cpp 能服务多个用户吗?
可以。官方 llama-server 支持并行 Slot 与连续批处理。应使用 llama-bench 隔离核心运行时性能,并用独立的流式 HTTP 压测验证 Queue、TTFT、Cancellation、Error 与端到端多用户行为。
OpenAI 客户端能在 Ollama 与 vLLM 之间零改动迁移吗?
只有协议一致性用例通过后才能这样做。兼容端点不保证参数、Chat Template、模型名、默认值、流式事件、usage 与错误完全一致。固定这些行为后,切换 base_url 才是最后的路由动作,而不是完整迁移方案。
哪些指标能决定本地大模型服务选型?
先把业务质量设为硬门槛,再按真实到达负载比较 TTFT、ITL 或 TPOT、E2E、输出 Token 吞吐、错误、排队、内存和 SLO 合格 Goodput。冷启动与恢复时间单独统计。最终选择应同时满足工作负载 SLO、成本与运维能力。
总结
Ollama、vLLM 与 llama.cpp 是本地大模型推理的三种不同运维答案。先确定工作负载、模型身份、硬件、API 与安全契约,再测量真实到达率和满足 SLO 的 Goodput。若 Ollama 的集成工作流与实测容量足够,就不必增加复杂度;若调度与分布式 GPU 服务经过验证能产生价值,再选择 vLLM;若可移植性与运行时控制更重要,则选择 llama.cpp。模型、上下文、量化、引擎或硬件变化后,都应基于固定制品与 Trace 重新评估。