编程 大模型推理引擎 2026 终极对决:vLLM vs SGLang,从 PagedAttention 到 RadixAttention,一次把 LLM 部署讲透(深度实战)

2026-07-13 05:12:59 +0800 CST views 7

大模型推理引擎 2026 终极对决:vLLM vs SGLang,从 PagedAttention 到 RadixAttention,一次把 LLM 部署讲透(深度实战)

训练模型是把模型"教出来",部署模型才是把它"用起来"。2026 年,几乎所有后端团队都在面对同一个问题:同一张 GPU,凭什么别人的服务能扛 10 倍 QPS,你的却一上线就 OOM?答案不在模型,而在推理引擎。本文把 vLLM 和 SGLang 这两大主流引擎掰开揉碎,从 KV Cache 的显存账本,一直讲到 P/D 分离与量化部署,配可直接抄的生产级代码。

0. 为什么 2026 年每个工程师都该懂 LLM 部署

过去两年,"大模型"三个字几乎等于"训练"。但真正到生产环境跑业务时你会发现:训练是一次性的资本开支,推理是每天 24 小时持续的运营成本。一个 70B 的模型,训练可能烧了几百万,但上线后每个月光 GPU 租金和 latency 损失,才是长期流血点。

更残酷的现实是:GPU 显存是 LLM 服务里最稀缺的资源,没有之一。你花大价钱买回来的 H 卡,很多时候 GPU 利用率(MFU)还不到 40%——不是算力不够,而是引擎不会"调度"。

这就是为什么推理引擎(Inference Engine)成了 2026 年最值得投入学习的系统软件方向。它们干的事情,本质上是把操作系统、编译器和分布式系统的老把式,重新用在了 Transformer 上:

  • 分页的思路管理显存(vLLM 的 PagedAttention)
  • 前缀树的思路复用计算(SGLang 的 RadixAttention)
  • 批处理/调度的思路压榨吞吐(Continuous Batching)
  • 请求拆分的思路平衡延迟(Chunked Prefill、P/D 分离)

本文的主角是目前社区最活跃的两个开源引擎:vLLM(伯克利 LMSYS 系,生态最广)和 SGLang(前后端协同设计,性能与结构化生成见长)。我们不止讲概念,更要给出能跑的代码。


1. 核心概念:推理引擎到底在优化什么

1.1 KV Cache 与那本算不清的显存账

自回归生成是 LLM 推理的本质:每生成一个新 token,都要"回头看"之前所有 token 的注意力。为了避免重复计算,引擎会把每一层注意力算出来的 Key 和 Value 缓存下来,这就是 KV Cache

KV Cache 显存占用公式:

KV Cache 大小 = 2 × 层数 L × 注意力头数 H × 每头维度 D × 序列长度 S × 每个元素字节数

以一个 70B 级别、上下文 32K 的模型为例:单层 KV 可能就占几百 MB,整段 32K 上下文的 KV Cache 可以轻松超过模型权重本身。换句话说,显存里大部分空间不是给模型参数用的,是给缓存用的

传统做法的灾难在于:给每个请求预分配一整块连续显存,按"最大可能长度"来划。结果绝大多数请求用不到那么长,造成严重的内部碎片;而且哪怕两个请求的系统提示词完全相同,也各自复制一份 KV Cache——双重浪费。这就像给每个顾客都预租一整桌酒席,哪怕他只吃一口。

1.2 连续批处理(Continuous Batching)

早期方案是"静态批处理":凑够 N 个请求一起算,全部生成完才释放。问题是请求长短不一,短的早结束了还得陪跑,吞吐极低。

连续批处理(源自 Orca 论文的 iterative-level scheduling)的做法是:以"单步迭代"为粒度调度。每生成一个 token,调度器就重新决定下一步跑哪些请求——谁先生成完谁先走,新请求随时插入。这把 GPU 的利用率从"看运气"变成了"稳定拉满"。

1.3 分页注意力:vLLM 的 PagedAttention

vLLM 的核心创新,是借用了操作系统虚拟内存分页的思想。它把每个序列的 KV Cache 切成固定大小的 block(页),像页表一样用 block table 做映射:

  • KV Cache 不再需要连续存储,物理显存可以分散存放,按需分配;
  • 显存浪费被控制在 ~4% 以内;
  • 更妙的是支持内存共享:多个请求若共享同一段前缀(比如同一段系统提示词),对应的 block 可以直接引用,不用复制。
# vLLM 离线推理的最小例子(V1 引擎)
from vllm import LLM, SamplingParams

prompts = [
    "你好,请用一句话解释什么是 KV Cache:",
    "用 Rust 写一个快速排序:",
]
# temperature=0.8, top_p=0.95, 最多生成 256 个 token
sampling_params = SamplingParams(temperature=0.8, top_p=0.95, max_tokens=256)

llm = LLM(model="Qwen/Qwen2.5-7B-Instruct", gpu_memory_utilization=0.90)
outputs = llm.generate(prompts, sampling_params)

for output in outputs:
    print(f"Prompt: {output.prompt!r}")
    for ot in output.outputs:
        print(f"  -> {ot.text}")

注意 gpu_memory_utilization=0.90 这一行——它告诉 vLLM 最多可以用掉 90% 的显存来装 KV Cache 池。PagedAttention 把这块池子切成一个个 block 统一管理,这就是高吞吐的来源。

1.4 基数注意力:SGLang 的 RadixAttention

SGLang 走了一条更"激进"的路。它不只在单个请求内部分页,而是把所有活跃请求的 KV Cache 用一棵**基数树(Radix Tree)**组织起来,全局共享。

机制如下:

  1. 每个请求的 token 序列被当成一条路径,逐层插入全局基数树;
  2. 新请求到来时,从根节点匹配最长公共前缀
  3. 匹配到的部分直接复用已有 KV Cache,只重新计算差异部分;
  4. 节点带引用计数和最近访问时间,用于淘汰。

这意味着:同一系统提示词下的 100 个并发对话、同一份 RAG 文档的多次追问、多轮对话的历史上下文——它们的公共前缀只算一次。实测在多轮对话和共享前缀场景下,KV 缓存命中率可提升 3~5 倍,首 token 延迟显著下降。

PagedAttention 与 RadixAttention 的本质区别:前者解决"单请求内显存碎片",后者解决"跨请求计算复用"。vLLM 后来也通过 enable_prefix_caching 支持了前缀缓存,但 SGLang 把"前缀复用"做成了运行时的第一公民,配合基数树实现自动、细粒度的全局共享。

1.5 分块预填充(Chunked Prefill)

Transformer 推理有两个截然不同的阶段:

  • Prefill(预填充):一次性处理整段输入提示词,算力密集、延迟高;
  • Decode(解码):逐 token 生成,访存密集、latency 敏感。

如果不加控制,一个 8K token 的 prefill 会长时间霸占 GPU,把后面正在 decode 的请求"饿死",导致尾延迟(tail latency)飙升。

分块预填充把长 prefill 切成多个 chunk,和 decode 请求穿插执行,保证 GPU 时刻有活干,同时不让任何长请求独占资源。vLLM 通过 enable_chunked_prefill,SGLang 在运行时默认开启。

1.6 推测解码(Speculative Decoding)

Decode 阶段一次只产出一个 token,GPU 大量算力浪费在"等数据"上。推测解码的思路:用一个小而快的草稿模型(或 ngram/EAGLE/MTP 等机制)先"猜"出接下来 N 个 token,再用大模型并行验证——猜对了就一次性采纳多个,猜错就回退到正确的那个。

验证用的是"拒绝采样"(rejection sampling),数学上保证结果与原始大模型完全一致(不损失质量)。在草稿模型匹配度高的场景下,吞吐可提升 2~3 倍。

# vLLM 用 ngram 草稿做推测解码(无需额外模型)
vllm serve Qwen/Qwen2.5-7B-Instruct \
  --speculative-model "[ngram]" \
  --num-speculative-tokens 5 \
  --ngram-prompt-lookup-max 4

1.7 P/D 分离(Disaggregated Prefill/Decode)

当流量大到一定程度,把 prefill 和 decode 放在同一批 GPU 上会互相干扰。P/D 分离的做法:用一组 GPU 专门做 prefill,另一组专门做 decode,中间通过高速网络传输 KV Cache(vLLM 用 Mooncake / NIXL connector,SGLang 也有对应方案)。

这能让 prefill 集群专注算力、decode 集群专注低延迟,整体资源利用率再上一个台阶。代价是引入 KV 传输的网络开销和系统复杂度。

1.8 量化(Quantization)

显存不够时,量化是性价比最高的手段:

  • 权重量化:FP8 / INT4 / AWQ / GPTQ,把 16 位权重压到 8 位甚至 4 位;
  • KV Cache 量化:把 KV 也降精度存储;
  • 新兴的 FP4 在 2026 年的新硬件上开始落地。

量化能显著降显存、提吞吐,代价是极少量精度损失,对大多数业务可接受。


2. 架构分析:V1 引擎 vs SGLang 运行时

2.1 vLLM V1:集中式调度 + 分层执行

vLLM 在 2025–2026 年完成了向 V1 引擎的迁移。核心是一个叫 EngineCore集中式调度器,它在独立进程里持有全局视图,统一决定每一步跑哪些请求、分配哪些 KV block。

分层结构(从外到内):

API Server (OpenAI 兼容)
   └─ Engine (接收请求, 输出生成结果)
        └─ EngineCore / Scheduler (集中式调度, 全局唯一)
             └─ Executor (MultiProcExecutor 负责多卡)
                  └─ Worker (每张 GPU 一个, 真正跑模型前向)

V1 的关键改进:调度与执行解耦、异步化、zero-copy 的数据通路,以及对 Speculative Decoding、Chunked Prefill、Prefix Caching、Guided Decoding、Disaggregated P/D 的一等支持。代码量约 77 万行(Python + CUDA 内核),是工程复杂度最高的开源推理引擎之一。

2.2 SGLang:前后端协同 + 零开销调度

SGLang 的设计哲学是"前后端协同(co-design)":

  • 前端:一门领域特定语言(DSL),让你用近似 Python 的语法编排复杂 LLM 程序——多轮对话、任务规划、工具调用、JSON 结构化输出;
  • 后端:一个极致优化的运行时,专注调度、内存管理与多卡并行。

其后端亮点:

  • 零开销 CPU 调度器:调度决策几乎不占 CPU,把算力全留给 GPU;
  • RadixAttention:全局前缀缓存,自动复用;
  • 大规模专家并行(EP):对 DeepSeek 这类 MoE 模型做大规模专家并行 + 数据并行注意力,吞吐显著优于通用框架;
  • 结构化输出:基于正则/有限状态机(FSM)的约束解码,强制模型输出合法 JSON / 特定格式;
  • 量化支持 FP4/FP8/INT4/AWQ/GPTQ,以及 Torch 编译(torch.compile)加速。

2.3 设计哲学对比(一句话总结)

维度vLLMSGLang
定位通用、生态最广的推理库性能与结构化生成优先的运行时
缓存复用PagedAttention + prefix cachingRadixAttention 全局前缀树
强项场景通用在线服务、社区插件多多轮对话、共享前缀、MoE 大模型
编程模型OpenAI 兼容 API 为主前端 DSL + 后端运行时协同
典型用户想"开箱即用"接现有业务追求极致吞吐 / 复杂 LLM 程序

两者的边界在 2026 年越来越模糊——vLLM 加了 prefix caching 和 disaggregated P/D,SGLang 也支持 OpenAI 兼容 API。选型更多是"场景 + 团队熟悉度"的权衡,而非能力有无。


3. 代码实战

3.1 安装

# 方式一:pip 安装 vLLM(需要 CUDA 环境)
pip install vllm

# 方式二:pip 安装 SGLang
pip install "sglang[all]"

# 方式三(推荐生产):Docker,避免环境踩坑
docker pull vllm/vllm-openai:latest
docker pull lmsysorg/sglang:latest

3.2 vLLM:从离线推理到在线服务

离线推理已见 1.3。生产环境通常用 OpenAI 兼容的服务:

# 启动一个 OpenAI 兼容的推理服务
vllm serve Qwen/Qwen2.5-7B-Instruct \
  --host 0.0.0.0 --port 8000 \
  --gpu-memory-utilization 0.90 \
  --max-model-len 32768 \
  --enable-prefix-caching \
  --enable-chunked-prefill \
  --tensor-parallel-size 1

客户端直接用 OpenAI SDK 调,零改造:

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")

resp = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {"role": "system", "content": "你是一个严谨的中文技术助手。"},
        {"role": "user", "content": "用三句话解释连续批处理。"},
    ],
    temperature=0.7,
    max_tokens=512,
)
print(resp.choices[0].message.content)

3.3 SGLang:启动服务 + 前端 DSL

# 启动 SGLang 服务(默认也是 OpenAI 兼容)
python -m sglang.launch_server \
  --model-path Qwen/Qwen2.5-7B-Instruct \
  --host 0.0.0.0 --port 30000 \
  --tp 1 \
  --mem-fraction-static 0.85 \
  --context-length 32768

用 SGLang 前端 DSL 编排一个"带工具调用 + 结构化输出"的复杂程序:

import sglang as sgl

@sgl.function
def extract_user(s, text: str):
    # 强制输出 JSON 结构(约束解码)
    s += sgl.system("你只输出 JSON,不要解释。")
    s += sgl.user(f"从下面文本抽取姓名和年龄:\n{text}")
    s += sgl.gen("json",
                 regex=r'\{\s*"name"\s*:\s*"[^"]*",\s*"age"\s*:\s*\d+\s*\}')

state = extract_user.run(text="张三今年28岁,是一名后端工程师。")
print(state["json"])
# 输出类似:{"name": "张三", "age": 28}

注意 regex= 参数——SGLang 用正则驱动的约束解码,保证模型吐出来的一定是合法 JSON,省去你事后 json.loads 一直 try/except 的痛苦。

3.4 结构化输出实战

结构化输出是工程落地的刚需(喂给下游 API、存数据库)。两个引擎都支持:

vLLM(guided decoding):

from vllm import LLM, SamplingParams

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer"},
    },
    "required": ["name", "age"],
}

llm = LLM(model="Qwen/Qwen2.5-7B-Instruct", guided_decoding_backend="xgrammar")
out = llm.generate(
    [{"prompt": "从'李四 35 岁'抽取信息:", "guided_json": schema}],
    SamplingParams(temperature=0, max_tokens=128),
)
print(out[0].outputs[0].text)  # 保证合法 JSON

SGLang(见 3.3 的 regex 用法),两者底层都用类似的 FSM/CFG 约束解码,区别在接口风格。

3.5 量化部署实战

# vLLM 加载 AWQ 量化模型(提前用 AutoAWQ 量化好)
vllm serve TheBloke/Qwen2.5-7B-Instruct-AWQ \
  --quantization awq

# SGLang 加载 FP8 模型
python -m sglang.launch_server \
  --model-path neuralmagic/Qwen2.5-7B-Instruct-FP8 \
  --quantization fp8

量化能让你在单张消费级显卡上跑原本需要高端卡的大模型,是私有化部署的救命稻草。

3.6 P/D 分离部署(vLLM 示例)

# disaggregated_prefill.py 思路(精简版)
# 启动两个 vLLM 实例:prefill 用 GPU0,decode 用 GPU1,KV 通过连接器传输
from vllm import LLM, SamplingParams
from vllm.config import KVTransferConfig

# Prefill 节点
kv_config_p = KVTransferConfig(
    kv_connector="MooncakeConnector",
    kv_role="kv_producer",
)
llm_p = LLM(model="Qwen/Qwen2.5-7B-Instruct",
            kv_transfer_config=kv_config_p,
            gpu_memory_utilization=0.9)

# Decode 节点
kv_config_d = KVTransferConfig(
    kv_connector="MooncakeConnector",
    kv_role="kv_consumer",
)
llm_d = LLM(model="Qwen/Qwen2.5-7B-Instruct",
            kv_transfer_config=kv_config_d,
            gpu_memory_utilization=0.9)

生产环境通常用 NIXL / Mooncake 这类高性能 KV 传输连接器,配合负载均衡代理把请求路由到 prefill 再转 decode。

3.7 生产级 Docker Compose

# docker-compose.yml —— 单卡 vLLM 服务
services:
  vllm:
    image: vllm/vllm-openai:latest
    runtime: nvidia
    ipc: host
    ports:
      - "8000:8000"
    volumes:
      - ./models:/models
    command: >
      --model /models/Qwen2.5-7B-Instruct
      --tensor-parallel-size 1
      --gpu-memory-utilization 0.90
      --max-model-len 32768
      --enable-prefix-caching
      --enable-chunked-prefill
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

3.8 基准测试:别拍脑袋,看数字

# vLLM 自带压测工具,测量 TTFT / TPOT / 吞吐
python -m vllm.entrypoints.openai.cli.benchmark.serve \
  --backend vllm \
  --model Qwen/Qwen2.5-7B-Instruct \
  --dataset-name sharegpt \
  --num-prompts 1000 \
  --request-rate 16

# SGLang 自带压测
python -m sglang.bench_serving \
  --backend sglang \
  --model-path Qwen/Qwen2.5-7B-Instruct \
  --dataset-name sharegpt \
  --num-prompts 1000 \
  --request-rate 16

关注三个核心指标:TTFT(首 token 延迟,受 prefill 影响)、TPOT(每 token 延迟,受 decode 影响)、吞吐(请求/秒或 token/秒)。延迟和吞吐天然冲突,调优就是在这条曲线上找业务平衡点。


4. 性能优化:怎么选、怎么调

4.1 选型决策树

  • 通用在线服务、要接一堆现成业务、社区插件多 → 选 vLLM,OpenAI 兼容 + 生态最稳。
  • 多轮对话 / 大量共享前缀 / RAG 反复追问同一文档 → 优先考虑 SGLang,RadixAttention 的复用收益明显。
  • DeepSeek 等 MoE 大模型、追求极致吞吐SGLang 的大规模专家并行更占优。
  • 需要复杂 LLM 程序编排(工具调用、结构化输出、控制流)SGLang 前端 DSL 更顺手;vLLM 也能做,但偏底层。
  • 不确定 → 两个都跑一遍 bench_serving,用真实流量分布比数字,别凭感觉。

4.2 双方通用调优清单

参数作用建议
gpu_memory_utilization / mem-fraction-staticKV 池占显存比例0.85~0.92,留点余量防 OOM
max_model_len最大上下文别盲目设 128K,按真实分布设,省显存
enable_prefix_caching / RadixAttention前缀复用多轮/共享前缀场景务必开
enable_chunked_prefill长 prefill 切片高并发必开,降尾延迟
--speculative-model推测解码草稿匹配度高时 +2~3x 吞吐
tensor_parallel_size / --tp多卡张量并行模型放不下时加

4.3 量化取舍

  • 显存吃紧 → 上 AWQ / GPTQ(INT4),精度损失小;
  • 新硬件(支持 FP8)→ FP8 几乎无损且最快;
  • 极致压缩 → 试 FP4(2026 新兴,需硬件支持);
  • KV Cache 也建议量化,长上下文场景省得最多。

4.4 多卡与分布式

  • TP(张量并行):单节点多卡切模型,最常用;
  • PP(流水线并行):模型特别深时切层;
  • EP(专家并行):MoE 专用,SGLang 对 DeepSeek 类做得最好;
  • DP(数据并行):多副本扛并发,配合前缀共享。

4.5 监控

把 TTFT、TPOT、GPU 利用率、KV Cache 命中率做成 dashboard。KV Cache 命中率低?说明前缀没复用上,检查 system prompt 是否一致、是否开了 prefix caching。GPU 利用率忽高忽低?多半是 prefill/decode 互相干扰,考虑 chunked prefill 或 P/D 分离。


5. 总结与展望

2026 年的 LLM 推理引擎,早已不是"能把模型跑起来"那么简单,而是一场围绕显存、调度、复用的系统工程较量:

  • vLLM 用 PagedAttention + V1 集中式调度,把"通用高吞吐推理"做到了极致,生态与兼容性无出其右;
  • SGLang 用 RadixAttention + 前后端协同,把"前缀复用"和"结构化生成"做到了极致,尤其在多轮对话、MoE 大模型上锋利。

几个明确的趋势:

  1. P/D 分离 会从"大厂专属"下沉到中小团队,KV 传输连接器(NIXL/Mooncake)越来越标准化;
  2. 长上下文 推动 KV Cache 量化、分层缓存(如 HiCache)成为标配;
  3. 多模态统一:SGLang 已支持扩散模型、VLM,推理引擎正变成"生成式 AI 的统一运行时";
  4. 异构硬件:FP4、NPU、国产加速卡会被引擎快速吸收。

一句话选型建议:求稳、求生态、求通用 → vLLM;求快、求复用、求复杂编排 → SGLang;真要上生产,两个都 bench 一遍,让真实流量分布替你拍板。

本文代码均可在对应引擎最新版本运行;版本迭代快,具体参数以官方文档为准。动手前先 bench_serving,别让直觉替你做性能决策。

参考资源

  • vLLM 官方文档与 GitHub(PagedAttention、V1 Engine 架构解析)
  • SGLang 官方文档与 GitHub(RadixAttention、大规模专家并行博客)
  • Orca: 《Efficient Memory Management for LLM Serving with PagedAttention》
  • 各引擎自带 bench_serving 压测工具与示例

推荐文章

CSS 媒体查询
2024-11-18 13:42:46 +0800 CST
php内置函数除法取整和取余数
2024-11-19 10:11:51 +0800 CST
mysql 优化指南
2024-11-18 21:01:24 +0800 CST
go命令行
2024-11-18 18:17:47 +0800 CST
Vue3中的v-slot指令有什么改变?
2024-11-18 07:32:50 +0800 CST
程序员茄子在线接单