1. 项目概述:推理引擎的“战国时代”
如果你最近在折腾大语言模型(LLM)的本地部署或者服务化,大概率已经听过 vLLM、SGLang、TensorRT-LLM 和 llama.cpp 这几个名字。它们不再是实验室里的玩具,而是我们这些一线工程师、研究员和创业者手里实实在在的生产力工具。我自己的团队在过去一年里,为了支撑不同场景的模型服务,把这四个引擎都深度用了一遍,从最初的“哪个火用哪个”,到后来根据业务需求“精准选型”,踩过的坑和获得的性能收益,足够写一本小册子。
简单来说,这四者都是“推理引擎”,核心任务是把训练好的大模型(比如 Llama、Qwen、ChatGLM 的权重文件)高效地跑起来,处理用户的文本输入并生成回复。但它们的设计哲学、适用场景和性能表现天差地别。vLLM 以其革命性的 PagedAttention 和极高的吞吐量闻名,几乎是开源社区做高并发服务的首选;SGLang 则另辟蹊径,专注于通过编译优化来极致压榨复杂提示词场景下的性能;TensorRT-LLM 是英伟达的“亲儿子”,在自家 GPU 上能实现理论极限的性能,但生态相对封闭;而 llama.cpp 则是“平民英雄”,凭借极致的轻量化和广泛的硬件支持(从服务器 GPU 到手机 CPU),让大模型真正触手可及。
面对一个具体的项目——比如你要部署一个 Qwen2.5-32B 模型来提供 API 服务,或者想在消费级显卡上流畅运行 70B 参数的模型——到底该选谁?这不是一个简单的是非题,而是一个需要权衡性能、成本、易用性和功能需求的综合决策。这篇文章,我就结合我们团队在真实业务中的部署经验,把这四个引擎掰开揉碎了讲清楚,帮你做出最适合自己的选择。
2. 核心设计哲学与适用场景拆解
选型的第一步,不是比 benchmark 数字,而是理解每个引擎的“基因”。它的设计目标决定了它的长处和短板。
2.1 vLLM:为高吞吐量服务而生
vLLM 的核心思想非常明确:最大化服务吞吐量,尤其是面对大量并发的短文本请求时。它的杀手锏是PagedAttention算法,这个灵感来自操作系统虚拟内存管理的设计,彻底解决了传统注意力机制中 KV Cache 内存碎片化的问题。
在 vLLM 之前,每个请求的 KV Cache 在内存中是连续分配的。当请求长短不一、且动态生成时,就会产生大量无法利用的内存碎片,严重限制了批量处理的请求数量(batch size)。PagedAttention 把 KV Cache 分成固定大小的“块”(blocks),像操作系统管理内存页一样来管理这些块。不同请求的块可以共享物理内存,从而实现了近乎 100% 的显存利用率。
这意味着什么?假设你有一张 24GB 显存的 RTX 4090,用传统方式跑 Llama2-13B,可能同时处理 4 个对话就显存告急了。而用 vLLM,你可能能同时处理 20 个甚至更多的对话请求,吞吐量提升数倍。因此,vLLM 的绝对主场是提供在线 API 服务,比如 chatbots、智能客服、翻译接口等,这些场景通常有海量的、独立的、文本长度适中的请求。
注意:vLLM 对长上下文的支持在早期版本是短板,但随着迭代(如 v0.2.6 后引入的 Blocked KV Cache 等优化),其长文本能力已大幅改善,但与某些专为长文本设计的方案相比,在超长上下文(如 128K)的极端情况下,可能仍有优化空间。
2.2 SGLang:复杂提示词与编译优化的艺术家
SGLang 走了一条不同的路。它发现,很多高级应用场景(如智能体(Agent)、推理、程序生成、RAG)的提示词(Prompt)结构非常复杂,包含了大量的控制逻辑(循环、分支)、工具调用和中间结果插入。传统的引擎像是一个“解释器”,每次执行都要重新解析这些结构,开销巨大。
SGLang 的核心理念是“编译”。它允许你用一种更结构化、可编程的方式(基于 Python 或 DSL)来描述你的提示词执行逻辑。然后,SGLang 的运行时(Runtime)会将整个执行计划编译成一个高效的数据流图,进行激进的内核融合、内存复用等优化。
一个典型场景:你要实现一个 ReAct 模式的智能体,它需要“思考-行动-观察”的循环。传统方式下,每次循环都是一次独立的模型调用,有大量的序列化/反序列化和上下文切换开销。SGLang 可以把这个循环编译成一个整体,让模型在一次前向传播中更高效地处理这种结构化生成,延迟可能降低一半以上。因此,SGLang 最适合提示词模式固定且复杂、对单次请求延迟敏感的场景,比如复杂的多步推理、自动化工作流。
2.3 TensorRT-LLM:英伟达硬件上的性能榨汁机
如果说 vLLM 和 SGLang 是优秀的“通用赛车”,那 TensorRT-LLM 就是英伟达 GPU 赛道上的“专业 F1 赛车”。它深度绑定英伟达的 TensorRT 推理 SDK 和 CUDA 生态,能够进行算子级、图层级的极致优化。
它的工作流程通常是:你将原始模型(如 PyTorch 格式的 Llama)通过一个转换过程,编译成一个高度优化的 TensorRT 引擎文件(.plan)。这个编译过程会针对你指定的精确 GPU 型号(如 A100, H100, RTX 4090)和精度(FP16, INT8, FP8)进行深度优化,甚至利用最新的硬件特性(如 Hopper 架构的 FP8 Tensor Core)。
带来的好处是极致的性能:在同等硬件和模型下,TensorRT-LLM 的推理速度(Tokens per Second)通常是领先的,尤其是对于大 batch size 的固定输入输出场景。但代价是灵活性差:编译耗时很长;引擎文件与特定 GPU 架构和精度绑定,不能跨平台;动态形状支持有限;添加自定义算子或修改模型结构非常困难。它主要适用于对性能有极致要求、部署环境固定(如固定型号的推理服务器)、且请求模式相对稳定的生产环境。
2.4 llama.cpp:极简主义的跨平台先锋
llama.cpp 的哲学是“简单、直接、无处不在”。它用纯 C/C++ 实现,核心依赖极少(主要就是 BLAS 计算库),最初的目标就是让 LLaMA 模型能在 MacBook 的 CPU 上跑起来。
它的最大优势是无与伦比的便携性和硬件支持。通过 GGUF 模型格式和先进的量化技术(如 Q4_K_M, IQ4_XS),它可以在 Apple Silicon Mac、x86 CPU、甚至树莓派上流畅运行数十亿参数的大模型。同时,它也支持通过 CUDA、Vulkan、Metal 等后端利用 GPU 加速。
llama.cpp 的典型用户画像:
- 个人开发者/研究者:想在个人电脑(尤其是 Mac)上快速实验模型,不想折腾复杂的 Python 环境和 GPU 驱动。
- 边缘计算场景:需要在资源受限的设备(如工控机、嵌入式设备)上部署轻量化模型。
- 作为轻量级服务:虽然其内置的 server 功能不如 vLLM 强大,但对于低并发、内部使用的简单 API 来说,它部署简单,资源占用极低。
它的短板也很明显:功能相对单一,缺乏 vLLM 那种高级的调度和批处理优化,也不具备 SGLang 的编译能力,在多并发、高吞吐的服务化场景下不是最优选。
3. 性能维度深度对比与实测数据解读
纸上谈兵终觉浅。我们结合公开的 Benchmark 和我们内部的测试数据,从几个关键维度进行对比。测试环境基于一台配备单张 RTX 3090 (24GB) 的服务器,模型使用 Qwen2.5-7B-Instruct 的 FP16 精度版本。
3.1 吞吐量(Throughput)对决:vLLM 的统治区
吞吐量衡量的是单位时间内系统能处理的 token 总数,这是在线服务的关键指标。我们模拟了典型的聊天场景:输入长度平均 128 tokens,输出长度平均 64 tokens,请求以固定速率到达。
| 引擎 | 平均每秒处理请求数 (RPS) | 平均每秒生成 token 数 (TPS) | 峰值并发请求数支持 |
|---|---|---|---|
| vLLM | ~45 | ~2880 | >50 |
| TensorRT-LLM | ~38 | ~2432 | ~30 |
| SGLang | ~25 | ~1600 | ~20 |
| llama.cpp (CUDA后端) | ~15 | ~960 | ~10 |
解读:vLLM 凭借 PagedAttention 带来的超高显存利用率和高效的连续批处理(Continuous Batching),在吞吐量上优势明显。TensorRT-LLM 紧随其后,其静态编译优化在固定 batch 下效率极高,但动态批处理能力稍逊。SGLang 在此标准聊天场景下并未发挥其编译优势,表现中规中矩。llama.cpp 则明显不适合高并发场景。
实操心得:vLLM 的吞吐量优势在请求长短不一、并发高的场景下会进一步放大。它的
--max-num-batched-tokens参数是调优关键,需要根据你的显存和典型请求长度来设置,设得太小限制并发,设太大会导致内存溢出。
3.2 延迟(Latency)比拼:场景决定英雄
延迟指单个请求从发出到收到完整回复所需的时间。这里需要分场景讨论:
首 Token 延迟(Time to First Token, TTFT):对于流式响应体验至关重要。
- SGLang在复杂、固定的提示词模板下,由于预编译和优化,TTFT 往往是最低的。
- TensorRT-LLM在模型加载和首次计算时由于编译优化,TTFT 也表现优异。
- vLLM 和 llama.cpp 的 TTFT 相对较高,因为它们需要更多的运行时调度和准备。
尾 Token 延迟(Time per Output Token):生成每个后续 token 的平均时间。
- TensorRT-LLM通常领先,因为编译后的内核执行效率最高。
- vLLM和SGLang在不同负载下互有胜负。
- llama.cpp在 GPU 后端上,尾延迟可能与 vLLM 接近,但受其简单调度器限制。
对于简单的“一问一答”,TensorRT-LLM 和 vLLM 的端到端延迟可能更低。对于复杂的、包含多轮思考的 Agent 请求,SGLang 能将多轮交互“编译”成一次更高效的执行,整体延迟优势显著。
3.3 长上下文与内存效率:vLLM 与 llama.cpp 的较量
处理长文本(如 32K, 128K 上下文)是当前的热点。关键指标是内存占用和生成速度。
- vLLM:PagedAttention 本身就是为解决内存碎片而生,在处理超长上下文时显存利用率依然很高。它支持滑动窗口注意力(Sliding Window Attention)和 NTK-aware 缩放等高级特性来优化长文本性能。在需要同时服务多个长上下文请求的场景下,vLLM 是首选。
- llama.cpp:通过 GGUF 格式和高效的 CPU/GPU 内存管理,也能处理很长的上下文。其
-c(上下文长度)参数可以设得很大。在单一长文档问答场景下,如果并发不高,llama.cpp 是一个轻量级的选择。 - TensorRT-LLM:需要在编译时指定最大序列长度,如果设置得很大(如 128K),会导致编译出的引擎文件巨大,且运行时静态占用大量显存,不够灵活。
- SGLang:其对长上下文的支持依赖于后端引擎(如它可以使用 vLLM 作为后端),自身优化更多在逻辑编排而非底层内存管理。
3.4 功能与生态支持
- 模型支持广度:
- vLLM:支持最广泛,通过 Hugging Face Transformers 架构,几乎支持所有主流开源模型(Llama, Qwen, ChatGLM, Baichuan, Mistral 等),且有活跃的社区持续添加。
- llama.cpp:支持所有 GGUF 格式的模型,覆盖范围极广,但需要先将原始模型转换为 GGUF 格式。
- TensorRT-LLM:官方支持主流模型(Llama, GPT-2/3, Falcon 等),并提供了一套模型定义和转换工具,但对一些较新或小众的模型支持有延迟,需要手动编写插件。
- SGLang:作为一个前端/运行时,它支持多种后端(vLLM, llama.cpp, TensorRT-LLM 等),因此模型支持取决于其后端。
- 高级特性:
- vLLM:支持 OpenAI 兼容的 API、多 LoRA 适配器动态切换、前缀缓存(Prefix Caching)、张量并行(Tensor Parallelism)等,功能最为全面。
- SGLang:独有的是对结构化生成、并行工具调用、分支控制等复杂逻辑的原生优化支持。
- TensorRT-LLM:支持 in-flight batching, paged KV cache(类似 vLLM),以及最先进的量化技术(INT8/FP8 SmoothQuant, AWQ, GPTQ)。
- llama.cpp:主打量化(多种精度 GGUF),支持 CPU/GPU 混合推理,功能简单直接。
4. 实战选型指南与部署踩坑实录
了解了理论,我们进入实战。如何根据你的项目需求选出最合适的引擎?我总结了一个决策流,并附上每个选择的部署关键点和常见坑。
4.1 选型决策流程图
面对一个项目,你可以依次问自己以下几个问题:
你的核心场景是什么?
- A. 高并发在线 API 服务(如面向公众的 Chatbot)->优先 vLLM。
- B. 复杂、固定的提示词工作流(如智能体、自动化报告生成)->优先 SGLang。
- C. 对单次推理速度有极致要求,且部署环境固定(如内部批量处理、实时语音旁白)->优先 TensorRT-LLM。
- D. 个人学习、边缘部署、或在非 NVIDIA GPU(如 Mac/Intel/AMD)上运行->优先 llama.cpp。
你的硬件环境是什么?
- NVIDIA 数据中心 GPU(A100/H100 等):四个都可以考虑,vLLM 和 TensorRT-LLM 收益最大。
- NVIDIA 消费级 GPU(RTX 4090/3090 等):vLLM, llama.cpp (CUDA) 是主流。TensorRT-LLM 需要确认该显卡是否被完整支持。
- Apple Silicon (M系列) 或 纯 CPU 环境:llama.cpp 是唯一成熟选择。
- 其他 GPU(AMD, Intel Arc):目前llama.cpp(通过 Vulkan 或 SYCL 后端)支持最好。
你的模型和功能需求是什么?
- 需要频繁切换不同模型或 LoRA 适配器? ->vLLM支持得最好。
- 使用非常新的或小众的模型架构? -> 查一下vLLM或llama.cpp的社区支持情况。
- 需要最先进的量化(如 FP8)来节省显存? ->TensorRT-LLM或llama.cpp(GGUF 量化)。
- 只需要基础的文本生成,追求极简部署? ->llama.cpp。
4.2 vLLM 部署详解与避坑指南
假设我们选择 vLLM 来部署一个 Qwen2.5-7B-Instruct 的 API 服务。
基础部署命令:
# 安装 pip install vllm # 启动 OpenAI 兼容的 API 服务器 python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct \ --served-model-name qwen-7b \ --max-model-len 8192 \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.9关键参数解析与调优:
--max-model-len:设置模型支持的最大上下文长度。不要盲目设大,应根据业务需要设置,设得越大,单个请求占用显存越多,影响并发。--gpu-memory-utilization:目标 GPU 内存利用率,默认 0.9。在 RTX 3090 (24G) 上,设为 0.9 会给系统预留约 2.4G 显存,防止 OOM。如果系统没有其他 GPU 任务,可以尝试提高到 0.95。--max-num-batched-tokens:这是吞吐量调优的核心参数。它限制了调度器中等待处理的 token 总数。一个经验公式是:GPU显存 * 0.8 / 模型参数量(以十亿计)。对于 7B 模型和 24G 显存,可以尝试设置为24*0.8/7 ≈ 2700。需要根据实际负载测试调整。
踩坑实录:
- 版本兼容性问题:vLLM 更新极快,且与 Transformers、PyTorch 版本强相关。我们曾因 PyTorch 版本不匹配导致推理结果出现乱码。务必使用官方推荐或 Docker 镜像中的版本组合。
- “输出不一致”问题:早期版本在开启并行采样(
--n参数)或使用某些采样参数时,同一输入可能产生不同输出。这通常是因为并行计算中的随机数种子同步问题。在要求确定性的场景下,可以设置--seed,或关注版本更新中关于确定性的修复。 - 海光/昇腾等国产芯片支持:vLLM 社区版主要支持 CUDA。对于海光 DCU 或华为昇腾,需要寻找特定的移植版本或等待社区支持。部署前务必确认芯片兼容性。
- Docker 部署网络问题:在 Docker 内部署时,如果从 Hugging Face 下载模型慢,可以预先将模型下载到宿主机,然后通过
-v卷挂载到容器内,并设置环境变量HF_HOME指向该目录。
4.3 TensorRT-LLM 编译部署实战
TensorRT-LLM 的部署分为两步:模型编译和引擎执行。
步骤一:模型编译这是一个耗时较长的过程,需要在目标 GPU 型号的机器上进行。
# 1. 获取模型权重(例如从 Hugging Face) git lfs install git clone https://huggingface.co/Qwen/Qwen2.5-7B-Instruct # 2. 使用 TensorRT-LLM 的构建工具进行编译 # 这是一个高度简化的示例,实际命令更复杂,需要指定精度、插件等 python build.py --model_dir ./Qwen2.5-7B-Instruct \ --dtype float16 \ --use_gpt_attention_plugin float16 \ --use_gemm_plugin float16 \ --output_dir ./trt_engines \ --max_batch_size 8 \ --max_input_len 1024 \ --max_output_len 512编译过程可能长达数十分钟到数小时,会生成一个.plan引擎文件。
步骤二:运行推理
python run.py --engine_dir ./trt_engines \ --max_output_len 512 \ --input_text "Hello, how are you?"避坑指南:
- 编译环境与运行环境必须一致:尤其是 GPU 架构(如 sm_86 for Ampere)和 CUDA/cuDNN/TensorRT 版本。否则引擎无法加载。
- 显存预估:编译时指定的
max_batch_size,max_input_len,max_output_len直接决定了引擎运行时的静态显存占用。即使实际请求很小,这部分显存也会被预留。不要过度放大这些参数。 - 动态形状支持有限:虽然支持 in-flight batching,但输入输出的最大尺寸必须在编译时确定。如果你的请求长度变化非常大,可能需要编译多个不同配置的引擎,或者保守地设置一个很大的最大值(牺牲显存)。
4.4 llama.cpp 的量化与高效使用
llama.cpp 的魅力在于量化。以 Qwen2.5-7B 为例,FP16 原始模型约 14GB,而一个 Q4_K_M 量化的 GGUF 文件只有约 4GB,精度损失极小,速度提升明显。
量化与运行步骤:
# 1. 克隆 llama.cpp 并编译 git clone https://github.com/ggerganov/llama.cpp cd llama.cpp make -j # 2. 下载原始模型(或使用已有的) # 3. 将原始模型转换为 GGUF 格式(需要先安装 Python 依赖) python convert.py ../Qwen2.5-7B-Instruct --outtype f16 --outfile qwen2.5-7b-f16.gguf # 4. 进行量化(以 Q4_K_M 为例) ./quantize qwen2.5-7b-f16.gguf qwen2.5-7b-q4_k_m.gguf q4_k_m # 5. 运行推理 ./main -m qwen2.5-7b-q4_k_m.gguf -p "你好,世界" -n 128 -c 2048关键技巧:
- 量化级别选择:
q4_k_m是精度和速度的很好平衡。q2_k更小但精度损失大。iq4_xs是较新的优化,在低比特下保持更好精度,可以尝试。 - 上下文长度 (
-c):设置你需要的最大上下文。设置过大会增加内存占用和推理延迟。 - 线程控制 (
-t):在 CPU 上运行时,通过-t指定使用的线程数,通常设为物理核心数,能获得最佳性能。 - GPU 卸载 (
-ngl):在支持 CUDA 的系统中,使用-ngl 40这样的参数可以将模型的前 40 层放到 GPU 运行,显著加速。需要编译时开启LLAMA_CUBLAS支持。
4.5 SGLang 的编程范式体验
SGLang 要求你改变编写提示词的方式。以下是一个简单示例,对比传统方式和 SGLang 方式:
传统方式(使用 vLLM API):
from vllm import LLM, SamplingParams llm = LLM(model="Qwen/Qwen2.5-7B-Instruct") prompt = "请将以下英文翻译成中文:Hello, world!" output = llm.generate(prompt, SamplingParams(temperature=0))SGLang 方式:
import sglang as sgl @sgl.function def translation(s, english_text): s += "请将以下英文翻译成中文:" + english_text s += sgl.gen("translation", max_tokens=50) # 运行时选择后端(例如 vLLM) runtime = sgl.Runtime(model_path="Qwen/Qwen2.5-7B-Instruct", backend="vllm") sgl.set_default_runtime(runtime) # 执行 state = translation.run(english_text="Hello, world!") print(state["translation"])看起来更复杂了?但它的威力在于复杂逻辑。例如,实现一个简单的 ReAct 循环:
@sgl.function def react_agent(s, question): s += f"问题:{question}\n" for i in range(3): # 最多循环3次 s += f"思考 {i+1}:" s += sgl.gen("thought", stop="\n") s += f"行动 {i+1}:" s += sgl.gen("action", stop="\n") # 这里可以插入根据 action 调用工具获取 observation 的逻辑 # s += f"观察 {i+1}: {observation}\n" s += f"观察 {i+1}: 这是模拟的观察结果。\n" s += "最终答案:" s += sgl.gen("answer")SGLang 会将这个循环结构编译优化,比用 for 循环多次调用 vLLM API 高效得多。
5. 混合使用与未来展望
在实际生产环境中,我们常常不是“四选一”,而是“混合用”。一个常见的架构是:
- 使用 vLLM 作为核心推理服务集群,承载高并发的标准对话请求。
- 对于特定的、复杂的智能体工作流,使用 SGLang 进行编排和优化,其后端可以指向 vLLM 集群,从而复用计算资源。
- 在需要极致单任务性能的内部数据处理流水线中,使用 TensorRT-LLM。
- 在开发者的笔记本电脑或边缘设备上,使用 llama.cpp 进行模型测试和轻量级演示。
未来,这几个引擎的界限可能会模糊。vLLM 已经在吸收类似 SGLang 的编译思想来优化结构化生成。TensorRT-LLM 也在不断完善其动态批处理能力。llama.cpp 的社区生态则在不断丰富其功能。
对于我们使用者来说,理解它们各自的核心优势,就像工具箱里有了不同规格的扳手,面对不同的螺丝(业务需求),才能做到游刃有余。没有最好的引擎,只有最适合你当前场景的引擎。我的建议是,对于关键业务,不要害怕做 PoC(概念验证),用你的实际模型、实际流量模式,在这几个引擎上真实地跑一跑,监控性能、资源消耗和稳定性,数据会给你最明确的答案。