最近总有人在后台问我同一个问题:想把 DeepSeek、Qwen 这类开源大模型放到自己的机器上跑起来,但翻了一圈教程,vLLM、SGLang、Ollama、llama.cpp 这四个名字反复出现,每个都说自己好用,到底选哪个?我跟你们说,这四个还真不是同一类东西。选错了,轻则多花几天折腾环境,重则上线一压测就崩。
这篇文章我就从机制和部署两条线,把这四个引擎的底细掰开讲清楚。看完你至少能明白两件事:第一,它们的性能差距到底从哪来;第二,以你的显存、并发和场景,应该选哪个。
1. 先把定位搞清楚:这四个引擎不在一个赛道上
1.1 一句话说清每个工具的本质
我用一张表把这四个家伙的定位列出来,你第一眼就能看出差别。
| 引擎 | 底层实现 | 启动方式 | 核心目标 | 典型用户 |
|---|---|---|---|---|
| vLLM | Python + GPU Kernel 深度优化 | python -m vllm.entrypoints.openai.api_server | 高并发、高吞吐的生产服务 | 有 GPU 服务器的工程团队 |
| SGLang | Python + 前缀缓存优化 | python -m sglang.launch_server | 高吞吐 + 长前缀复用 | 做 RAG、多轮对话、结构化输出的团队 |
| Ollama | Go 封装(底层走 llama.cpp 等) | ollama run qwen2.5:7b | 零配置的本地私有化 | 个人开发者、桌面用户 |
| llama.cpp | 纯 C/C++,无 Python 依赖 | 编译后直接运行二进制 | 极致的轻量与跨平台 | 老设备、开发板、Android/iOS 端侧 |
注意看最后两列。vLLM 和 SGLang 的目标是“同时喂给模型尽可能多的请求,把 GPU 用满”,这是一个典型的服务端思维。而 Ollama 和 llama.cpp 的目标是“让你在个人电脑上能跑起来”,更偏单机体验。后者当然也能开 API,但在并发能力上跟前者不是一个量级。
1.2 大多数人选错的根源
我见过太多人踩同一个坑:看 vLLM 各种 benchmark 跑分漂亮,就在自己那台 8G 显存的老笔记本上硬装,结果装了半天 CUDA 和 Triton 编译冲突,最后连模型都加载不出来。转头看到 Ollama 一条命令就能跑,又把它直接搬到生产环境当多用户 API 用,并发一上来立刻 OOM。
这就是典型的“不看定位只看名气”。选型不是比谁技术上更牛,而是看你的服务形态:
- 你是给自己一个人用,还是给公司 200 个同事用?
- 你跑的是 2K 上下文的闲聊,还是 32K 上下文的 RAG 检索?
- 你手头是 4090 单卡,还是 8 张 A100 的集群?
这些问题的答案,直接决定你应该选哪个引擎。
1.3 服务形态决定引擎取向
打个比方。如果大模型推理像运输货物:llama.cpp 像一辆三轮车,灵活、便宜、什么路都能走,但载货量小;Ollama 像一辆网约车,随叫随到不用你操心,适合个人跑腿;vLLM 像一辆公交车,专门为“拉很多人”优化,但需要专用道路和调度;SGLang 则像带了行包寄存服务的定制班车,如果大家出发的路线重合(共享前缀),能省下大量重复路程。
你要先搞清楚自己是在送一个包裹,还是在运营一条客运线路。这个基本面不解决,后面调什么参数都白搭。
2. 吞吐与延迟的博弈:vLLM 的连续批处理和显存管理
2.1 continuous batching:把 GPU 的空闲时间榨干
vLLM 拉开差距的核心,是它2019年以后在推理调度上采用的 continuous batching(连续批处理)。
传统静态批处理是这么干的:攒够一批请求,比如 8 个,一起丢给模型跑。这一批必须全部解码完,下一批才能进来。问题是,每个请求的长短不一样,快的请求跑完了只能干等慢的,GPU 在那段时间里实际上是闲置的。
连续批处理改成了迭代级调度:每一轮解码结束后,哪些请求完成了就直接退出,新请求立刻补位。翻译成人话就是,GPU 上一秒还在处理你的问题,下一秒空闲算力就开始处理下一个排队的人。实测下来,在请求长短不一、有快有慢的真实流量里,这种方式能把吞吐量提升数倍,而且不需要你改任何模型代码。
2.2 PagedAttention 和 KV Cache:显存是这样被“抠”出来的
大模型生成每个 token,都要把之前的 Key 和 Value 缓存下来,这就是 KV Cache。这个缓存有多大?以 7B 级别模型为例,每多缓存一个 token 往往要吃掉几十到上百 KB 显存,上下文越长越夸张。传统实现为了快,会按最大可能长度一次性预分配显存,但实际请求根本用不了那么多,于是大量显存被占着却空转,还碎成一片一片的。
vLLM 的 PagedAttention 借鉴了操作系统内存分页的思路。KV Cache 不再是一整块连续显存,而是拆成一个个物理块,逻辑上连续的显存可以散落在不同的物理位置,用到哪块才分配哪块。这个设计把显存利用率从原来的六七成拉到了接近九成以上。
2.3 为什么低并发时 vLLM 的优势反而体现不出来
这里必须说句公道话:如果你只是单人在命令行界面下和模型对话,vLLM 的高吞吐优势完全没有用武之地。吞吐需要“同时服务的请求数量”撑起来,一个人只有一个请求,再强的批处理调度也只是解一道题。
更麻烦的是,vLLM 为了追求大吞吐,会引入一定程度的排队和调度开销,在单请求场景下首字延迟甚至可能比 Ollama 还慢。所以经常有朋友跑来问我“为什么我装了 vLLM 反而感觉变慢了”,因为场景根本不匹配。
3. SGLang 的缓存复用思路,以及无 NVLink 多卡的真实影响
3.1 RadixAttention:把重复计算的前缀直接砍掉
SGLang 在工程上做得比较漂亮的一点,是它的 RadixAttention 前缀树缓存机制。
大家平时跑大模型,很多请求的前缀是重复的。比如你搭了一个知识库问答系统,每个问题前面都带着一大段“请你根据以下资料回答”这样的系统提示词和检索出来的文档;再比如多轮对话,每一轮都要带上前面好几轮的历史消息。这些前缀每个请求都在重复算一遍,纯属浪费。
SGLang 用一棵前缀树把 KV Cache 按公共前缀存起来。新请求过来时,如果它的前缀在树里命中,就直接复用,模型只需要计算新增的部分。这个优化在 RAG、多轮对话这类场景里的收益是肉眼可见的——同样一堆请求,算力消耗可能直接减掉一大半。vLLM 后来也做了 prefix caching,但 SGLang 的树形结构粒度更细,处理公共前缀的复用更激进。
3.2 认真聊聊:SGLang 无 NVLink 多卡影响多大
被问得最多的一个具体问题是:“我的服务器两张卡中间没有 NVLink,用 SGLang 开张量并行会不会很吃亏?”
先说结论:两张卡的情况下,影响有,但没有想象中那么致命。真正被拉开差距的是四卡以上的大集群。
原理是这样的。张量并行(TP)要把模型切到多张卡上,每一层做完之后都要跨卡同步一次权重结果,这个操作叫 AllReduce。有 NVLink 时,比如 H100 的 NVLink 带宽能达到 900GB/s 级别;走 PCIe 4.0 x16 的话,单向带宽只有大概 32GB/s,差了一个数量级以上。模型切得越碎、上下文越长,这个跨卡通信在每一步推理里占的时间比重就越大。
具体到实践:
- 单卡能塞下的模型,永远优先单卡跑,别给自己找跨卡通信的麻烦。
- 两张无 NVLink 的 24G 卡,跑 14B 左右的量化模型,TP=2 能跑,但你会感觉到比理论算力翻倍要慢不少,正常现象。
- 四张以上无 NVLink 的卡跑 70B 级别长上下文,通信开销会明显拖后腿。这种场景我建议观望一下并行方式,改用流水线并行,或者干脆投资支持 NVLink 的平台。
3.3 SGLang 部署时的常见启动报错
SGLang 的启动命令不算复杂:
python -m sglang.launch_server \ --model-path /models/your-model \ --tp-size 2 \ --port 30000直观讲它同样暴露 OpenAI 兼容接口。最容易翻车的点有两个:
第一,显存不够。SGLang 默认会尽量利用显存,如果你模型切分加 KV Cache 超过物理显存,会直接抛 CUDA OOM。解决思路是先量化模型、降低--max-total-tokens,而不是无脑开--tp-size。
第二,离线部署时模型下载问题。我个人的建议是提前用相应工具把权重整体拉到本地目录,启动时把--model-path指向这个本地路径。提前下载还有个额外好处:后续启动非常快,不用每次联网校验。
4. vLLM 部署实战:镜像、启动参数与显存估算
4.1 为什么我建议你从 Docker 镜像入手
vLLM 的本体是一个 Python 包,但它在底层强依赖的 CUDA 编译器、Triton、PyTorch 版本之间的兼容性足够让人崩溃。你辛辛苦苦装好,跑起来报一个版本不匹配的错误,心态很容易爆炸。
所以我推荐走官方 Docker 路线。官方镜像像vllm/vllm-openai:v0.27.1一样,已经把 CUDA、PyTorch、Triton 全打包好了,你只要给它传一块 GPU 就行。这样做还有个好处,以后你想升级 vLLM 版本,直接换镜像 tag 重新起容器,不会把宿主机环境搞乱。
4.2 一份可以直接改着用的启动命令
docker run --gpus all \ -p 8000:8000 \ -v /models:/models \ --ipc=host \ vllm/vllm-openai:v0.27.1 \ --model /models/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B-AWQ \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --served-model-name deepseek-r1-7b \ --trust-remote-code几个参数背后的逻辑我得讲清楚:
--gpu-memory-utilization 0.85表示把 85% 的显存预留给推理框架使用。别贪心调到 0.98,模型权重、KV Cache、中间激活层还有 CUDA 自身都要吃显存,你全部塞满,稍微来几个长请求就 OOM。我自己的经验值是 0.85 到 0.92 之间。--max-model-len是单条请求的最大上下文长度,它和 KV Cache 成正比。开 32K 很爽,但显存占用会翻好几倍,按需设置即可。--served-model-name是你对外公布的模型名。这里起个别名方便以后后端换模型不惊动调用方。
4.3 显存估算:先算清楚再下单
很多人问,我这个显卡到底能不能跑?我来教你们一个粗算法。
推理显存主要三块:模型权重、KV Cache、运行时激活。以 7B 模型为例,FP16 权重要 14GB;用 4bit AWQ 或 GPTQ 量化后,权重可以压到 4-5GB。KV Cache 在 7B 上每 token 大概是几十到上百 KB,2K 上下文大概就是几百 MB 的量级。
所以,一张 24G 的 4090 跑 7B 模型量化版,顺带开个 4K-8K 上下文完全没问题。如果跑 14B 量化版,大概需要 8-10GB 权重,剩余显存也能撑起中等长度上下文。想用 FP16 硬跑 70B 模型,单张 24G 卡就别想了,不是 Quantization 层级的优化能解决的,必须多卡张量并行或者换更大显存的卡。
4.4 API 兼容性与验证
vLLM 跑起来之后是标准的 OpenAI 兼容接口,你可以直接拿openai这个 Python 包来调用:
from openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY" ) resp = client.chat.completions.create( model="deepseek-r1-7b", messages=[{"role": "user", "content": "介绍一下 vLLM"}] ) print(resp.choices[0].message.content)先启动服务,再用curl http://localhost:8000/v1/models看一眼模型列表,确认服务起来了再调代码,能省很多排查时间。
这里还有个容易踩的坑:一个 vLLM 实例最好只服务一种模型类型。有人图省事想同时塞一个对话模型和一个 Embedding 模型进同一个服务,经常出现路由混乱的问题。Embedding 模型要用专门的/v1/embeddings端点,建议单独起一个容器。
5. Ollama 与 llama.cpp:桌面、老系统和端侧设备
5.1 Ollama 的便利,以及绕不开的毛病
Ollama 最大的价值是“开箱即用”。以前我同事配个本地模型要折腾一上午,用 Ollama 以后,装好、选模型、拉取、开聊,十分钟内完成。
它默认监听 11434 端口,/api/chat能直接支持聊天,较新的版本也提供了/v1/chat/completions这种 OpenAI 兼容路径,方便接各种客户端工具。你可以通过环境变量把模型存储目录改到其他盘,例如:
# Linux 下修改模型目录 export OLLAMA_MODELS=/data/ollama/models ollama serveWindows 下就在系统环境变量里加一个OLLAMA_MODELS,指向你要放的盘符目录。注意先关掉 Ollama 再改,改完重启才生效。
Ollama 最让人头大的是下载速度和稳定性。网络波动时动不动拉半天下不动,或者直接报500 internal server error: llama-server process这种错误。后者的排查思路通常是三步:第一步看显存是否够,模型量化档调低一档再试;第二步看模型文件是不是没拉完整,重新拉一遍模型;第三步看是不是端口被占用或旧进程残留,重启 Ollama 服务。
还有一个很多人问的问题:Ollama 里怎么关掉某些模型的思考过程。这个要看具体模型,像 Gemma 或是带推理链的模型,可以通过 API 参数控制推理努力程度,或者调整停止符把分析部分截断。不同版本支持的参数不一样,最稳妥的办法是去 Ollama 文档里查对应模型的能力参数,或者干脆换一个不带固有思维链输出的量化版本。
5.2 llama.cpp:从 Windows 7 到 Android 都能跑
llama.cpp 是我见过兼容性最离谱的推理引擎。它是纯 C/C++ 写的,不需要 Python、不需要 PyTorch,交叉编译之后在什么平台上都能塞。官方针对性适配了 ARM NEON、Apple Metal 这些指令集和框架,就算没有 NVIDIA GPU,靠 CPU 也能跑起来。
- Windows 7:你没看错,老机器也能跑。如果你还有 Win7 环境的部署需求,别追最新 release,去找支持该系统的老版本二进制。新版本对系统版本要求通常更高,两者取舍看你能不能接受老版本缺少的新特性。
- Android:两个途径。一是在 Termux 里编译,二是用项目文档里提供的安卓演示版本。跑个小尺寸 4bit 量化模型没问题,延迟也能接受。
- 开发板:像 RK3588 这类边缘设备,跑一个 1-4B 的量化小模型做离线文案理解、指令解析都够了。注意,llama.cpp 目前是纯语言模型主战场,视觉类的 YOLO 任务还是要交给专门的视觉推理框架。
5.3 GGUF 量化格式的选择逻辑
llama.cpp 生态统一使用 GGUF 格式,这也是它和 vLLM 那些引擎的一个关键差异。GGUF 的最大优势是单文件携带全部元数据,拷到哪都能跑,不需要去对齐 HuggingFace 的目录结构。
量化档位里,我的个人建议是:
Q4_K_M:性价比之选,日常使用首选,模型体积大概是 FP16 的 30% 左右。Q5_K_M:质量优先且显存够的时候选它,比 Q4 更接近原始精度。Q8_0:体积接近半精度,显存充裕、追求质量时用。- 不到万不得已别用 Q2 档,效果下降比较明显。
Ollama 和 llama.cpp 可以共用 GGUF 模型,所以如果你用 Ollama 拉模型遇到问题,完全可以手动下载 GGUF 文件放进 Ollama 的模型目录里导入,灵活度一下子高很多。
6. 资源分档与选型结论:照着这张表做决定
6.1 按显存和硬件规模划分
| 你的硬件 | 推荐引擎 | 模型规模建议 | 注意事项 |
|---|---|---|---|
| 8GB 显存以下 / 纯 CPU | Ollama 或 llama.cpp | 1-4B 量化模型 | 优先 GGUF 的 Q4_K_M 档 |
| 12-16GB 显存 | Ollama 或 llama.cpp | 7B-14B 量化模型 | 本地个人使用首选 Ollama |
| 24GB 单卡 | vLLM 或 SGLang | 7B-14B 量化,中长度上下文 | 可以承载小团队 API 服务 |
| 多卡 24G + NVLink | vLLM 或 SGLang | 32B-70B 量化或蒸馏模型 | 开 TP 并行,把通信带宽用满 |
| 多卡无 NVLink | vLLM 或 SGLang | 14B-32B 量化,TP=2 | 四卡以上长上下文要谨慎 |
| 老设备 / 开发板 | llama.cpp | 1-3B 量化 | 优先利用 Metal/Vulkan/NEON 加速 |
6.2 按使用形态划分
- 个人桌面问答:首选 Ollama,或者有图形界面需求的选 LM Studio。这类工具底层大多也是 llama.cpp 的 GGUF 推理,但给你封装好了模型管理,不需要敲命令。
- 小团队共享 API:vLLM。吞吐是硬指标,OpenAI 兼容生态最成熟,各种监测、灰度、路由工具都能直接接入。
- RAG、复杂多轮、批量离线任务:SGLang。前缀缓存能显著节省算力,尤其适合你们文档库和系统提示词很长、高度重复的场景。
- CPU 老电脑、嵌入式设备、端侧:llama.cpp。没有比它更轻的选择。
6.3 我的选择与掏心窝建议
我自己办公桌上那张 4090,平时就在 Ollama 里跑一个 14B 的量化模型,够我日常写代码审文档用。给团队搭内部服务的时候,我会单独起一个 vLLM 容器,接上统一的 API 入口,遇到压测高峰期也不会卡死。偶尔做 RAG 实验,我会用 SGLang 看看前缀复用的效果。而那块旧笔记本和 Android 小平板,清一色是 llama.cpp。
最后分享一点个人的经验:先定并发,再定引擎。很多人一上来就纠结“哪个引擎速度快”,但真正的决策变量其实是“同时服务多少人、跑多长上下文、显存容量多少”。这三个数字定了,选型表一查,答案基本就出来了。别听谁吹哪个框架天下第一就无脑换,合适才是硬道理。