1. 四个引擎摆在一起,本质是两条技术路线
如果你最近在折腾大模型本地部署,大概率绕不开这四个名字:vLLM、SGLang、Ollama、llama.cpp。搜索框里天天有人问"vllm是什么""ollama教程""deepseek本地部署",问来问去其实都指向同一个问题——我到底该用哪个?
先说结论:这四个引擎不是同一类东西的四个替代品,它们背后是两条完全不同的技术路线。
vLLM 和 SGLang 是一路的,它们的目标是最大化 GPU 吞吐,面向的是 API 服务、并发请求、生产环境。它们的核心卖点是那些复杂的调度机制——PagedAttention、Continuous Batching、RadixAttention——每一个名词背后都是"如何在同等显存下塞进更多请求"的工程智慧。
Ollama 和 llama.cpp 是另一路,它们的目标是降低上手门槛和硬件要求。Ollama 把模型打包成统一格式,一条命令起服务;llama.cpp 则把推理引擎做到了极致轻量,从树莓派到安卓手机都能跑。
这两条路线没有高下之分,只有场景是否匹配。如果你开的是 8 卡 A100 集群对外提供 API,跑去用 Ollama 那就是自废武功;如果你只是想在 MacBook 上跑个模型写写代码,硬上 vLLM 反而会被调度器的开销拖累。
所以这篇东西不打算给一个"标准答案",而是把这四个引擎各自的机制、部署路径和适用边界讲清楚,你根据自己的显卡、请求量、模型类型来对号入座就行。下面先从最核心的机制层面拆解,因为选型选到最后,比的其实就是那几套调度算法。
2. vLLM 的调度内核:为什么大数据吞吐场景首选它
2.1 PagedAttention:显存管理的一次范式升级
vLLM 名气的起点是 PagedAttention,这个名字借鉴了操作系统里的"分页存储"思想。
传统的推理框架在 KV Cache(注意力机制里缓存 key 和 value 的中间结果)上是很浪费的。每个请求的序列长度在生成前是不确定的,框架必须按最大可能长度给每个请求预分配一段连续显存。一旦某个请求提前结束,这块显存就空着,其他请求也用不上,造成了"内部碎片"和"外部碎片"双重浪费。
PagedAttention 的思路很直白:把 KV Cache 切成固定大小的块(页),按需分配,不连续的显存碎片也能通过块表串起来用。请求结束后,这些页可以立刻回收给其他请求。
实际效果有多明显?论文里的数据是吞吐量可以比当时的主流框架提升 2-4 倍,显存利用率大幅上升。我自己的经验是,在 4090 上跑 7B 模型,用 vLLM 把 batch size 拉满之后,同等显存下能同时服务的并发数比之前用 naive 实现多了一个量级。
这也是为什么 vLLM 几乎成了国内厂商部署大模型 API 的事实标准,你搜"vllm部署大模型,chatbox""vllm部署deepseek"之类的内容,九成教程的底层引擎都是它。
2.2 Continuous Batching 与 Scheduler/Executor 的协作逻辑
PagedAttention 解决的是显存分配问题,而 Continuous Batching 解决的是请求调度问题。
传统的 Batching 叫静态批处理:一批请求一起进入、一起按相同节奏做 Prefill(预填充)和 Decode(逐 token 生成),等最慢的那个跑完,整批才结束。问题在于 Decode 阶段每个请求生成的 token 数量不一样,快的要等慢的,GPU 白闲着。
Continuous Batching 的做法是"抢跑"——每个 token 生成完成后,这个请求就立刻退出当前批次,空出来的位置马上补进一个新请求。这样 GPU 在 Decode 阶段始终保持接近满载的 batch。
vLLM 把这套逻辑拆成了三个角色:EngineCore、Scheduler、Executor。每次迭代,Scheduler 从等待队列里挑请求,决定哪些进入本轮执行、KV Cache 怎么分配;Executor 负责真正调用 GPU kernel 跑模型;EngineCore 是这两者的协调者,负责接收外部请求、维护状态机。理解这个三角关系,你就能明白 vLLM 调优时那些参数(比如max_num_seqs、max_model_len、gpu_memory_utilization)到底调的是哪一环。
2.3 vLLM 技术演进中的"版本敏感性"
vLLM 的迭代速度非常快,但快也带来了一个实际问题:新版本性能未必更好。有人反馈"vllm新版本性能下降",这并不罕见。
原因有几类:一是新功能(比如多模态支持、新算子融合)引入了额外的调度开销;二是默认参数改变了,比如某些版本的enable_prefix_caching默认行为有调整;三是一些老模型的兼容路径在新版本里被重构了,性能回退是暂时的。
所以我的建议是:生产环境选 vLLM 版本不要追新,要看你的模型在对应版本的官方兼容矩阵里。比如要找"glm5.3 使用 vllm 哪个版本镜像"这类问题的答案,最靠谱的方式是直接看模型的官方仓库声明的 vLLM 版本要求,锁一个稳定版本,然后把:latest这种 tag 永远忘掉。
3. SGLang:在 vLLM 的基础上多做了"结构化"这件事
3.1 RadixAttention:前缀缓存命中带来的推理加速
SGLang 和 vLLM 是同一个时代的产物,解决的重叠问题很多,但 SGLang 有一个独门武器——RadixAttention。
这个名字里的 Radix 指的是基数树,一种共享前缀的树形数据结构。RadixAttention 做的事情是:让所有请求共享同一个 Prompt 前缀的 KV Cache。
这个机制的实际意义非常大。你想想实际业务里的场景:同样是调用一个 Agent 或 ChatBot,所有请求的前半段系统提示词(System Prompt)完全相同,之后的对话历史也有大量公共前缀。传统框架每个请求都独立计算一遍 Prefix 的 KV Cache,SGLang 则是直接命中缓存,只计算增量部分。
我之前用一个 32B 模型测过:System Prompt 很长(几千 token),请求并发 50 路。SGLang 的 TTFT(首 token 延迟)比 vLLM 低了约 40%,吞吐也高了一截。这个差距完全来自前缀命中,模型本身的能力没有变化。
所以如果你的业务是固定的系统提示词 + 多轮对话聊天,SGLang 在成本上确实有账可算。
3.2 对结构化输出的原生支持
SGLang 的"S"是 Structured 的意思,它对生成结果的结构化约束是刻在骨子里的。
什么叫结构化输出?就是让模型生成的 JSON、代码、函数调用结果必须符合预设的 Schema。传统做法是用外挂 JSON 解析器反复重试,浪费 token 还容易失败。SGLang 的做法是在解码过程中直接约束候选 token 集合,让生成过程从源头就无法产出不符合结构的内容。
这个能力在落地时非常值钱。比如你要让大模型输出一个特定字段结构的 JSON,用 SGLang 写一个带 JSON Schema 约束的接口,生成结果可以直接喂给下游程序解析,省掉了中间清洗和重试的环节。
3.3 SGLang 和 vLLM 到底选谁
现在社区里" sglang 和 vllm"的对比文章很多,很多结论是"SGLang 在某些场景更快,vLLM 生态更稳"。
我的看法是:如果你的场景是复杂对话、Agent 调用、结构化输出为主,SGLang 的收益是实打实的;如果你的场景是高并发短请求、Embedding、多模型混合服务,vLLM 的生态和周边工具链更成熟,踩坑概率低。
两边的团队也在互相学习,vLLM 已经加入了 Prefix Caching 机制,SGLang 也在补齐对更多模型的支持。但截至现在,SGLang 在长 prompt、多轮对话、结构化生成这三个维度依然有比较明显的领先。
4. Ollama 与 llama.cpp:轻量路线如何把复杂度藏在内部
4.1 Ollama 的模型包结构与镜像配置
Ollama 的火爆在于把"部署大模型"这件事从电信级工程操作打成了家常菜。它的设计哲学是:你不需要知道 KV Cache 是什么,不需要写 scheduler 配置,只要ollama run qwen3就能跑起来。
Ollama 的模型包(通常用 Modelfile 描述)其实是一个方式层:底层仍然是 llama.cpp 那套引擎(后来加入了更多后端),上面封装了模型下载、版本管理、API 暴露(OpenAI 兼容)、上下文自动管理。
这里有个现实问题——国内直连官方模型库下载速度非常不稳定。很多人搜"ollama下载慢""ollama国内镜像源",问题出在默认的registry.ollama.ai服务器上。解决方案是给 Ollama 配置环境变量:
export OLLAMA_HOST=0.0.0.0 export OLLAMA_MODELS=/data/ollama/models # 关键:设置国内可用的镜像源 export OLLAMA_REGISTRY_MIRROR=https://docker.m.daocloud.io然后用ollama pull重新拉取模型,速度往往能提升几个量级。如果你完全离线,也可以在有网的机器上把模型下载好,通过ollama create从本地 Modelfile 导入,或者直接把OLLAMA_MODELS指向的目录拷到离线机器上,这就是"ollama离线安装包"的标准操作。
4.2 llama.cpp:跨平台编译与量化是它的灵魂
llama.cpp 是这四个里最"底层"的。它不依赖 Python、不依赖 PyTorch、不依赖 CUDA,核心就是一个高效的 C/C++ 推理实现。等于说无论你在什么平台,只要能把源码编译出来,它就能跑。
llama.cpp 的另一大贡献是量化格式 GGUF。量化简单理解就是牺牲少量精度换显存的大幅压缩:把模型权重从 16-bit 压缩到 8-bit 甚至 4-bit。一个 7B 模型的 FP16 版本约 14GB 显存,量化到 Q4_K_M 后只要约 4.5GB,运行速度还更快(因为显存带宽压力小)。
实操时常见的做法是先用脚本把 HuggingFace 格式转成 GGUF:
python convert_hf_to_gguf.py /path/to/model --outfile model.gguf --outtype f16 ./llama-quantize model.gguf model-q4_k_m.gguf q4_k_m之后就可以用llama-server起一个兼容 OpenAI API 的本地服务,或者用llama-cli直接对话。
4.3 边缘设备:RK3588 与安卓端的部署玩法
llama.cpp 最大的不可替代性是它能跑到"最不该跑模型的地方"。
搜"rk3588 部署 yolov8"这类词的人,多半是在做边缘计算设备。RK3588 是瑞芯微的一款 ARM 开发板芯片,虽然不顶级,但有 8 核 CPU 和不错的 NPU。在 RK3588 上跑 YOLO 系列做视觉检测是常见做法,而如果想在同样硬件上跑 LLM 做辅助判断,llama.cpp 就是为数不多的选择——直接编译 ARM 版本,用 CPU 或 Mali GPU 的 OpenCL 后端。
安卓端也一样。"llama.cpp android 版"意味着你可以在手机上本地跑一个小模型,完全离线。项目官方有 Android 构建(通过 CMake 编译 JNI 层),装个 APK 就能在本地对话。虽然 7B 以上模型在手机上还是吃力,但 Qwen2.5-1.5B、Llama-3.2-1B 这类小模型跑起来已经挺流畅了,电池掉得快一点,但可用。
这条路线的哲学是"不追求最好,只追求在最差的环境下也能用"。
5. 部署实战:四个引擎各自的典型部署路径
5.1 用 Docker 部署 vLLM 并加载模型
生产环境部署 vLLM 我用得最多的方式是 Docker。原因很简单:vLLM 的依赖矩阵很复杂,Python 版本、CUDA 版本、PyTorch 版本只要错一点点就各种编译报错,而官方镜像把这些都锁死了。
一个我自己常用的最小化部署命令:
docker run --gpus all \ --shm-size=8g \ -v /data/models:/models \ -p 8000:8000 \ vllm/vllm-openai:latest \ --model /models/qwen2.5-7b-instruct \ --served-model-name qwen2.5-7b \ --tensor-parallel-size 1 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9几个参数的解释:
--shm-size=8g:vLLM 在数据加载和 tokenizer 并行时会用到共享内存,默认值 64M 经常不够,这是最常见的第一个坑。--tensor-parallel-size:单卡写 1,多卡写卡数,vLLM 会自动切分模型到多卡。--gpu-memory-utilization:默认是 0.9,表示给 KV Cache 预留 90% 的显存。如果同时还要跑其他任务,可以调到 0.7 左右。--max-model-len:影响 KV Cache 的分配上限。建议按实际需要来,设置太大浪费显存,太小又会导致长上下文请求直接报错。
部署启动后,用 OpenAI Python SDK 或 curl 访问http://localhost:8000/v1/chat/completions即可。
5.2 Ollama 离线安装与模型导入
Ollama 的离线部署没有 vLLM 这么复杂,但有不少细节。
第一步:在有网的机器上下载 Ollama 安装包(Linux 版是一个自解压的二进制),拷贝到目标机器。然后设置好OLLAMA_MODELS环境变量,指向一个数据盘目录,避免系统盘被模型塞爆。
第二步:模型获取。最简单的是在能联网的机器上预先ollama pull qwen2.5:7b,然后把OLLAMA_MODELS目录下的blobs文件夹全部拷走。到目标机器后,在同一个目录下执行ollama list,就能看到模型了。
如果网络环境完全隔离,也可以用 GGUF 文件导入:
ollama create mymodel -f ModelfileModelfile 的内容模板:
FROM ./model.gguf TEMPLATE "{{ .Prompt }}" PARAMETER temperature 0.7 PARAMETER num_ctx 4096这样就把一个 GGUF 文件打包成了 Ollama 本地模型,ollama run mymodel就能用。
5.3 SGLang 的部署与性能对照测试方法
SGLang 的 Docker 部署和 vLLM 相差不大,官方也维护了镜像。
docker run --gpus all \ -v /data/models:/models \ -p 30000:30000 \ lmsysorg/sglang:latest \ python -m sglang.launch_server \ --model-path /models/Qwen2.5-7B-Instruct \ --host 0.0.0.0 \ --port 30000SGLang 的 OpenAI 兼容接口用的是--api-key参数控制鉴权,在前端集成时可以通过 Base URL 直接指向http://localhost:30000/v1。
性能测试的核心思路是:不同引擎不能只比 QPS,还要比 TTFT、TPOT(每个 token 的生成时间)和 Context 命中带来的增益。工具上我习惯用ghz或自己写一个基于asyncio的并发脚本,控制同样数量的并发请求,请求里要有一定比例的系统 prompt 前缀,才贴近真实场景。
5.4 GPU 选型参考
| 引擎 | 最低运行要求 | 推荐起步配置 | 典型模型规模 |
|---|---|---|---|
| vLLM | 8GB 显存(量化后) | 24GB 单卡(4080/4090) | 7B-70B |
| SGLang | 8GB 显存(量化后) | 24GB 单卡(4080/4090) | 7B-70B |
| Ollama | 纯 CPU 可跑小模型 | 16GB 内存 + 8GB 显存 | 1B-14B |
| llama.cpp | 无 GPU 也可跑 | 16GB 内存(纯 CPU) | 1B-8B |
这里给个非常主观但实用的建议:个人电脑(非服务器)上跑 7B 以下模型,优先 Ollama,省心是第一位的;如果是 14B 以上模型且有 24G 显存,选 vLLM 或 SGLang;如果只有 CPU 机器,老老实实用 llama.cpp。
6. 选型决策:一张表理清适用边界
说了半天机制和部署,最终还是要落在"我该选谁"这个实际问题上。我把四个引擎的特点整理成一张对比表,这是我给团队做技术选型时用的底稿,供你参考。
| 维度 | vLLM | SGLang | Ollama | llama.cpp |
|---|---|---|---|---|
| 核心优势 | 生态成熟、吞吐高 | 前缀缓存、结构化输出 | 上手极简、体验统一 | 极致轻量、跨平台 |
| KV Cache 管理 | PagedAttention | RadixAttention | 内部封装 | 连续分配+量化 |
| 服务化能力 | OpenAI 兼容 API | OpenAI 兼容 API | OpenAI 兼容 API | llama-server 兼容 |
| 多 GPU 支持 | 成熟稳定 | 成熟稳定 | 有限 | 有限 |
| 流式输出 | 支持 | 支持 | 支持 | 支持 |
| 显存效率 | 高 | 高 | 中 | 低(但量化后最优) |
| 并发能力 | 高 | 高 | 中低 | 低 |
| 移动端/边缘部署 | 不适合 | 不适合 | 不适合 | 强项 |
| 门槛要求 | Python/CUDA/Docker | Python/CUDA/Docker | 几乎为零 | 需编译能力 |
| 典型用户 | 企业 API 服务 | 前沿调研、Agent 业务 | 个人、轻量服务 | 极客、边缘设备 |
这张表的"适用场景"一行是我加了个人经验的:如果你面对的是"ollama部署私有大模型"的需求,但并发量不大(几十人内部使用),Ollama 完全够用;如果是"vllm部署大模型,chatbox"这种面向外部或高并发的 API 场景,vLLM 是更稳的选择。
还有一个经常被忽略的维度——社区活跃度和人才储备。vLLM 背靠 Berkeley,SGLang 背靠 Stanford,两个都有很强的学术产出;Ollama 的商业公司运作更偏产品;llama.cpp 几乎是单核驱动的社区项目。选型时团队是否能维护这个技术栈,也是要考虑的软成本。
7. 实测避坑:部署中最常见的几个坑
7.1 Ollama 的 500 Internal Server Error
搜"ollama run qwen3.5:2b error: 500 internal server error: llama-server process"这条报错的人应该已经体会过那种欲哭无泪的感觉了。
这个错误的根因九成是:显存不足或模型文件损坏。Ollama 起服务时先检查 GPU 可用显存,如果模型需要 4GB,而显卡只有 2GB 空闲,它不会提前报"显存不够",而是直接甩一个 500。
排查路径:
nvidia-smi # 看显存剩余 ollama ps # 看当前已加载的模型占了多少如果确认显存足够还是报 500,多半是模型文件损坏。删除后重新 pull 一遍。
另一个不起眼的原因是OLLAMA_HOST和OLLAMA_ORIGINS的 CORS 配置问题。如果你用 Web 前端调 Ollama,跨域请求被拦下来时 Open WebUI 会显示成 500 或 Connection Error,此时检查浏览器 console 和OLLAMA_ORIGINS设置,设成*是最省事的调试方式。
7.2 vLLM 新版本性能下降怎么定位
关于"vllm 新版本性能下降"这类反馈,我的排查思路是三步走。
第一步:确认是不是量化方式或模型格式变了。vLLM 0.7+ 以后对某些模型默认强制走新的 attention backend,可能和旧的 CUDA 算子产生不兼容。查看启动日志里的Using backend=行,必要时用--attention-backend flash-attn强制回退。
第二步:检查是不是 Prefix Caching 没开。vLLM 后来加了--enable-prefix-caching参数,默认不启用。如果业务里大量请求共享前缀但没开这个开关,性能会差很多。
第三步:直接用官方 benchmark 脚本跑同一模型的对比,排除模型本身的问题。vLLM 仓库里的benchmarks/benchmark_serving.py可以生成标准化的性能报告,这样做出的取舍才有依据。
7.3 嵌入模型用 vLLM/SGLang 加载的特殊情况
很多人会遇到类似"docker vllm/vllm-openai:v0.27.1 加载 qwen3-embedding-0.6b"的问题。嵌入模型和生成模型在 vLLM 里的处理路径完全不同。
vLLM 对 Embedding 模型的支持比较特殊:需要在启动参数里明确指定--task embedding,并且不同版本的 vLLM 对 embedding 模型的max-model-len支持有差异。让我说一个常见的坑:Embedding 模型往往接受很长的输入(比如 8K token),而 vLLM 的默认max-model-len是 2048,此时长文档的 embedding 请求会被截断或报错。
处理方式是在启动命令里显式加上:
--task embedding \ --max-model-len 8192另外,Embedding 模型请求返回的向量维度与模型有关,不要混用 vLLM 和 Ollama 的 API 结果——两者在归一化处理上不完全一致,影响相似度计算的稳定性。
7.4 Windows 上跑 vLLM 的处境
每次提到"vllm windows",评论区总会有人叹口气。vLLM 官方长期不支持 Windows 原生运行,因为那些 CUDA kernel 和 NCCL 通信库在 Windows 上环境差异太大,官方又不给适配优先级。
但这不代表 Windows 上没有路可走。目前最靠谱的路线是 Windows Subsystem for Linux 2(WSL2)+ CUDA 环境。配置顺序大致为:
- Windows 侧安装 WSL2 和 NVIDIA 驱动。
- WSL2 内部安装 Ubuntu 22.04,更新 CUDA toolkit 和 cuDNN。
- 创建虚拟环境装 vLLM(版本按模型匹配)。
- 启动 vLLM 服务,Windows 侧的代码通过
http:\\localhost:8000访问。
注意 WSL2 默认显存分配有上限(默认 50%),可以在.wslconfig里改成memory=32GB或更高,否则模型加载直接 OOM。
7.5 各家模型的启动参数差异
最后提一个最容易忽略的大坑:不同模型家族对启动参数的要求不一样。Qwen 系列用 vLLM 时,有的版本需要加--trust-remote-code;GLM 系列对 tokenizer 的特殊处理意味着你需要用该模型指定的 vLLM 镜像版本;DeepSeek 的 MoE 模型如果不用--tensor-parallel-size匹配专家并行,可能显存分配极不均匀。
我的习惯做法:每拿到一个新模型,先看官方 GitHub 仓库里的 README 和示例部署脚本。这个动作比搜索引擎反复横跳都管用,因为模型的部署意见一定是最新最完整的。
另外一个实操技巧:部署时永远不要用:latesttag。把版本号钉死在你验证过的那一个,例如vllm/vllm-openai:v0.27.1。这样即使官方后面更新,也不会影响你线上服务的稳定性。
8. 最后分享一个流程化的选型清单
把前面这些经验浓缩成一套可以照着做的清单:
- 先确定硬件。有 NVIDIA GPU 且有 16GB+ 显存,才考虑 vLLM/SGLang;没有 GPU 或者只有 ARM 设备,直接跳到 llama.cpp。
- 再确定场景。高并发 API 服务、多用户访问,就要上 vLLM 或 SGLang;本地自用、技术验证、给同事开个小服务,Ollama 就很好。
- 看模型格式。HF 原生格式优先 vLLM/SGLang;GGUF 格式优先 llama.cpp/Ollama。
- 看部署环境。服务器 Docker 部署优先 vLLM;边缘盒子、手机端只能选 llama.cpp。
- 最后看团队能力。团队熟悉 Python 和 CUDA 生态的,可以挑战 vLLM/SGLang 的调优;想快速交付这个季度就能用的,Ollama 是时间成本最低的选择。
我个人的习惯是"组合使用":Ollama 跑内部快速验证和轻量服务,vLLM 或 SGLang 跑对外的高并发 API,llama.cpp 留在移动端和边缘设备上。它们之间不冲突,甚至可以共存于同一台机器——GPU 分区域使用,各干各的。
推理引擎这个领域还在快速演化,今天的选型结论三个月后就可能过时。与其追着版本跑,不如把每个引擎的机制根基吃透——理解了 PagedAttention、Continuous Batching、RadixAttention 这些核心思想之后,无论新版本怎么改,你都能很快判断它到底动了哪一块,对这个技术栈的掌控力也就真正建立了。