本地跑大模型这件事从2023年到现在工具链已经换了好几茬。我自己的机器上同时装着 vLLM、SGLang、Ollama 和 llama.cpp不是因为选择困难而是这四个东西压根解决的就不是同一类问题。经常有人问我到底该用哪个这个问题本身就不太对——就像问螺丝刀和电钻该买哪个得先看你是要拧眼镜腿还是要在承重墙上打孔。这篇就把这四个推理引擎的底层机制、适用边界、部署实操和踩坑经验一次讲透看完你应该能根据自己的硬件、并发量和延迟要求直接锁定该用哪个而不是每个都装一遍再删。1. 先搞清楚推理引擎到底在解决什么问题1.1 大模型推理的两个阶段Prefill 和 Decode要理解这四个引擎的差异得先知道大模型推理在计算层面分两个截然不同的阶段。Prefill预填充阶段处理的是你输入的整段 prompt。这一步是高度并行的矩阵乘法GPU 利用率能拉满属于计算密集型compute-bound。你输入1000个token这1000个token是一次性并行喂进模型的。Decode解码阶段是逐token生成的。每生成一个token都要把整个模型的权重读一遍或者读KV Cache但计算量很小属于内存带宽密集型memory-bound。这就是为什么生成速度往往比预填充慢得多——瓶颈不在算力在显存带宽。这两个阶段的特性差异直接决定了不同引擎的优化方向。vLLM 和 SGLang 的核心创新几乎都围绕如何让 Decode 阶段更高效地利用显存带宽和如何让多个请求的 Prefill/Decode 更好地并行展开。而 llama.cpp 和 Ollama 更多是在单请求、低资源场景下做量化压缩和内存管理。1.2 四个引擎的定位差异一张表说清引擎核心定位典型硬件并发能力量化支持上手难度vLLM高吞吐生产级服务数据中心GPU极强PagedAttentionGPTQ/AWQ/FP8中等SGLang高吞吐复杂编排数据中心GPU极强RadixAttentionGPTQ/AWQ/FP8中等偏上Ollama本地一键运行消费级GPU/CPU弱默认串行GGUF极低llama.cpp极致轻量可移植CPU/边缘设备/手机弱GGUF中等这张表是结论但背后的为什么才是关键。vLLM 的 PagedAttention 解决的是 KV Cache 显存碎片问题SGLang 的 RadixAttention 解决的是多请求共享前缀的重复计算问题Ollama 本质上是 llama.cpp 的封装加上模型管理和 API 层llama.cpp 则是把量化做到极致、能在各种奇怪硬件上跑起来的 C 实现。1.3 选型的第一性原理先问自己三个问题我在帮别人选型时通常先问三个问题并发量多大如果同时只有1-2个用户Ollama 或 llama.cpp 足够如果要支撑几十上百并发vLLM 或 SGLang 是唯一选择。硬件是什么有 A100/H100 这类数据中心卡闭眼选 vLLM/SGLang只有消费级显卡或纯 CPUllama.cpp 系更实际。是否需要复杂编排如果只是简单对话vLLM 够用如果涉及多轮结构化输出、分支推理、前缀共享SGLang 的编程模型更顺手。这三个问题的答案组合起来基本就能锁定方向。下面逐个拆解。2. vLLMPagedAttention 到底解决了什么痛点2.1 KV Cache 的显存浪费问题vLLM 最核心的贡献是PagedAttention。要理解它先得理解传统推理的 KV Cache 是怎么浪费显存的。在 Transformer 推理中每个已生成的 token 都需要缓存其 Key 和 Value 向量这就是 KV Cache。传统实现里KV Cache 是连续分配的——系统会预分配一块足够大的连续显存来存放最大可能长度的 KV Cache。问题是你根本不知道用户会生成多长所以要么按最大长度预分配浪费巨大要么动态扩展产生显存碎片。实测数据很能说明问题传统方案下KV Cache 的显存利用率往往只有20%-40%剩下全是碎片和预留。这意味着你花大价钱买的显存一大半在空转。PagedAttention 的思路借鉴了操作系统的虚拟内存分页机制把 KV Cache 切成固定大小的 block比如16个token一块这些 block 在物理显存上不需要连续通过一张 block table 来映射逻辑位置到物理位置。这样一来显存利用率能拉到90%以上同样的卡能塞下更多并发请求。2.2 连续批处理Continuous Batching的威力vLLM 另一个杀手锏是连续批处理。传统批处理是静态的攒够一批请求一起跑完再处理下一批。问题是不同请求生成长度差异巨大短请求早就结束了还得等长请求跑完GPU 大量空转。连续批处理在每个 decode step都重新组批某个请求生成完了立刻把它踢出去腾出的位置马上塞进新来的请求。这样 GPU 几乎不会因为等待而空转。在真实负载下连续批处理能把吞吐量提升数倍到十几倍具体取决于请求长度的分布。2.3 vLLM 部署实操从镜像到跑通vLLM 官方推荐用 Docker 部署最省心。以加载 Qwen 系列模型为例docker run --gpus all \ -v ~/.cache/huggingface:/root/.cache/huggingface \ -p 8000:8000 \ --ipchost \ vllm/vllm-openai:latest \ --model Qwen/Qwen2.5-7B-Instruct \ --max-model-len 8192 \ --gpu-memory-utilization 0.9几个参数值得展开说--gpu-memory-utilization 0.9告诉 vLLM 可以用90%的显存。这个值不是越高越好留一点给 CUDA context 和其他开销0.85-0.92 是比较稳的区间。设成0.95以上有时会 OOM。--max-model-len最大上下文长度。这个值直接决定 KV Cache 的预分配规模设太大浪费显存设太小长文本会报错。要根据实际业务的最长输入来定。--ipchost共享主机 IPC 命名空间多卡通信时需要单卡也建议加上避免一些共享内存问题。启动后就是一个 OpenAI 兼容的 API 服务直接调/v1/chat/completions即可。2.4 vLLM 的坑版本、显存和长上下文第一个坑是版本匹配。vLLM 迭代非常快不同版本对模型架构的支持差异很大。比如某些新出的模型架构老版本 vLLM 直接加载失败。我一般建议锁定一个稳定版本别盲目追 latest。如果遇到model architecture not supported先查该版本的支持列表。第二个坑是显存估算。很多人按模型参数量 × 2字节来估算这是错的。7B 模型 FP16 权重大约14GB但加上 KV Cache、激活值、CUDA context实际需要18-20GB。所以24GB的卡跑7B模型max-model-len别设太大否则并发一上来就 OOM。第三个坑是长上下文下的性能断崖。上下文超过一定长度后attention 的计算量是平方级增长的吞吐会明显下降。如果业务是长文档处理要么用支持 FlashAttention 的版本要么考虑分块处理。3. SGLangRadixAttention 与前缀共享的工程价值3.1 从 PagedAttention 到 RadixAttention 的演进SGLang 的RadixAttention可以理解为 PagedAttention 的进阶版。它用一棵基数树Radix Tree来管理 KV Cache核心目标是解决多请求共享前缀的重复计算问题。举个典型场景多轮对话。第二轮对话的输入是系统提示 第一轮问答 第二轮问题其中系统提示 第一轮问答这部分和之前完全一样。传统引擎会把这部分重新算一遍 Prefill纯属浪费。RadixAttention 通过基数树识别出共享前缀直接复用已有的 KV Cache只计算新增部分。在系统提示很长比如几千token的角色设定或者 few-shot 示例固定的场景下这个优化能省下大量 Prefill 计算。实测中前缀共享率高的负载SGLang 的吞吐能比 vLLM 高出不少。3.2 SGLang 的编程模型适合什么场景SGLang 不只是个推理引擎它还有一套前端 DSL领域特定语言用来编排复杂的生成流程。比如分支推理同一个问题生成多个候选再选最优结构化输出强制模型按 JSON schema 输出多轮工具调用模型调用工具、拿结果、继续推理的循环如果你的业务只是用户问、模型答的简单对话SGLang 的编程模型优势用不上vLLM 更简单直接。但如果涉及 Agent、RAG 里的复杂编排SGLang 能省掉大量胶水代码。3.3 SGLang 部署与调优要点SGLang 的部署和 vLLM 类似python -m sglang.launch_server \ --model-path Qwen/Qwen2.5-7B-Instruct \ --port 30000 \ --mem-fraction-static 0.85--mem-fraction-static对应 vLLM 的gpu-memory-utilization控制静态分配的显存比例。SGLang 还有个--schedule-policy参数可以选择调度策略在延迟优先和吞吐优先之间权衡。实操心得SGLang 对前缀共享的优化在冷启动时体现不出来需要请求积累到一定量、基数树建起来之后才明显。所以压测时别只发几个请求就下结论要模拟真实的多轮对话模式。4. Ollama把 llama.cpp 包装成开箱即用4.1 Ollama 的本质llama.cpp 模型管理 API很多人以为 Ollama 是独立引擎其实它的推理内核就是llama.cpp。Ollama 在 llama.cpp 之上加了三层东西模型管理类似 Docker 的pull/run体验自动下载 GGUF 量化模型API 服务默认暴露 OpenAI 兼容接口Modelfile可以自定义系统提示、参数、模板所以 Ollama 的性能上限就是 llama.cpp 的性能上限它的价值在于把复杂的编译、量化选择、参数配置全部隐藏了。4.2 Ollama 的安装与国内下载加速Ollama 安装本身很简单官网下载对应平台安装包即可。但国内用户最大的痛点是模型下载慢。默认从官方源拉取速度经常只有几十KB/s。解决办法是配置镜像源。Ollama 支持通过环境变量指定模型仓库地址export OLLAMA_HOST0.0.0.0:11434 export OLLAMA_MODELS/data/ollama/models把OLLAMA_MODELS指到大容量磁盘避免默认装在系统盘把 C 盘撑爆——这是很多人踩过的坑模型动辄几个GBC 盘很快就红了。至于下载加速可以手动下载 GGUF 文件放到模型目录或者使用国内的模型托管平台。手动放置时注意目录结构Ollama 对文件命名和 manifest 有要求放错位置会识别不到。4.3 Ollama 的并发瓶颈与适用边界Ollama 默认是串行处理请求的。也就是说两个请求同时进来第二个要等第一个跑完。虽然可以通过OLLAMA_NUM_PARALLEL环境变量开启并行但受限于 llama.cpp 的架构并发能力远不如 vLLM。所以 Ollama 的适用边界很清晰个人开发、本地测试、单用户场景。如果你要做一个给团队用的服务Ollama 会很快成为瓶颈。我见过有人用 Ollama 搭内部服务几个人同时用就开始排队体验很差。4.4 Ollama 常见报错排查热词里出现的500 internal server error: llama-server process是 Ollama 的典型报错。这个错误通常意味着底层的 llama.cpp 进程崩了原因可能是显存不足模型太大GPU 装不下尝试用更小的量化版本Q4 代替 Q8模型文件损坏下载中断导致 GGUF 文件不完整删掉重新拉上下文长度超限请求的上下文超过了模型配置调小num_ctx排查时先看 Ollama 的日志ollama serve前台运行能看到详细输出日志里会明确告诉你底层进程为什么退出。5. llama.cpp量化与可移植性的极致5.1 GGUF 格式与量化等级的选择llama.cpp 的核心资产是GGUF 格式和一套成熟的量化方案。量化就是把模型权重从 FP16 压缩到更低精度牺牲一点精度换取大幅的显存/内存节省。常见的量化等级量化等级每权重位数7B模型大小质量损失适用场景Q8_08 bit~7.5GB极小显存充足追求质量Q6_K6 bit~5.8GB很小平衡之选Q5_K_M5 bit~5.0GB小推荐日常使用Q4_K_M4 bit~4.1GB可接受显存紧张首选Q3_K_M3 bit~3.3GB明显极限压缩Q2_K2 bit~2.7GB严重不推荐我的经验是Q4_K_M 是性价比拐点。再往下压质量损失开始明显模型会变笨往上到 Q6/Q8收益递减但体积涨得快。除非有特殊需求Q4_K_M 或 Q5_K_M 是大多数人的最优解。5.2 llama.cpp 在边缘设备上的玩法llama.cpp 最迷人的地方是可移植性。它是纯 C 实现几乎能在任何有编译器的平台上跑。热词里提到的 Android 版就是典型——通过 Termux 或者专门的 App能在手机上跑小模型。在 Android 上跑 llama.cpp 的关键是用 ARM 优化的编译选项NEON 指令集选择小模型1B-3B和低量化Q4 以下控制上下文长度手机内存有限实测下来骁龙8系芯片跑 1B-3B 的 Q4 模型生成速度能到每秒几个token做简单的本地助手够用但别指望流畅对话。5.3 用 llama.cpp 搭本地编程助手llama.cpp 自带一个llama-server提供 OpenAI 兼容 API可以接各种编程助手插件。配置思路./llama-server \ -m models/qwen2.5-coder-7b-q4_k_m.gguf \ -c 8192 \ -ngl 99 \ --port 8080-ngl 99表示把所有层都放到 GPU 上99是全部的惯用写法-c是上下文长度。如果显存不够减少-ngl的值让部分层留在 CPU 上速度会慢但能跑起来。踩坑提醒编程助手对上下文长度要求高代码文件动辄几千行。-c设太小会导致代码被截断模型看不到完整上下文补全质量直线下降。建议至少 8192有条件上 16384。6. 四个引擎的横向对比与选型决策树6.1 吞吐、延迟、资源占用的实测对比在相同硬件单张24GB消费级卡和相同模型7B Q4下我做过一轮粗略对比指标vLLMSGLangOllamallama.cpp单请求首token延迟中中低低单请求生成速度快快中中10并发总吞吐极高极高低低显存占用高高中低冷启动时间长长短短注意这里的对比是同模型同量化下的相对值。vLLM/SGLang 在低并发时优势不明显甚至因为调度开销略慢但并发一上来差距是数量级的。6.2 按场景选型五类典型需求场景一个人本地玩模型。选 Ollama。一键安装ollama run就能用不用折腾编译和参数。场景二本地编程助手。选 llama.cpp。可控性强能精细调参配合编辑器插件体验好。场景三团队内部 API 服务。选 vLLM。OpenAI 兼容并发强部署标准化。场景四Agent/RAG 复杂编排。选 SGLang。前缀共享和编程模型能省大量工程。场景五边缘设备/手机。选 llama.cpp。可移植性是唯一解。6.3 混合部署为什么我机器上四个都装着实际工作中这四个不是互斥的。我的典型配置是vLLM跑生产 API 服务处理高并发请求SGLang跑需要复杂编排的实验性任务Ollama用来快速试新模型pull下来就能跑llama.cpp做量化和边缘部署的验证它们共享同一批 GGUF 或 HuggingFace 模型文件通过软链接复用省磁盘空间。这种混合模式让我在不同任务间切换时不用重新配置环境。7. 部署中那些文档不会告诉你的坑7.1 显存估算别信参数量×2前面提过但值得再强调。显存占用 权重 KV Cache 激活值 CUDA context 碎片。经验公式权重参数量 × 精度字节数FP16是2INT4约0.5KV Cache2 × 层数 × 头数 × head_dim × 序列长度 × 精度字节数其他开销预留 2-4GB7B FP16 模型8192上下文实际需要约 20GB。所以 24GB 卡跑 7B 是舒服的跑 13B 就紧张了。7.2 模型格式转换的坑HuggingFace 格式和 GGUF 格式不通用。vLLM/SGLang 用 HF 格式Ollama/llama.cpp 用 GGUF。转换需要专门的脚本llama.cpp 仓库里的convert_hf_to_gguf.py。转换时注意分词器配置要一起转否则 tokenize 结果不一致量化在转换后进行先转 FP16 GGUF 再量化转换耗时长大模型可能要几十分钟7.3 网络与镜像国内部署的现实问题国内拉取 HuggingFace 模型经常超时。解决方案用hf_transfer加速下载配置镜像站手动下载后放到缓存目录Docker 镜像同理vLLM 的镜像有好几个GB拉取慢的话可以配置国内镜像加速。7.4 版本锁定别做最新版受害者这四个工具都在快速迭代新版本可能引入不兼容变更。生产环境务必锁定版本用具体的 tag 而不是 latest。我吃过亏某次自动更新后vLLM 的 API 参数变了服务直接起不来。现在所有部署都用固定版本号升级前先在测试环境验证。8. 我个人的选型心得折腾这四个引擎两年多最大的体会是没有最好的引擎只有最合适的场景。新手最容易犯的错是用 Ollama 搭生产服务或者用 vLLM 跑单机测试前者并发崩后者大炮打蚊子。如果只能记一句话并发高、有数据中心卡选 vLLM 或 SGLang单机、消费级硬件、图省事选 Ollama要极致控制或跑在奇怪设备上选 llama.cpp。另外分享一个实用技巧不确定选哪个时先用 Ollama 快速验证模型效果确认模型可用后再根据并发需求迁移到 vLLM/SGLang。这样能避免在环境配置上浪费大量时间——毕竟模型选错了引擎再优化也没用。最后提一句量化很多人为了省显存无脑上 Q2/Q3结果模型变傻还找不到原因。量化等级的选择要结合任务难度简单分类任务 Q4 够用复杂推理任务尽量 Q6 以上。这个权衡没有标准答案得自己实测。