推理框架详解——为啥需要、做什么、底层举例与部署 Demo

第 29/30 篇

系列目录 28 DeepInfer 深度解析——De…30 推理算子与内核——Attention…

TL;DR

目录

把一个 Hugging Face 上的因果语言模型跑出第一个 token,并不难:model.generate() 几行就够。把同一份权重变成可并发、可观测、可按 TTFT/TPOT 调 SLO、能在固定显存里塞进更多活跃会话的服务,难的是另一件事——你需要一层推理框架(inference framework / serving engine),把调度、KV 管理、并行、采样、API 和内核路径钉成一条可运维的流水线。

本篇是「推理框架」的独立深潜:回答为啥需要、在栈里干什么、2026 常用有哪些、底层怎么举例子、怎么用可复制命令上手。机制细节不重复铺开——Prefill/Decode/KV 心智看 11,分页与连续批处理看 12,产品横评看 13,服务化与网关看 21 / 22,DeepSeek 生产切片看 28。本文补的是:框架解剖学 + 可跑通 Demo。

命令来自各项目公开文档(vLLM、SGLang、LMDeploy、llama.cpp/Ollama);本文未在 GPU 上实跑下列 serve 命令。硬件假设与常见失败模式会写明,吞吐数字一律不当独立复现。

下图是整篇的总览:框架真正拥有的是中间五层。

推理框架栈分层

〇、30 秒版

  1. 裸 generate 不是服务:缺请求级调度、分页 KV、连续批处理、OpenAI 兼容面与多租户配额时,并发一上来 TTFT/TPOT 和显存会同时崩。
  2. 框架拥有的层:Scheduler、KV Manager、Parallel Runtime、Sampling/Structured Output、Model Runner(再往下才是 FlashAttention / FlashInfer 一类 kernel)。
  3. 2026 常用地图:通用默认多看 vLLM;Agent/前缀缓存多看 SGLang;NVIDIA 极致延迟看 TensorRT-LLM;吞吐特化看 LMDeploy;昇腾看 MindIE;DeepSeek 特化看 DeepInfer;边缘/本地看 llama.cpp / Ollama。TGI 已维护模式/仓库归档,仅存量场景,新项目不推荐。
  4. 底层四件套:Prefill vs Decode 的 roofline 差异、PagedAttention block table、Continuous Batching 时间线、Attention backend 分层——框架是把它们组装成产品,不是发明其中某一篇论文。
  5. 上手顺序:先 Demo A(vLLM OpenAI server)→ Demo B(SGLang 或 LMDeploy 冒烟)→ 需要本地/CPU 再 Demo C(llama.cpp/Ollama);上生产再叠加 21、22、23。

一、为啥需要推理框架

1.1 从「能跑」到「能卖」

实验室脚本关心的是:这份权重能不能在一张卡上出答案。生产线关心的是另一组指标:

指标 含义 没有框架时常见症状
TTFT 首 token 延迟 长 prompt 堵死短请求;队列无优先级
TPOT / ITL 间 token 延迟 decode batch 抖动;尾延迟爆炸
并发会话数 同时活跃的 KV 占用 缺跨请求 block 管理与共享;碎片、浪费与 OOM 并存
吞吐 (tok/s) 单位时间产出 静态 batch 里短请求空等;GPU 利用率虚高或虚低
多租户 配额、隔离、多 LoRA 进程级互踩;无法按 API Key 限流

transformers 的 generate() 设计目标是正确性与研究灵活性,不是高并发 serving。它通常:一次处理一个(或静态拼好的)batch;默认 KV 用 DynamicCache 随生成动态增长、连续保存(按固定上限预分配的是 StaticCache 等路径,不是默认);缺跨请求的 block 管理、前缀共享与动态插入;也没有稳定的 OpenAI 兼容 HTTP、grammar-constrained decoding、prefix cache 命中统计。

1.2 瓶颈地图

把问题摊开,会看到四类彼此耦合的瓶颈——框架存在的理由,就是同时对付它们:

瓶颈地图

一句话:推理框架 = 在固定 GPU 预算下,用调度 + 内存管理 + 内核路径,同时抬高并发、吞吐,并守住延迟 SLO。

二、框架在栈里的位置

回到总览图(fig1)。自上而下:

  1. Serving API:/v1/chat/completions、流式 SSE、鉴权、限流。有的引擎自带(vLLM、SGLang;TGI 存量栈亦如此),有的挂在 Triton / 网关之后(TRT-LLM 常见组合)。
  2. Scheduler:waiting/running 队列、continuous batching、chunked prefill、抢占与优先级。
  3. KV Manager:block 分配/回收、prefix/radix cache、必要时 swap。
  4. Parallel Runtime:张量并行(TP)、流水并行(PP)、数据并行(DP)、专家并行(EP)及对应集合通信。
  5. Sampling + Structured Output:temperature/top-p、stop、JSON/grammar、tool call 约束。
  6. Model Runner + Kernels:把 metadata 喂给 FlashAttention / FlashInfer / 厂商 plugin;加载 AWQ/GPTQ/FP8 权重。

再往下是硬件与驱动(CUDA / ROCm / CANN / Metal)。框架不拥有 GPU,但拥有「如何把请求变成对 GPU 友好的步」的策略。 服务化集群、PD 分离编排、跨机 KV 存储——往往在框架之外再叠一层(21、28 的边界讨论)。

三、框架实际做什么

下面按「一次请求穿过引擎」来看职责,而不是按仓库目录罗列文件名。

请求生命周期

3.1 请求调度与 Continuous Batching

静态 batch 的致命问题:短请求必须陪长请求等到整批 EOS。Orca 提出的迭代级调度让每个 decode step 都可以接纳新请求、放出已完成请求——这就是 continuous batching。框架的 Scheduler 每步决定:谁 prefill、谁 decode、谁被抢占、本步 token budget 多少。

Continuous Batching 时间线

Chunked prefill(把超长 prompt 切成块与 decode 交织;思想可溯到 SARATHI 一类工作)是同一家族的补丁:避免一次巨大 prefill 饿死所有 decode。机制展开见 12;这里只需记住——没有这层调度,再快的 kernel 也救不了尾延迟。

3.2 Paged KV 与前缀缓存

裸 generate 路径里 KV 多是请求内连续增长;一旦进入多请求 serving,若仍按「整段预留 / 无共享」管理,浪费与碎片会同时发生。PagedAttention 把 KV 切成固定大小 block,用 block table 做逻辑→物理映射,思想直接类比操作系统的虚拟内存。

Paged KV

在此之上,vLLM 一类用 block-hash 做 automatic prefix caching;SGLang 用 radix tree 更吃「多分叉、共享系统提示」的 Agent 流量。框架要保证:分配、共享、写时复制、回收在调度步之间保持一致,否则 attention 会读到错块。

3.3 并行策略

策略 切什么 典型触发
TP 层内矩阵 单卡装不下,或要降单步延迟
PP 层间流水 极深模型;注意 bubble
DP 模型副本,按请求分片 抬吞吐;要请求级负载均衡
EP MoE 专家 DeepSeek 类宽专家;常配 EPLB

框架负责:进程拓扑、权重分片、collective 插入点、以及「某个并行组合下 attention metadata 怎么拼」。选不对并行,不是慢一点,而是直接挂(NCCL 超时、显存不均、专家热点)。

3.4 量化加载、多 LoRA、结构化输出、投机接口

3.5 OpenAI 兼容 API 与观测钩子

生产集成成本往往高于内核微优化。稳定的 /v1/chat/completions、流式、usage 字段、错误码,能让网关与评测脚本少写一半胶水。观测上,框架至少应暴露:queue wait、TTFT、TPOT、KV 占用、prefix cache hit rate、running/waiting 队列长度、swap/recompute、batch size、preempt 次数——再交给 23 那套 GenAI 可观测栈。

四、常用引擎地图(2026)

4.1 能力矩阵(定性)

下表是工程直觉图,不是分数榜。同一引擎在不同模型/卡型上的相对位置会变;细节横评仍以 [13] 为准。

能力矩阵

简要注记:

4.2 选型多路径

选型路径

分流口诀:

  1. 单卡开发 / 演示 → vLLM 或 Ollama。
  2. 高吞吐多租户 API → vLLM 或 SGLang,再叠 21、22。
  3. NVIDIA 极致延迟且团队能养编译栈 → TensorRT-LLM。
  4. 昇腾国产卡 → MindIE(不要假装 vLLM 容器换个 base image 就完事)。
  5. DeepSeek V4.1 + 匹配硬件 → 认真读 28,再决定 DeepInfer 还是社区适配。

五、底层举例(讲透四件)

下面四件尽量落到可核对的状态,而不是只复述 11 / 12 的结论。机制长文仍回那两篇;这里补「框架怎么把它们钉成一步调度」。

5.1 Prefill vs Decode 的 Roofline

Prefill vs Decode

把算术强度粗记成「本步算了多少 FLOP / 搬了多少字节」。Prefill 一次吃进很多 token,强度高,容易撞算力墙(图右侧);Decode 每步只追加极少 query、却要扫(或分页扫)整份 KV,强度低,容易撞带宽墙(图左侧)。纵轴是有效算力(FLOP/s)示意,不是直接把请求吞吐画成坐标。

因此:

5.2 PagedAttention / block table(含一步 append)

见 fig5。kernel 侧通过 block_tables[req][logical_block] 找到物理页。append 一个新 token 时:当前 block 未满 → 写下一 slot;写满后 → 才从 free pool 分配新 block,并在 block table 末尾追加映射。框架 bug 的高发区往往在 refcount、prefix 共享(写时复制)、抢占换出后再换入的一致性。

最小 worked example(block_size = 16,数字仅示意):

时刻 waiting running 说明
t0 A(prompt=20), B(prompt=10) ∅ 两请求入队
t1 B A 调度选 A 做 prefill:A 需 ⌈20/16⌉=2 个 block,例如物理页 {7,3};block_tables[A]=[7,3],第 2 块只用了 4/16 slot
t2 ∅ A, B B 入 running:B 需 1 block,例如 {9};若 A/B 共享系统前缀,可提高 refcount 复用同一物理页,而不是各拷一份
t3 ∅ A, B decode 一步:A 在 block 3 的下一 slot 写新 KV(未满则不新开块);若本步 token budget 不足,B 可能继续等下一 step
t4 ∅ B A 结束:refcount–,无共享则归还 {7,3};running 容量归还;waiting 仍是「未调度请求」队列,不是 slot 池

读代码时,把「逻辑块号 → 物理页 → slot 偏移」三条线同时盯住;错一条,attention 就会读到别人的 KV。

5.3 Continuous Batching:一步 SchedulerOutput

见 fig4。实现上,Scheduler 维护 waiting / running;每步大致:

SchedulerOutput = {
  scheduled:   # 本步要跑的 req(prefill 或 decode)
  blocks_to_alloc / blocks_to_free,
  swapped_in / swapped_out,   # 若启用 CPU swap
  preempted,                  # 被挤出 running 的 req
}
Worker: prepare_inputs(SchedulerOutput) → forward → sample

伪代码(概念,非某一版本 API):

def schedule(waiting, running, budget_tokens, free_blocks):
    batch = []
    # 1) 先尽量留下一步的 decode,避免饿死已上线会话
    for r in running:
        if budget_tokens <= 0: break
        batch.append(r); budget_tokens -= 1
    # 2) 再用剩余 budget / 块 接纳 waiting 的 prefill(可 chunk)
    while waiting and budget_tokens > 0 and free_blocks > 0:
        r = waiting[0]
        need = blocks_needed_for_next_chunk(r)
        if need > free_blocks: break
        alloc(r, need); waiting.pop(0); running.append(r)
        batch.append(r); budget_tokens -= chunk_tokens(r)
    return SchedulerOutput(batch, ...)

vLLM V1 的主调度器在公开树里仍是 Python(例如 vllm/v1/core/sched/scheduler.py 的 Scheduler),EngineCore 重构后把调度循环与执行边界划得更清;C++/CUDA 主要落在 attention / sampler 等 kernel 与扩展路径,不要笼统写成「调度下沉到 C++」。SGLang 则把 radix 命中与缓存感知调度绑得更紧——产品差异见 13,机制共性见 12。

客户端 abort / 断连时,框架必须:从 running/waiting 摘掉请求 → 停掉后续 sample → 按 refcount 释放 KV。漏这一步,显存会在「用户已经走了」之后继续涨。

5.4 Attention backend 分层

Attention 后端分层

读开源代码时,建议固定视角:

  1. 谁选择 backend(按 dtype、是否 paged、prefill/decode、硬件能力);
  2. 谁构造 metadata(positions、block tables、cu_seqlens、alibi/rope);
  3. 哪个 kernel 真正跑(FA2/FA3、FlashInfer、Triton fused、厂商 plugin)。
概念 常见落点(vLLM V1 风格,版本会变) 可观察信号
调度决策 v1/core/sched/scheduler.py running/waiting 长度、preempt
Block 表 KV cache manager / block pool KV 占用、allocation 失败
Backend 选择 attention backend registry 日志里的 FA / FlashInfer 名称
实际算子 FlashAttention / FlashInfer / plugin 单步耗时、GPU SM 利用率

FlashAttention 族解决的是 IO-aware tiling;FlashDecoding 针对 decode 的拆分;FlashInfer 把 paged 布局与采样周边接得更齐。框架价值是在正确的步选对正确的 backend,并在切换时不破坏 KV 语义。

5.5 开放问题(框架层仍在吵什么)

  1. PD 分离 vs chunked prefill:何时拆池比同池交织更划算?看 prompt/decode 比、网络与 KV 传递成本,没有万能阈值。
  2. block-hash vs radix:多租户下前缀共享的语义与隔离;命中率高不等于账单与噪声公平。
  3. 编译栈 vs 动态图:TRT-LLM 一类峰值 vs 换模型/换架构的维护税。
  4. 框架 API vs 外置网关:限流、多租户、统一鉴权放引擎内还是 22——边界清了才好排障。

进阶拼图(PD 分离槽位、投机解码槽位)见下图;落地分别指向 21 与 15/28。

PD 与投机槽位

六、部署 Demo(按公开文档整理;标明假设)

部署拓扑

共同假设:Linux;Demo A/B 需要匹配驱动的 NVIDIA GPU;能拉 Hugging Face 或已有本地权重。端口:A/B 用 8000;Demo C 的 llama.cpp 示例用 8080,Ollama 默认常为 11434——不要混用。本文不下载大模型、不声称已在本机跑通;下列命令按公开文档整理(核对意识:2026-10-29),版本会变。

6.1 Demo A:vLLM OpenAI server

文档入口:https://docs.vllm.ai(安装与 OpenAI 兼容 server 页)。

python3 -m venv .venv && source .venv/bin/activate
pip install -U pip
pip install vllm   # 版本按文档选择;CUDA 轮子需匹配驱动

export MODEL=Qwen/Qwen2.5-0.5B-Instruct
# served-model-name 固定后,下面 curl 不必跟着重写 HF id
vllm serve "$MODEL" --host 0.0.0.0 --port 8000 --served-model-name demo

就绪检查(先于 chat):

curl -s http://127.0.0.1:8000/v1/models | head

Docker 形态(镜像 tag 随发布变化;常需 --ipc=host,先查文档):

docker run --gpus all --ipc=host -p 8000:8000 \
  vllm/vllm-openai:latest \
  --model "$MODEL" --served-model-name demo

冒烟(model 与 --served-model-name 一致;示意输出,未实跑):

curl http://127.0.0.1:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "demo",
    "messages": [{"role":"user","content":"用一句话解释 KV cache"}],
    "max_tokens": 128
  }'

常见失败:

现象 可能原因 处理方向
CUDA OOM 模型过大 / KV 预留过高 换小模型;降 max_model_len;量化
拉模型超时 HF 网络 镜像站或预下载到本地路径
nvidia-smi 见不到进程 没进 GPU 容器 / 驱动不匹配 检查 --gpus、驱动与 CUDA
接口 404 / 模型名不符 路径或 served-model-name 不一致 先打 /v1/models,再对齐 curl 的 model

6.2 Demo B:SGLang / LMDeploy 冒烟(串行对照)

本节是冒烟启动,不是完整 benchmark。真正的 TTFT / 缓存命中 / 多轮前缀对照,直接跟 12 / 13 的统一流程,避免在未实跑时另造一套口径。

SGLang(仓库 https://github.com/sgl-project/sglang;当前 quickstart 也可能推荐 uv pip + sglang serve,以官方页为准):

python3 -m venv .venv-sgl && source .venv-sgl/bin/activate
pip install "sglang[all]"   # 或文档推荐的 uv 安装方式

python -m sglang.launch_server \
  --model-path "$MODEL" \
  --host 0.0.0.0 --port 8000
# 同样先 curl /v1/models,再打 /v1/chat/completions

LMDeploy(默认 API 端口常为 23333,这里显式指定 8000):

python3 -m venv .venv-lmd && source .venv-lmd/bin/activate
pip install lmdeploy
lmdeploy serve api_server "$MODEL" --server-port 8000

选 SGLang 还是 LMDeploy:偏 Agent/前缀 → SGLang;偏 TurboMind 路径对照 → LMDeploy。不要和 vLLM 同时占同一张卡——串行更干净。

6.3 Demo C:llama.cpp / Ollama(本地 / 边缘)

无 GPU 或只想本机玩:

# Ollama(安装见 https://ollama.com ;默认端口常 11434)
ollama pull qwen2.5:0.5b
ollama run qwen2.5:0.5b "用一句话解释 continuous batching"

llama.cpp(需 GGUF;从源码构建时二进制常见为 ./build/bin/llama-server,也可使用发行页预编译包):

# 示例:已 cmake 构建后
./build/bin/llama-server -m ./qwen2.5-0.5b-q4_k_m.gguf --port 8080

边界:Demo C 验证的是「本地可用性」,不是数据中心调度能力;不要把 Ollama 的体感延迟直接外推到 vLLM 集群。

6.4 环境与安全备忘

七、落地选型与多路径

四条采用路径

务实顺序:

  1. 路径 A(学习):11 → 12 → 本篇 → 13。先建立「调度 / KV / kernel」分层,再看产品差异。
  2. 路径 B(上手):Demo A 跑通 → Demo B 对照一项差异(缓存或吞吐)→ 需要本地再 Demo C。
  3. 路径 C(生产):引擎之上补 21 服务化、22 网关、23 观测;没有 SLO 与限流的 curl 通 ≠ 可上线。
  4. 路径 D(特化):硬件/模型强约束时再进 TRT-LLM、MindIE 或 DeepInfer;默认不要双栈并行太久——回归与镜像成本是实打实的。

「多路径」不是犹豫症,而是承认:开发机、在线集群、边缘盒子、国产卡、模型厂特化栈从来不是同一个优化问题。

八、与系列交叉引用 + 延伸阅读

你想解决的问题 去哪篇
Prefill/Decode/KV 心智 11 推理引擎基础
PagedAttention / Continuous Batching 机制 12
引擎产品横评与决策树 13
量化与 KV 量化 14
投机解码 / MTP 15
服务化 / PD 分离 21
网关 22
可观测 23
成本与安全合规 24
DeepSeek 生产引擎切片 28 DeepInfer
国产芯片主路径 26

参考(公开文档与仓库)


本篇定位回顾:在 [11]/[12] 的机制与 [13] 的产品横评之间,补上「框架为什么存在、内部如何分层、如何用 Demo 摸到那一层」;深度放在解剖学与可运行路径,而不是再做一张吞吐排行榜。

源码下载

本文相关源码已整理,共 1 个文件。

打开下载目录 →

文章导航

分类入口
architectureai-infra
标签入口
#llm#infra#inference#vllm#sglang#tensorrt-llm#lmdeploy#mindie#deepinfer#serving

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-04-22 · architecture / ai-infra

【大模型基础设施工程】21:推理服务化

从单机引擎到生产级集群:Triton、Ray Serve、KServe、vLLM OpenAI Server、PD 分离、LoRA 多租户、KEDA 自动扩缩、Serverless GPU 的全景工程实战。


By .