引言:为什么大模型推理需要专门的框架
当你向ChatGPT提问时,背后的推理系统每秒需要处理数百万次的矩阵乘法运算。与传统的神经网络推理不同,大语言模型的推理具有独特的性能特征:自回归生成导致每个请求的输出长度不可预测,KV缓存的动态增长使得内存管理成为核心挑战,而预填充与解码两个阶段截然不同的计算模式,更是要求系统设计者在延迟和吞吐量之间进行精细权衡。
传统的深度学习推理框架如ONNX Runtime或TensorFlow Serving,虽然在小模型上表现优异,但在面对大模型推理的独特需求时,往往力不从心。这催生了专门的大模型推理框架——vLLM、TensorRT-LLM、llama.cpp等,它们各自以不同的设计哲学,试图解决同一个问题:如何在有限的硬件资源上,以最低的成本、最快的速度,服务尽可能多的用户。
本文将深入解析这三大主流框架的技术架构,揭示它们在内存管理、批处理策略、算子优化等核心维度上的技术博弈,帮助你理解为什么2024-2025年见证了这些框架的快速演进,以及如何根据你的业务场景做出正确选择。
第一章:大模型推理的核心挑战
1.1 自回归生成的性能困境
大语言模型的推理过程可以分解为两个截然不同的阶段:预填充(Prefill) 和解码(Decode)。
flowchart TD
A[用户请求] --> B[预填充阶段]
B --> |处理完整提示词| C[计算KV缓存]
C --> D[解码阶段]
D --> |逐词生成| E{是否结束?}
E --> |否| F[更新KV缓存]
F --> D
E --> |是| G[返回完整响应]
style B fill:#e1f5fe
style D fill:#fff3e0
预填充阶段一次性处理整个提示词,计算密集型,可以充分利用GPU的并行计算能力。而解码阶段则完全不同——每生成一个token,都需要加载整个模型的权重和所有的KV缓存,只为计算一个新token。这种内存带宽受限的特性,使得解码阶段成为推理速度的瓶颈。
根据清华大学2025年发布的《A Survey of LLM Inference Systems》综述论文,一个70B参数的模型在生成时,每秒需要从显存读取约140GB的数据(假设bf16精度),而解码阶段每次仅产生约32字节的输出。这种极端的计算强度不平衡,是传统推理框架难以高效处理大模型推理的根本原因。
1.2 KV缓存的内存管理噩梦
KV缓存是大模型推理的核心优化技术。它缓存了注意力机制中的Key和Value矩阵,避免在生成每个新token时重新计算之前所有token的表示。然而,这也带来了一个棘手的问题:KV缓存的大小是动态增长的,且无法预先确定。
考虑一个服务100个并发请求的系统,每个请求可能生成1到1000个token不等。传统方法是预先为每个请求分配最大可能长度的内存,但这导致了严重的内存碎片化和内部碎片问题。根据vLLM团队的测量,在静态内存分配策略下,实际内存利用率可能低至20-30%。
1.3 批处理的两难抉择
批处理是提升GPU利用率的有效手段。然而,大模型推理的批处理面临独特的挑战:
- 静态批处理:等待一批请求全部完成才开始下一批,导致GPU空闲等待,吞吐量低
- 请求长度差异:不同请求的输出长度差异巨大,短请求完成后,为其分配的资源被浪费
- 首token延迟:用户期望快速看到第一个输出,但传统批处理可能导致长时间等待
这些挑战构成了大模型推理框架设计的核心约束条件。接下来,我们将看到三大主流框架如何以不同的技术路径应对这些挑战。
第二章:vLLM——内存管理的革命者
2.1 PagedAttention:将操作系统的智慧引入GPU
vLLM的核心创新是PagedAttention算法,它将操作系统中虚拟内存管理的分页思想引入了GPU内存管理。这一设计从根本上解决了KV缓存的内存碎片化问题。
flowchart LR
subgraph 传统方法
A1[请求1<br/>预留1000 tokens]
A2[请求2<br/>预留1000 tokens]
A3[请求3<br/>预留1000 tokens]
A4[实际使用<br/>约300 tokens<br/>浪费70%]
end
subgraph PagedAttention
B1[Block 1<br/>16 tokens]
B2[Block 2<br/>16 tokens]
B3[Block 3<br/>16 tokens]
B4[...按需分配<br/>零浪费]
end
A4 -.->|对比| B4
style A4 fill:#ffcdd2
style B4 fill:#c8e6c9
PagedAttention将KV缓存划分为固定大小的块(Block),每个块存储16个token的Key和Value向量(默认配置)。当模型需要存储新的KV缓存时,从全局的块池(Block Pool) 中按需分配,而不是预先分配连续的大块内存。
这种设计带来了几个关键优势:
- 消除内部碎片:只有实际使用的块才被分配,浪费仅限于最后一个不完整块(最多15个token的存储)
- 支持内存共享:多个请求可以共享相同的KV缓存块,这对于前缀缓存至关重要
- 灵活的抢占机制:当内存不足时,可以精确地驱逐特定请求的块,而不是整体OOM
2.2 连续批处理:让GPU永不停歇
vLLM的另一个核心创新是连续批处理(Continuous Batching),也称为迭代级调度。与传统的静态批处理不同,连续批处理在每个解码迭代后都会重新调度请求队列。
gantt
title 连续批处理 vs 静态批处理
dateFormat mm:ss
section 静态批处理
请求A处理 :a1, 00:00, 10s
请求B处理 :b1, 00:00, 15s
请求C处理 :c1, 00:00, 8s
GPU空闲等待 :crit, 00:08, 2s
新批次 :n1, 00:15, 10s
section 连续批处理
请求A处理 :a2, 00:00, 10s
请求B处理 :b2, 00:00, 15s
请求C处理 :c2, 00:00, 8s
请求D(新加入) :d2, 00:08, 12s
请求E(新加入) :e2, 00:10, 10s
其工作原理如下:
- 每个解码迭代开始时,调度器检查是否有请求已完成或新请求到达
- 将完成的请求从运行队列移除,释放其KV缓存块
- 将新请求加入运行队列(如果内存允许)
- 执行前向传播,生成所有运行请求的下一个token
这种设计使得GPU几乎不会空闲等待。根据vLLM论文的实验数据,相比静态批处理,连续批处理可以将吞吐量提升2-10倍,具体取决于请求长度的分布。
2.3 选择性批处理:解决变长序列的融合难题
连续批处理面临一个技术挑战:不同请求的序列长度不同,如何将它们批处理为一个张量?传统的批处理要求所有序列长度相同,这需要填充(Padding),浪费计算资源。
vLLM采用**选择性批处理(Selective Batching)**策略:将注意力层和非注意力层分开处理。非注意力层(如FFN、LayerNorm)可以轻松处理变长序列(将所有序列拼接成一个长序列),而注意力层则通过PagedAttention的特殊实现,在不填充的情况下处理变长序列。
# 选择性批处理的简化示意
# 非注意力层:直接处理拼接后的序列
concatenated_hidden = torch.cat([seq1, seq2, seq3], dim=0)
output = ffn(concatenated_hidden) # 无需填充
# 注意力层:通过块表索引访问KV缓存
# 每个序列有自己的块表,指向全局块池中的块
attention_output = paged_attention(
query, key_cache, value_cache,
block_tables=[block_table_1, block_table_2, block_table_3]
)
2.4 vLLM的架构全景
vLLM V1引擎的核心架构由以下组件构成:
flowchart TD
subgraph LLM引擎
A[处理器<br/>Tokenizer & 预处理]
B[调度器<br/>FCFS/Priority]
C[KV缓存管理器<br/>块池 & 分配器]
D[模型执行器<br/>Worker & Forward Pass]
E[输出处理器<br/>Detokenize & 后处理]
end
F[请求队列] --> A
A --> B
B --> |调度决策| C
C --> |块分配| D
D --> |前向传播| E
E --> G[响应输出]
C --> H[全局块池<br/>Block Pool]
style C fill:#e8f5e9
style D fill:#fff8e1
调度器是整个系统的核心。它维护三个队列:
waiting:等待处理的请求running:正在解码的请求swapped:被换出到CPU内存的请求(当GPU内存不足时)
调度策略支持两种模式:
- FCFS(先来先服务):默认策略,公平但可能不够高效
- Priority(优先级):高优先级请求优先服务,适用于SLA敏感的场景
KV缓存管理器维护一个全局的块池,通常包含数十万个块(取决于显存大小和块大小)。每个块的状态(空闲/已分配)、引用计数、以及关联的请求ID都被精确跟踪。
2.5 高级特性:前缀缓存与投机解码
vLLM还实现了多项高级优化技术:
**前缀缓存(Prefix Caching)**是处理多轮对话或系统提示词的关键优化。当多个请求共享相同的前缀时,前缀缓存可以重用已计算的KV缓存,避免重复计算。其实现原理是对每个块计算哈希值,存储在全局哈希表中,当新请求到达时,检查其前缀是否已在缓存中。
sequenceDiagram
participant R1 as 请求1
participant Cache as KV缓存
participant R2 as 请求2
R1->>Cache: 处理前缀 "你是AI助手..."
Cache->>Cache: 计算哈希 H1, H2, H3
Cache->>Cache: 存储KV块
R2->>Cache: 处理前缀 "你是AI助手..."
Cache->>Cache: 计算哈希 H1, H2, H3
Cache->>Cache: 命中缓存!
Cache-->>R2: 直接复用KV块
Note over R2,Cache: 省略前缀的预填充计算
**投机解码(Speculative Decoding)**是另一种加速技术。其核心思想是使用一个小型草稿模型预测接下来的K个token,然后用目标模型一次性验证这K个预测。如果预测正确,一次前向传播就能生成多个token;如果预测失败,也只损失一次验证的开销。vLLM支持多种草稿策略,包括n-gram、EAGLE和Medusa。
第三章:TensorRT-LLM——硬件优化的极致追求
3.1 NVIDIA的原生优化哲学
与vLLM的通用性设计不同,TensorRT-LLM代表了另一种哲学:深度绑定硬件,榨取每一滴性能。作为NVIDIA官方推出的推理框架,TensorRT-LLM能够充分利用NVIDIA GPU的所有硬件特性。
TensorRT-LLM的核心优势在于:
- 算子融合:将多个小算子合并为一个大算子,减少内存访问和内核启动开销
- 精度优化:支持FP8、INT8、INT4等多种量化格式,以及混合精度计算
- 图优化:分析计算图,消除冗余计算,优化内存布局
- 特定架构优化:针对H100、A100等不同GPU架构进行定制优化
3.2 模型编译:用时间换性能
TensorRT-LLM采用**提前编译(AOT, Ahead-of-Time)**策略。在部署模型之前,需要将模型编译成TensorRT引擎,这个过程可能需要几分钟到几小时不等。
flowchart LR
A[HuggingFace<br/>模型权重] --> B[TensorRT-LLM<br/>编译器]
B --> |优化Pass| C[TensorRT引擎<br/>.plan文件]
C --> D[推理服务]
B --> E[算子融合]
B --> F[精度校准]
B --> G[内存规划]
B --> H[CUDA Graph<br/>捕获]
style C fill:#c8e6c9
编译过程包括:
- 层融合:将Linear-Bias-ReLU等连续操作合并为单一内核
- 精度校准:确定每层的最优精度,平衡精度和性能
- 内存规划:静态分配所有中间张量的内存位置
- CUDA Graph捕获:将计算图录制为可重放的CUDA Graph
这种策略的代价是灵活性降低:更换模型或修改配置都需要重新编译。但其优势是在推理时几乎零开销——所有优化决策都已预先完成。
3.3 Paged KV Cache:学习vLLM的智慧
TensorRT-LLM也实现了类似PagedAttention的Paged KV Cache机制,但其实现更加底层和优化。关键差异包括:
- 块大小可配置:可以根据模型特性和硬件调整块大小
- 与FlashAttention深度集成:块访问模式针对FlashAttention进行了优化
- 异步块分配:在后台线程中预分配块,减少关键路径延迟
3.4 Inflight Batching:NVIDIA的连续批处理
TensorRT-LLM实现了名为Inflight Batching的连续批处理机制。其核心特点是:
- 动态形状处理:支持变长序列的无缝批处理
- 上下文与生成混合:可以在同一批次中处理预填充和解码请求
- 自动负载均衡:根据请求长度动态调整批次组成
flowchart TD
subgraph 迭代1
A1[请求A: Prefill]
B1[请求B: Decode]
C1[请求C: Decode]
end
subgraph 迭代2
A2[请求A: Decode]
B2[请求B: 完成!]
C2[请求C: Decode]
D2[请求D: Prefill<br/>新加入]
end
subgraph 迭代3
A3[请求A: Decode]
C3[请求C: Decode]
D3[请求D: Decode]
E3[请求E: Prefill<br/>新加入]
end
迭代1 --> 迭代2 --> 迭代3
3.5 性能实测:TensorRT-LLM的优势场景
根据BentoML在2024年的基准测试,TensorRT-LLM在以下场景中表现突出:
| 场景 | 模型 | vLLM | TensorRT-LLM | 优势 |
|---|---|---|---|---|
| 低延迟(<100ms TTFT) | Llama-3-70B Q4 | 85ms | 62ms | 27%更快 |
| 高吞吐 | Llama-3-8B | 4741 T/s | 5452 T/s | 15%更高 |
| 长上下文 | Llama-3-8B 128K | 较慢 | 2.72x更快 | 显著优势 |
| 超大规模(100+并发) | Llama-3-70B | 稳定 | 略优 | 边际优势 |
TensorRT-LLM在低延迟和长上下文场景中具有明显优势,这得益于其深度优化的内核和内存访问模式。然而,在中等规模的生产环境中,vLLM的性能已经足够好,而其易用性和灵活性更具吸引力。
第四章:llama.cpp——跨平台的极简主义
4.1 CPU优先的设计哲学
llama.cpp的设计哲学与vLLM和TensorRT-LLM截然不同。它的目标是:让大模型推理可以在任何硬件上运行——从高端服务器到树莓派,从NVIDIA GPU到苹果M系列芯片。
这种哲学体现在几个关键设计决策上:
- 纯C++实现:无Python依赖,启动快,内存占用小
- 量化优先:支持GGUF格式的多种量化方案,从Q4_0到Q8_0
- CPU优先:核心优化针对CPU架构,GPU支持作为加速选项
- 零依赖:除了标准C++库,不需要任何外部依赖
4.2 GGUF格式:量化艺术的巅峰
llama.cpp引入了GGUF格式,这是一种专门为大模型推理设计的文件格式。GGUF的创新之处在于:
flowchart LR
subgraph GGUF文件结构
A[文件头<br/>元数据]
B[词表<br/>Tokenizer]
C[张量信息表<br/>名称/类型/偏移]
D[量化权重<br/>Q4_0/Q5_1/KQ...]
end
A --> B --> C --> D
subgraph 量化方案
E[Q4_0: 4-bit<br/>无缩放因子]
F[Q5_1: 5-bit<br/>带缩放因子]
G[Q8_0: 8-bit<br/>高精度]
H[K-Quants: 混合精度]
end
K-Quants是llama.cpp的最新创新,它对不同层采用不同的量化策略:
- 关键层(如注意力输出层)使用较高精度(Q6_K)
- 不敏感层使用较低精度(Q4_K)
- 这种混合策略在几乎不损失模型质量的情况下,实现了更小的模型体积
4.3 CPU推理的深度优化
llama.cpp对CPU推理进行了极致优化,包括:
SIMD向量化:针对不同CPU架构使用SIMD指令
- x86: AVX2, AVX-512
- ARM: NEON
- Apple Silicon: ARM NEON + AMX(矩阵扩展)
内存访问优化:
- 缓存友好的权重布局
- 预取和循环展开
- 减少缓存未命中
多线程并行:
- 层间并行:不同层在不同线程上计算
- 注意力头并行:多个注意力头并行计算
- 批处理并行:多个序列并行处理
// llama.cpp的注意力计算简化示意
void attention_forward(
const float* query, // [n_heads, head_dim]
const float* key_cache, // [n_layers, n_ctx, n_heads, head_dim]
const float* value_cache, // [n_layers, n_ctx, n_heads, head_dim]
float* output,
int n_heads, int head_dim, int n_ctx
) {
#pragma omp parallel for // 多线程并行
for (int h = 0; h < n_heads; h++) {
// SIMD优化的注意力计算
float* scores = alloca(n_ctx * sizeof(float));
for (int t = 0; t < n_ctx; t++) {
scores[t] = dot_product_simd(
&query[h * head_dim],
&key_cache[t * n_heads * head_dim + h * head_dim],
head_dim
);
}
softmax_inplace(scores, n_ctx);
// 加权求和
weighted_sum_simd(value_cache, scores, output, ...);
}
}
4.4 GPU后端:CUDA、Metal、Vulkan
虽然CPU是llama.cpp的核心,但它也支持多种GPU后端:
| 后端 | 平台 | 特点 |
|---|---|---|
| CUDA | NVIDIA GPU | 最成熟的GPU支持,性能接近原生 |
| Metal | Apple Silicon | 充分利用统一内存架构,零拷贝 |
| Vulkan | 跨平台 | 支持AMD、Intel等GPU |
| SYCL | Intel GPU | 针对Intel架构优化 |
| HIP | AMD ROCm | AMD GPU原生支持 |
Apple Silicon的Metal后端是一个特别值得关注的优化。由于M系列芯片采用统一内存架构,CPU和GPU共享同一块物理内存,llama.cpp可以:
- 零拷贝在CPU和GPU之间传递数据
- 动态决定哪些层在CPU上计算,哪些在GPU上计算
- 支持"部分卸载":模型太大时,只将部分层放入GPU内存
4.5 llama.cpp的独特价值
llama.cpp的核心价值在于其普适性。当你的约束条件是:
- 没有NVIDIA GPU:llama.cpp是唯一的选择
- 内存有限:4-bit量化让70B模型可以在16GB内存中运行
- 需要快速原型验证:无需编译,下载即用
- 边缘设备部署:从手机到嵌入式设备都能运行
第五章:三大框架的技术博弈全景
5.1 设计哲学对比
| 维度 | vLLM | TensorRT-LLM | llama.cpp |
|---|---|---|---|
| 核心目标 | 高吞吐服务 | 极致性能 | 普适运行 |
| 硬件绑定 | CUDA/ROCm | NVIDIA CUDA | 跨平台 |
| 内存管理 | PagedAttention | Paged KV Cache | 静态分配 |
| 批处理策略 | 连续批处理 | Inflight Batching | 静态/动态 |
| 优化深度 | 算法层面 | 硬件层面 | 编译层面 |
| 易用性 | 高 | 中 | 极高 |
| 灵活性 | 高 | 低 | 中 |
5.2 性能特征分析
根据清华大学综述论文的分析,三大框架的性能特征可以总结为:
radar-beta
title 推理框架性能雷达图
axis 吞吐量["吞吐量"], 延迟["首Token延迟"], 内存效率["内存效率"], 易用性["易用性"], 硬件兼容["硬件兼容"], 模型支持["模型支持"]
curve{vLLM}: 9, 7, 9, 9, 6, 9
curve{TensorRT-LLM}: 9, 9, 8, 5, 3, 7
curve{llama.cpp}: 5, 6, 7, 10, 10, 8
吞吐量:vLLM和TensorRT-LLM在高并发场景下吞吐量接近,都远超llama.cpp
首Token延迟:TensorRT-LLM在短提示词场景下延迟最低,得益于其优化的预填充内核
内存效率:vLLM的PagedAttention提供了最佳的内存利用率
易用性:llama.cpp几乎零配置即可运行,vLLM次之,TensorRT-LLM需要编译步骤
硬件兼容性:llama.cpp支持最广泛的硬件,vLLM支持CUDA和ROCm,TensorRT-LLM仅支持NVIDIA
模型支持:vLLM支持最广泛的模型架构(包括MoE、多模态等)
5.3 适用场景指南
基于以上分析,我们可以给出场景化的选择建议:
flowchart TD
A[选择推理框架] --> B{是否有NVIDIA GPU?}
B --> |否| C[llama.cpp<br/>唯一选择]
B --> |是| D{是否需要极致性能?}
D --> |是| E{是否接受编译延迟?}
E --> |是| F[TensorRT-LLM<br/>最佳性能]
E --> |否| G[vLLM<br/>性能接近,更灵活]
D --> |否| H{并发用户数?}
H --> |<10| I[vLLM或llama.cpp<br/>都可以]
H --> |>50| J[vLLM<br/>高吞吐优势]
style C fill:#c8e6c9
style F fill:#c8e6c9
style G fill:#c8e6c9
style J fill:#c8e6c9
选择vLLM当你需要:
- 高并发在线服务(100+ QPS)
- 快速部署和迭代(无需编译)
- 支持多种模型架构(MoE、多模态)
- 灵活的硬件选择(NVIDIA、AMD)
选择TensorRT-LLM当你需要:
- 极致的延迟优化(<50ms TTFT)
- 最大吞吐量(成本敏感的大规模部署)
- 长上下文处理(128K+ tokens)
- 对NVIDIA硬件的深度利用
选择llama.cpp当你需要:
- 在非NVIDIA硬件上运行
- 本地推理或边缘部署
- 快速原型验证
- 最小化依赖和启动时间
第六章:未来演进方向
6.1 分离式架构:预填充与解码的解耦
一个新兴的趋势是分离式推理架构(Disaggregated Inference),即将预填充和解码分配到不同的计算节点。这种架构的优势在于:
- 资源优化:预填充节点可以配置更多计算资源,解码节点配置更多内存带宽
- 负载均衡:独立扩展预填充和解码能力
- 延迟控制:预填充节点专注于降低TTFT,解码节点专注于提升解码速度
vLLM已经在V1版本中支持这种架构,允许配置N个预填充实例和M个解码实例,KV缓存通过网络传输。
6.2 量化技术的深度融合
量化已经从推理优化技术演进到训练感知量化。最新的趋势包括:
- INT4训练:直接使用4-bit精度进行训练,大幅降低训练成本
- 混合精度量化:不同层使用不同精度,平衡精度和效率
- 动态量化:根据输入动态调整精度,在关键位置保持高精度
6.3 推理-训练一体化
随着测试时计算(Test-Time Compute)的兴起,推理和训练的边界正在模糊。未来框架可能需要同时支持:
- 推理时的在线学习
- 强化学习推理(如o1模型的思维链)
- 动态模型更新
结语:没有银弹,只有权衡
大模型推理框架的演进史,本质上是内存、计算、延迟、吞吐量四个维度上的权衡史。vLLM选择在内存管理上突破,TensorRT-LLM选择在硬件优化上深耕,llama.cpp选择在普适性上发力。
正如计算机科学的永恒真理:没有银弹。选择哪个框架,取决于你的业务约束和优化目标。但理解它们背后的技术博弈,能帮助你在面对复杂的生产环境时,做出更明智的决策。
当你在深夜调试推理服务,看着GPU利用率在30%到90%之间波动时,希望这篇文章能帮助你理解:这不只是配置问题,这是系统设计的哲学选择。而每一次选择,都在定义你的AI产品的用户体验边界。
参考文献:
[1] Kwon et al., “Efficient Memory Management for Large Language Model Serving with PagedAttention”, SOSP 2023
[2] Pan & Li, “A Survey of LLM Inference Systems”, arXiv:2506.21901, 2025
[3] NVIDIA, “TensorRT-LLM: High-Performance Inference for Large Language Models”, 2024
[4] ggml-org, “llama.cpp: LLM Inference in C/C++”, GitHub Repository
[5] Yu et al., “Orca: A Distributed Serving System for Transformer-Based Generative Models”, OSDI 2022
[6] Dao et al., “FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness”, NeurIPS 2022
[7] Leviathan et al., “Fast Inference from Transformers via Speculative Decoding”, ICML 2023
[8] OpenAI, “How AI Training Scales: The Gradient Noise Scale”, 2018