2、LLM推理基础:Transformer架构、自回归生成、KV Cache、预填充与解码阶段、吞吐量与延迟等核心概念
要深入理解主流LLM推理引擎(如vLLM、TensorRT-LLM、SGLang等)的设计哲学与优化手段,首先必须牢固掌握LLM推理的底层机制。推理引擎的每一项优化——从PagedAttention到Continuous Batching,从算子融合到量化——本质上都是在解决Transformer自回归解码过程中的特定瓶颈。本章将系统拆解LLM推理的五大核心概念:Transformer架构的推理视角、自回归生成范式、KV Cache机制、预填充与解码两阶段模型,以及吞吐量与延迟的性能指标体系。这些概念是后续章节对比各推理引擎功能特性的“通用语言”,也是进行性能调优和容量规划的理论基础。
上图勾勒了LLM推理的核心要素及其相互关系。Transformer解码器是计算主体,自回归生成定义了逐的交互模式,KV Cache是连接预填充与解码阶段的“记忆”载体,而吞吐量与延迟则是衡量整个系统效率的最终标尺。下面我们逐一深入剖析。
2.1 Transformer架构的推理视角
核心原理:从训练到推理的范式转换
现代LLM(如系列、Llama系列、Qwen系列)均基于Transformer解码器架构。在推理阶段,模型权重已固定(除非涉及微调),网络的前向传播过程不再有反向传播和梯度更新。这意味着推理时的计算图是静态的,这为推理引擎提供了极大的优化空间——例如算子融合(将多个线性变换合并)、层间融合、以及针对特定GPU架构的kernel定制。
从推理视角看,Transformer解码器层主要由两个子层构成:掩码多头自注意力(Masked Multi-Head Attention)和前馈神经网络(FFN)。自注意力机制允许模型在生成当前时,关注输入序列中所有之前的位置(包括当前),从而捕捉长距离依赖。FFN则对每个位置的表示进行非线性变换,增强模型的表达能力。此外,层归一化(LayerNorm)和残差连接(Residual Connection)贯穿其中,保证训练稳定性和深层网络的梯度流动。
| 组件 | 训练阶段行为 | 推理阶段行为 | 推理引擎优化关注点 |
|---|---|---|---|
| Q/K/V线性投影 | 计算所有的Q/K/V | 仅计算当前的Q,K/V从Cache读取 | 矩阵乘法算子融合、精度校准 |
| 注意力分数计算 | 并行计算所有位置 | 仅计算当前与历史位置的分数 | 访存优化、FlashAttention |
| FFN层 | 并行处理所有 | 仅处理当前的表示 | 算子融合、量化(INT8/FP8) |
| LayerNorm | 基于batch统计 | 基于单统计 | kernel融合、减少内存往返 |
判断方法:如何识别推理架构的瓶颈
在推理引擎的Profiling工具(如Nsight Compute、PyTorch Profiler)中,我们常看到两类瓶颈:计算瓶颈(Compute-bound)和访存瓶颈(Memory-bound)。对于Transformer推理,预填充阶段通常表现为计算瓶颈(大量矩阵乘法),而解码阶段则几乎总是访存瓶颈(因为每次只生成一个,但需要读取整个KV Cache和模型权重)。判断方法是观察GPU利用率(SM Occupancy)和内存带宽利用率。如果SM占用率高但内存带宽利用率低,则是计算瓶颈;反之则是访存瓶颈。
案例拆解:Llama-2-7B的推理计算量估算
假设输入序列长度为128,输出序列长度为128。对于Llama-2-7B(32层,隐藏维度4096),预填充阶段需要处理128个的矩阵乘法,计算量约为 2 * 128 * 4096 * 4096 * 32 ≈ 137 GFLOPs。而解码阶段每生成一个,计算量仅为 2 * 1 * 4096 * 4096 * 32 ≈ 1.07 GFLOPs。然而,解码阶段需要读取的KV Cache大小为 2 * 32 * 128 * 4096 * 2 bytes ≈ 67 MB(FP16),加上模型权重14GB,总访存量远大于计算量。这清晰地解释了为何解码阶段是访存瓶颈。
常见误区:忽略“权重访存”
许多初学者认为KV Cache是解码阶段唯一的访存开销。实际上,模型权重的访存开销远大于KV Cache。以7B模型为例,每生成一个,必须读取全部14GB权重(FP16),而KV Cache在长上下文时可能只有几百MB。因此,推理引擎的优化重点之一是权重缓存与复用(如使用更大的L2 Cache或优化kernel的数据复用策略)。
2.2 自回归生成:逐的序列构建
核心原理:条件概率的链式分解
自回归(Autoregressive)生成是LLM推理的基本范式。其数学本质是条件概率的链式法则:P(y1, y2, ..., yn | x) = P(y1|x) * P(y2|x, y1) * ... * P(yn|x, y1, ..., yn-1)。模型在每一步只预测下一个的概率分布,然后通过采样策略(如贪心搜索、Top-k、Top-p)选择或采样出一个,将其拼接到输入序列末尾,作为下一步的输入。这个过程循环进行,直到生成结束符(EOS)或达到最大长度。
这种逐生成方式带来了两个关键特性:顺序依赖性(第t+1个依赖于前t个的输出)和不可并行性(无法同时生成多个)。这直接导致了推理延迟的累积。
操作流程:一次完整的自回归生成
- 输入预处理:将用户提示词通过Tokenizer转换为 ID序列。
- 预填充:将整个输入序列一次性送入模型,并行计算所有输入位置的隐状态,并生成第一个输出。
- 循环解码:将新生成的作为输入,送入模型,计算其隐状态,并预测下一个。
- 终止判断:检查是否生成EOS 或达到max_new_tokens限制。
案例拆解:贪心解码与束搜索的代价
贪心解码(Greedy Decoding)每一步选择概率最高的,速度快但可能陷入局部最优。束搜索(Beam Search)维护多个候选序列(束宽=4),每一步保留概率最高的4个序列,最终选择整体概率最高的序列。束搜索的代价是计算量增加约束宽倍,且KV Cache占用也相应增加。在实际部署中,对于聊天场景,通常使用采样(Sampling)而非束搜索,因为后者容易产生重复和枯燥的文本。
常见误区:误以为可以并行生成多个
虽然存在“投机采样”(Speculative Decoding)或“并行解码”技术,但标准自回归生成是严格串行的。投机采样通过一个小模型先草拟多个,再由大模型并行验证,但这并未改变自回归的本质,只是通过“猜测-验证”减少了串行步数。理解这一点,才能理解为何推理引擎要引入Continuous Batching来提升吞吐量——因为单个请求的串行延迟无法缩短,只能通过并行处理多个请求来提升整体效率。
2.3 KV Cache:推理的“记忆”与显存瓶颈
核心原理:避免重复计算的缓存机制
在自回归解码的每一步,计算当前的注意力分数时,需要用到之前所有的Key(K)和Value(V)向量。如果不加缓存,每一步都需要重新计算之前所有的K和V,这将导致计算量随序列长度呈平方级增长,完全不可接受。KV Cache正是为了解决这一问题而设计的:在预填充阶段,计算并存储所有输入的K和V;在解码阶段,每生成一个新,计算其K和V并追加到缓存中,同时从缓存读取历史K和V用于注意力计算。
KV Cache的大小计算公式为:2(K和V) × 层数 × Batch Size × 序列长度 × 注意力头数 × 头维度 × 精度字节数。例如,对于Llama-2-7B(32层,32个头,头维度128),在FP16精度下,每个的KV Cache占用为 2 × 32 × 32 × 128 × 2 = 512 KB。当序列长度为4096时,单条序列的KV Cache占用高达2GB。
判断方法:显存容量规划
部署LLM时,显存占用主要由三部分构成:模型权重、KV Cache和激活值(Activations)。激活值在预填充阶段较大,但解码阶段较小。KV Cache是动态增长的,是显存规划的核心变量。推理引擎(如vLLM)通过PagedAttention将KV Cache分块管理,类似操作系统的虚拟内存分页,从而减少显存碎片,提高利用率。
| 模型规模 | 权重显存 (FP16) | KV Cache/ | 4K上下文KV Cache | 8K上下文KV Cache |
|---|---|---|---|---|
| 7B | 14 GB | 512 KB | 2 GB | 4 GB |
| 13B | 26 GB | 1 MB | 4 GB | 8 GB |
| 70B | 140 GB | 5 MB | 20 GB | 40 GB |
案例拆解:KV Cache导致的显存OOM
假设我们使用单张A100-80G部署Llama-2-13B模型。权重占用26GB,预留激活值和CUDA context约4GB,剩余可用显存约50GB。如果每条请求的上下文长度为4096,则每条请求的KV Cache占用4GB。理论上最多可并发处理12条请求。但如果推理引擎没有高效的内存管理(如vLLM的PagedAttention),由于显存碎片化,实际并发数可能只有6-8条。这就是为什么推理引擎的KV Cache管理能力直接决定了其吞吐量上限。
常见误区:忽略KV Cache的显存增长
许多初次部署者只关注模型权重的大小,而忽略了KV Cache的显存占用。当并发请求增多或上下文变长时,KV Cache会迅速耗尽显存,导致OOM。正确的做法是使用推理引擎提供的显存利用率监控,并设置合理的最大并发数和最大序列长度。
2.4 预填充与解码阶段:两种截然不同的计算模式
核心原理:计算密集与访存密集的分离
LLM推理过程被清晰地划分为两个阶段:预填充(Prefill)和解码(Decode)。预填充阶段处理整个输入提示词,并行计算所有输入的隐状态,并生成第一个输出。此阶段的计算量较大,属于计算密集型(Compute-bound),GPU的算力利用率较高。解码阶段则逐生成后续内容,每一步只处理一个,但需要读取整个KV Cache和模型权重,属于访存密集型(Memory-bound),GPU的算力利用率通常很低(可能低于10%)。
这种两阶段特性对推理引擎的设计有深远影响。例如,Chunked Prefill技术将长提示词的预填充拆分为多个小块,与解码请求交错执行,以平衡GPU利用率和延迟。而Continuous Batching则是在解码阶段动态调度多个请求,让GPU始终有足够的并行工作。
判断方法:通过Profiling识别阶段切换
使用NVIDIA Nsight或PyTorch Profiler观察GPU kernel执行时间,可以清晰看到预填充阶段的长kernel(如大矩阵乘法)和解码阶段的短kernel(如小矩阵乘法、GEMV)。预填充阶段的GPU利用率通常高于50%,而解码阶段可能低于10%。推理引擎的调度器需要在这两种阶段之间平滑切换,避免GPU空闲。
案例拆解:长提示词场景下的预填充延迟
假设用户输入一篇5000字的文章(约7000个)作为提示词,要求模型进行摘要。预填充阶段需要处理这7000个,计算量约为 2 × 7000 × 4096 × 4096 × 32 ≈ 7.5 TFLOPs。在A100(FP16算力312 TFLOPs)上,理论计算时间约为24ms。但实际由于内存带宽限制和kernel启动开销,可能需要100-200ms。这个延迟(TTFT)对用户体验影响显著。推理引擎通过预填充与解码的分离调度,可以优先处理预填充请求,减少TTFT。
常见误区:将两阶段视为一个整体
性能调优时,必须区分预填充和解码的指标。预填充阶段关注TTFT(Time To First ),解码阶段关注TPOT(Time Per Output )。将两者混为一谈,会导致无法准确定位瓶颈。例如,如果TTFT正常但TPOT很高,问题可能出在KV Cache访存或权重带宽上;如果TTFT很高,则可能是预填充计算量过大或GPU算力不足。
2.5 吞吐量与延迟:性能的度量衡
核心原理:系统效率与用户体验的平衡
吞吐量(Throughput)和延迟(Latency)是衡量LLM推理系统性能的两个核心指标,它们之间存在内在的权衡关系。吞吐量通常定义为系统在单位时间内生成的总数(tokens/s),反映了系统的整体处理能力。延迟则分为两种:TTFT(首延迟,从请求发出到收到第一个的时间)和TPOT(每输出延迟,后续每个的生成间隔)。对于流式输出,TPOT直接决定了用户感知的生成速度。
高吞吐量意味着系统可以服务更多并发请求,但通常会增加单个请求的延迟(因为需要排队和共享计算资源)。低延迟则提供更好的交互体验,但可能牺牲系统的整体利用率。推理引擎的核心目标就是在满足延迟约束(SLA)的前提下,最大化吞吐量。
判断方法:性能基准测试
进行性能测试时,需要明确测试场景:并发数、输入/输出长度分布、请求到达率。常用的基准测试工具包括LLMPerf、vLLM的benchmark脚本等。关键指标包括:
- 吞吐量:总生成数 / 总耗时(包括排队和生成时间)。
- TTFT:所有请求的TTFT平均值或P99值。
- TPOT:所有请求的TPOT平均值或P99值。
- 端到端延迟:从请求发出到完整响应接收的总时间。
| 场景 | 优化目标 | 关键指标 | 典型约束 |
|---|---|---|---|
| 在线聊天 | 低延迟 | TTFT < 500ms, TPOT < 50ms | 并发数 < 100 |
| 离线批量处理 | 高吞吐 | 吞吐量 > 1000 tokens/s | 延迟无硬性要求 |
| 代码生成 | 平衡 | TTFT < 1s, TPOT < 100ms | 并发数 < 50 |
案例拆解:通过Continuous Batching提升吞吐量
假设一个推理引擎采用静态批处理(Static Batching),必须等待一个批次的所有请求都生成完毕后,才能处理下一批请求。如果批次中有请求生成长度差异大,短请求会等待长请求,导致GPU利用率下降。而Continuous Batching(连续批处理)允许在批次中动态添加和移除请求:当一个请求生成完毕,立即将其移出批次,并加入新的请求。这样GPU始终在处理活跃的请求,显著提升了吞吐量。vLLM、TensorRT-LLM等引擎均实现了此机制。
在一个实际测试中,使用Llama-2-7B模型,输入长度128,输出长度256,并发32个请求。静态批处理的吞吐量约为800 tokens/s,而Continuous Batching的吞吐量可达1500 tokens/s,提升近一倍。但代价是单个请求的TPOT可能略有波动(因为需要与其他请求共享GPU资源)。
常见误区:只关注吞吐量而忽略延迟分布
平均延迟可能掩盖长尾问题。P99延迟(即99%的请求延迟低于该值)是更重要的指标。在推理引擎中,由于调度和资源竞争,P99延迟可能远高于平均值。性能调优时,应同时关注平均值和P99/P95值,并分析延迟分布直方图。