LLM推理加速方法综合指南(2025)
1. 概述:LLM推理的挑战
1.1 自回归生成的计算瓶颈
大语言模型的推理过程本质上是自回归生成(Autoregressive Generation)。在每个解码步骤中:
- 将已生成的 token 序列作为输入
- 执行完整的注意力计算
- 输出下一个 token 的概率分布
这一过程面临严峻的效率挑战:
| 瓶颈类型 | 具体表现 | 影响 |
|---|---|---|
| 计算瓶颈 | 每个解码步骤需完整前向传播, 注意力无法并行 | GPU利用率低下 |
| 内存带宽瓶颈 | KV Cache的频繁读写成为主要延迟来源 | 访存成为瓶颈 |
| 自回归串行化 | 下一个token依赖前一个token的生成 | 延迟难以掩盖 |
| Prefill-Decompose冲突 | 首次token生成无法有效分块 | 长prompt处理困难 |
1.2 KV Cache的内存问题
随着序列长度增长,KV Cache的内存占用呈二次方增长:
对于一个70B参数的模型,处理8192长度序列时,KV Cache可能占用超过60GB显存,这导致:
- 单序列内存占用过大:无法在单卡上运行大模型
- 批处理受限:batch size受限于KV Cache容量
- 内存碎片化:动态长度的序列导致显存碎片
1.3 延迟vs吞吐量的权衡
LLM推理优化中存在经典的权衡关系:
| 优化目标 | 侧重技术 | 典型应用场景 |
|---|---|---|
| 低延迟(Latency) | 推测解码、 Continuous Batching | 交互式对话、实时响应 |
| 高吞吐量(Throughput) | 量化、并行批处理 | 离线批量处理、API服务 |
| 长上下文 | KV Cache优化、稀疏注意力 | 文档理解、长程推理 |
2025年的优化趋势是多技术融合:将推测解码与连续批处理结合,在保证延迟的同时提升吞吐量。
1.4 2025年推理优化全景
┌─────────────────────────────────────────────────────────────────┐
│ LLM推理优化技术全景 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 推测解码 │ │ KV Cache │ │ 量化 │ │
│ │ Speculative │ │ 优化 │ │ Quantization│ │
│ │ Decoding │ │ │ │ │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └────────────┬─────┴──────────────────┘ │
│ ↓ │
│ ┌──────────────┐ │
│ │ 批处理与 │ │
│ │ 并行策略 │ │
│ └──────┬───────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ ↓ ↓ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 长上下文 │ │ 硬件适配 │ │
│ │ 优化策略 │ │ 优化 │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
2. 推测解码方法
推测解码(Speculative Decoding)是2023-2025年最重要的推理加速技术之一。其核心思想是利用验证的并行性来加速自回归生成。
2.1 Speculative Decoding回顾
详细理论请参见推测解码理论。
核心机制:
- Draft阶段:轻量级模型快速生成 个候选token
- Verify阶段:主模型并行验证所有候选token
加速比理论上限:
其中 是平均接受率,当 接近1时,加速比可达 倍。
2.2 EAGLE系列方法
EAGLE(Exploring Autoregressive Generation Layers through Efficiency)是一系列改进推测解码的方法。
EAGLE-1:自回归层的推测解码
EAGLE-1的核心洞察是:利用主模型自身的浅层作为draft模型。
// EAGLE-1核心逻辑
class EAGLE1 {
public:
// draft_model: 主模型的前L层
// target_model: 完整主模型
vector<Token> draft_generate(const vector<Token>& prompt, int draft_len) {
vector<Token> drafts;
vector<HiddenState> hidden_states;
// Draft阶段:只使用浅层,生成候选token
HiddenState h = get_shallow_hidden(prompt);
for (int i = 0; i < draft_len; i++) {
Token t = sample_from_shallow(h);
drafts.push_back(t);
h = shallow_forward(h, t);
hidden_states.push_back(h);
}
// Verify阶段:使用完整模型验证
auto accept_mask = full_verify(prompt, drafts, hidden_states);
return accepted_tokens(accept_mask, drafts);
}
};关键优势:
- 不需要额外训练draft模型
- Draft质量高(来自主模型浅层)
- 接受率高,通常达到0.8-0.95
EAGLE-2:改进的验证策略
EAGLE-2在EAGLE-1基础上进行了多项改进:
| 改进点 | EAGLE-1 | EAGLE-2 |
|---|---|---|
| 验证策略 | 贪婪验证 | 树形验证 |
| Draft长度 | 固定4-8 | 动态调整(4-16) |
| 剪枝策略 | 无 | 基于置信度的剪枝 |
| 多候选 | 单链 | 多分支树 |
EAGLE-2的树形注意力:
其中 来自不同的draft分支。
2.3 Medusa多Token预测
Medusa通过在主模型上添加多个解码头来实现并行多Token预测。详见Medusa多Token预测。
与EAGLE的对比:
| 特性 | EAGLE | Medusa |
|---|---|---|
| Draft来源 | 主模型浅层 | 额外的解码头 |
| 训练需求 | 需微调浅层 | 需训练解码头 |
| 接受率 | 较高(~0.9) | 中等(~0.7-0.8) |
| 额外参数 | 无 | 约10-15% |
2.4 方法对比与选择
┌─────────────────────────────────────────────────────────────────┐
│ 推测解码方法选择决策树 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 开始选择 │
│ │ │
│ ↓ │
│ ┌─────────────────────┐ │
│ │ 是否有训练资源? │ │
│ └──────────┬──────────┘ │
│ 是↙ ↘否 │
│ ↓ ↓ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Medusa │ │ EAGLE-1 │ │
│ │ 额外训练解码头│ │ 利用主模型浅层│ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
2025年推荐:
- 追求最高接受率:EAGLE-2 + 树形验证
- 资源受限场景:Medusa(无需训练draft模型)
- 生产环境:vLLM集成方案,开箱即用
3. KV Cache优化
KV Cache管理是LLM推理优化的核心问题之一。FlashAttention通过IO感知的tiling策略显著提升了注意力计算效率,但KV Cache的内存管理仍需专门优化。
3.1 PagedAttention原理
PagedAttention1是vLLM的核心技术,由UC Berkeley研究团队提出。其核心思想是借鉴操作系统中的分页内存管理思想来管理KV Cache。
传统KV Cache的问题:
- 预分配连续内存块,浪费严重
- 不同序列长度差异导致碎片化
- 无法高效支持变长序列
PagedAttention的解决方案:
将KV Cache划分为固定大小的块(Block):
// PagedAttention内存布局
struct KVCacheBlock {
int block_id; // 块ID
int token_count; // 当前块中的token数
int max_tokens; // 块最大容量(通常为16或32)
bool is_full; // 是否已满
// 物理内存指针
void* kv_cache_ptr; // 指向GPU内存
};物理块与逻辑块的映射:
逻辑序列: [T1, T2, T3, T4, T5, T6, T7, T8, T9]
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
物理块: [Block0] [Block1] [Block2]
[T1-T4] [T5-T8] [T9-...]
逻辑连续,但物理不连续
分页管理的优势:
| 特性 | 传统方案 | PagedAttention |
|---|---|---|
| 显存利用率 | 60-70% | 90%+ |
| 碎片化 | 严重 | 极小 |
| 批处理能力 | 受限 | 显著提升 |
| 分配延迟 | O(n) | O(1)(预分配池) |
3.2 内存碎片化问题
内存碎片化是KV Cache管理中的核心挑战。
碎片化类型:
| 类型 | 成因 | 影响 |
|---|---|---|
| 内部碎片 | 预分配过大内存块 | 浪费存储空间 |
| 外部碎片 | 频繁分配/释放 | 内存利用率下降 |
| 临时碎片 | 短序列占用长块 | 资源错配 |
PagedAttention通过以下方式解决:
- 块级管理:固定大小的块减少内部碎片
- 动态分配:按需分配新块
- 块池(Block Pool):预分配块池,避免频繁分配
// 块池管理器
class KVCachePool {
private:
vector<KVCacheBlock*> free_blocks; // 空闲块列表
vector<KVCacheBlock*> allocated_blocks; // 已分配块
public:
// O(1)时间复杂度的块分配
KVCacheBlock* allocate() {
if (!free_blocks.empty()) {
auto* block = free_blocks.back();
free_blocks.pop_back();
allocated_blocks.push_back(block);
return block;
}
// 需要时分配新块
return allocate_new_block();
}
// 块释放回池中
void free(KVCacheBlock* block) {
block->token_count = 0;
block->is_full = false;
allocated_blocks.erase(
remove(allocated_blocks.begin(),
allocated_blocks.end(),
block),
allocated_blocks.end()
);
free_blocks.push_back(block);
}
};3.3 连续批处理(Continuous Batching)
连续批处理2是生产环境中提升GPU利用率的关键技术。
传统静态批处理的局限:
时间步: T1 T2 T3 T4 T5
序列A: [P][A][B][C] 完成
序列B: [P][A][B][C][D][E] 完成
序列C: [P][A][B] 生成中
序列D: [P][A] 生成中
传统策略: 等待所有序列完成才能处理新请求
连续批处理的核心思想:当某个序列生成结束(遇到EOS token)时,立即释放其占用的资源,插入新的序列继续处理。
时间步: T1 T2 T3 T4 T5
序列A: [P][A][B][C] ← 完成后退出
序列B: [P][A][B][C][D][E] ← 完成后退出
序列C: [P][A][B] [新序列E插入]
序列D: [P][A] [序列F插入]
连续策略: GPU始终有序列在处理,最大化利用率
与PagedAttention的协同:
// 连续批处理 + PagedAttention
class ContinuousBatchingScheduler {
public:
void step() {
// 1. 检查已完成的序列
for (auto& seq : running_sequences) {
if (seq.is_finished()) {
release_kv_blocks(seq); // PagedAttention释放
schedule_new_sequence(); // 插入新序列
}
}
// 2. 执行当前批次的forward
auto batch = get_current_batch();
execute_batch(batch); // 利用PagedAttention高效执行
// 3. 更新KV Cache状态
update_kv_cache(batch);
}
};3.4 分页管理策略
3.4.1 静态分页
最简单的策略,提前分配固定数量的块:
- 优点:实现简单,无分配开销
- 缺点:无法适应动态长度
class StaticPaging {
static const int BLOCKS_PER_SEQ = 64; // 每序列固定64块
void allocate_sequence(Sequence& seq) {
for (int i = 0; i < BLOCKS_PER_SEQ; i++) {
seq.blocks.push_back(block_pool.allocate());
}
}
};3.4.2 动态分页
根据序列长度动态分配块:
class DynamicPaging {
// 分配满足序列长度的最小块数
int num_blocks_needed(int seq_len) {
return (seq_len + BLOCK_SIZE - 1) / BLOCK_SIZE;
}
void allocate_sequence(Sequence& seq, int seq_len) {
int num_blocks = num_blocks_needed(seq_len);
for (int i = 0; i < num_blocks; i++) {
seq.blocks.push_back(block_pool.allocate());
}
}
};3.4.3 2025年新趋势:StreamingLLM式管理
StreamingLLM3提出的**注意力汇聚(Attention Sink)**现象启发了新的KV Cache管理策略:
核心发现:
- 模型倾向于将注意力集中在少数”锚点”token上(如第一个token)
- 中间token的注意力权重相对分散
- 不需要缓存所有历史token
策略:
| Token类型 | 处理方式 | 理由 |
|---|---|---|
| 初始token | 始终保留 | Attention Sink |
| 近期token | 正常缓存 | 高注意力权重 |
| 历史token | 可压缩/丢弃 | 低注意力权重 |
4. 量化技术
量化(Quantization)是减少LLM显存占用和加速推理的核心技术。2025年,量化技术从INT8/INT4向更极端的FP8、INT2/INT1发展。
4.1 INT8/INT4量化
4.1.1 量化基础
量化将高精度浮点数映射到低精度整数表示:
其中 是目标位数, 是缩放因子(scale)。
4.1.2 INT8量化实现
// INT8量化
struct QuantizedTensor {
vector<int8_t> data; // 量化后的数据
float scale; // 缩放因子
int32_t zero_point; // 零点偏移(用于非对称量化)
};
// INT8矩阵乘法(使用SIMD指令)
void int8_matmul(const QuantizedTensor& a,
const QuantizedTensor& b,
float* output) {
// 使用AVX-512 VNNI指令加速
// 16个INT32累加器并行累加
__m512i acc0 = _mm512_setzero_si512();
__m512i acc1 = _mm512_setzero_si512();
// ... 更多的累加器
for (int i = 0; i < K; i += 16) {
// 加载并解量化
auto a_vals = _mm512_loadu_si512(&a.data[i]);
auto b_vals = _mm512_loadu_si512(&b.data[i]);
// VNNI乘加指令
acc0 = _mm512_dpbusd_avx512(acc0, a_vals, b_vals);
}
// 最终解量化并输出
// ...
}4.1.3 INT4量化挑战
INT4量化面临更严重的精度损失:
| 量化级别 | 理论压缩比 | 精度损失风险 | 适用场景 |
|---|---|---|---|
| INT8 | 4x | 低 | 通用推理 |
| INT4 | 8x | 中等 | 显存受限场景 |
| INT2 | 16x | 高 | 实验性应用 |
| INT1 | 32x | 极高 | 仅概念验证 |
GPTQ量化算法4:
GPTQ(Generative Pretrained Transformer Quantization)通过逐输出通道补偿来减少量化误差:
// GPTQ量化核心步骤
void gptq_quantize(const Matrix& W, QuantizedTensor& W_q) {
// 1. 找出每一列的敏感度(基于Hessian矩阵)
Matrix H = compute_hessian_approximation(W);
// 2. 按敏感度排序
vector<int> order = argsort_by_sensitivity(H);
// 3. 逐列量化并补偿
for (int col : order) {
// 计算当前列的量化误差
float error = W[:, col] - dequantize(quantize(W[:, col]));
// 将误差补偿到后续列
for (int j = col + 1; j < num_cols; j++) {
W[:, j] -= error * H[col][j] / H[col][col];
}
// 保存量化结果
W_q[:, col] = quantize(W[:, col]);
}
}4.2 FP8量化趋势
2025年,FP8(8位浮点)成为大模型推理的新标准。
FP8格式:
| 格式 | E bits | M bits | 范围 | 精度 |
|---|---|---|---|---|
| E4M3 | 4 | 3 | ±448 | 较高 |
| E5M2 | 5 | 2 | ±57344 | 较宽 |
NVIDIA H100/H200 GPU原生支持FP8:
// FP8矩阵乘法(使用H100 Tensor Core)
void fp8_matmul(const __fp8_e4m3* A,
const __fp8_e4m3* B,
float* C,
float scale_a,
float scale_b) {
// 使用CUDA的FP8 Tensor Core
cuda::wmma::fragment<cuda::wmma::matrix_a, 16, 16, 16,
__fp8_e4m3, cuda::wmma::row_major> frag_a;
cuda::wmma::fragment<cuda::wmma::matrix_b, 16, 16, 16,
__fp8_e4m3, cuda::wmma::col_major> frag_b;
cuda::wmma::fragment<cuda::wmma::accumulator, 16, 16, 16, float> frag_c;
// 加载数据
cuda::wmma::load_matrix_sync(frag_a, A, 16);
cuda::wmma::load_matrix_sync(frag_b, B, 16);
// 执行矩阵乘法
cuda::wmma::mma_sync(frag_c, frag_a, frag_b, frag_c);
// 存储结果
cuda::wmma::store_matrix_sync(C, frag_c, 16, cuda::wmma::mem_row_major);
}FP8 vs INT8 对比:
| 特性 | FP8 | INT8 |
|---|---|---|
| 动态范围 | 自动适应 | 需手动设置 |
| 精度 | 较高 | 中等 |
| 硬件支持 | H100+/MI300+ | 广泛支持 |
| 推理速度 | 更快 | 快 |
| 实现复杂度 | 较低 | 中等 |
4.3 量化感知训练
量化感知训练(Quantization-Aware Training, QAT)通过在训练过程中模拟量化来减少精度损失。
QAT vs PTQ(训练后量化):
| 特性 | QAT | PTQ |
|---|---|---|
| 训练成本 | 高(需完整训练) | 低(无额外训练) |
| 精度 | 更高 | 略低 |
| 适用场景 | 极致性能要求 | 快速部署 |
| 模型大小 | 无限制 | 受限于校准数据 |
LSQ(Learned Step Size Quantization)5:
LSQ将量化步长作为可学习参数:
// LSQ梯度更新
void lsq_update(Tensor& weight, Tensor& scale,
Tensor& grad, float lr) {
// STE(Straight-Through Estimator)梯度
Tensor grad_scale = grad * weight;
// 更新scale
scale -= lr * grad_scale.mean();
// 更新weight
Tensor quant_weight = round(weight / scale) * scale;
weight -= lr * (grad * (weight - quant_weight));
}4.4 延迟vs精度权衡
量化带来的延迟-精度权衡是工程决策的核心。
精度 ↑
│
FP16 ●────┼────● INT8
● │ ● INT4
● │
● │ ● FP8
●↓│↓●
───────────→ 延迟 ↓
2025年推荐策略:
| 应用场景 | 推荐量化 | 预期加速 | 精度损失 |
|---|---|---|---|
| 生产推理 | FP8 或 INT8 | 1.5-2x | < 1% |
| 边缘部署 | INT4 (GPTQ) | 3-4x | 2-5% |
| 极致压缩 | INT4 + 剪枝 | 5-8x | 5-10% |
5. 批处理与并行策略
5.1 Continuous Batching详解
连续批处理(Continuous Batching)是提升吞吐量的关键技术。详见第3.3节的详细讨论。
与其他技术的协同:
// 完整的推理优化框架
class OptimizedInferenceEngine {
private:
ContinuousBatchingScheduler scheduler; // 调度器
PagedAttentionManager pam; // KV Cache管理
QuantizedModel model; // 量化模型
SpeculativeDecoder* spec_decoder; // 推测解码器(可选)
public:
void forward() {
// 1. 调度:选择最优批次
auto batch = scheduler.get_batch();
// 2. KV Cache预取
pam.prepare_kv_blocks(batch);
// 3. 融合执行(量化 + attention + mlp)
if (spec_decoder) {
// 推测解码路径
auto drafts = spec_decoder->draft(batch);
auto accepted = model->verify(drafts);
scheduler.update_with_acceptance(accepted);
} else {
// 标准自回归路径
model->forward(batch);
}
// 4. 释放完成的序列
scheduler.finalize_batch();
// 5. 插入新序列
scheduler.schedule_pending();
}
};5.2 Ring Attention
Ring Attention6是处理超长序列的分布式注意力机制。
核心思想:将序列分片,每个设备计算部分注意力的结果,通过环形通信聚合。
设备0: [Segment 0] ──→ [Segment 1] ──→ [Segment 2] ──→ [Segment 3]
↓ ↑
设备1: [Segment 1] ──→ [Segment 2] ──→ [Segment 3] ──→ [Segment 0]
↓ ↑
设备2: [Segment 2] ──→ [Segment 3] ──→ [Segment 0] ──→ [Segment 1]
↓ ↑
设备3: [Segment 3] ──→ [Segment 0] ──→ [Segment 1] ──→ [Segment 2]
每一步:设备接收左侧数据,计算注意力,发送给右侧
通信复杂度分析:
| 序列长度 | 设备数 | 每设备通信量 | 总通信量 |
|---|---|---|---|
| 原始 | - | ||
| Ring Attention |
虽然总通信量不变,但每设备通信量减少为 ,且通信可与计算重叠。
5.3 流水线并行
流水线并行(Pipeline Parallelism)将模型的不同层分配到不同设备。
朴素流水线的困境:气泡(Bubble)
Layer 0: [B0][B1][B2][B3][B4][ ][ ][ ][ ] (Forward)
Layer 1: [ ][B0][B1][B2][B3][B4][ ][ ][ ] (Forward)
Layer 2: [ ][ ][B0][B1][B2][B3][B4][ ][ ] (Forward)
Layer 3: [ ][ ][ ][B0][B1][B2][B3][B4][ ] (Forward)
GPU利用率低,产生大量气泡
1F1B(One-Forward-One-Backward)调度:
// 1F1B调度
void one_forward_one_backward(int num_stages, int num_microbatches) {
for (int i = 0; i < num_microbatches; i++) {
// 前向传播
for (int stage = 0; stage < num_stages; stage++) {
forward(microbatch=i, stage=stage);
}
// 反向传播(逆序)
for (int stage = num_stages - 1; stage >= 0; stage--) {
backward(microbatch=i, stage=stage);
}
}
}GPipe vs PipeDream:
| 特性 | GPipe | PipeDream |
|---|---|---|
| 调度方式 | 同步 | 异步 |
| 气泡数量 | 较多 | 较少 |
| 内存需求 | 较低 | 较高(需保存多个版本) |
| 梯度一致性 | 保证 | 需特殊处理 |
5.4 内存优化策略
5.4.1 激活重计算(Activation Recomputation)
通过重新计算激活值而非存储来节省显存:
权衡:增加约20-30%的计算量,节省约50%的激活显存。
// 激活重计算实现
class RecomputedModel {
Tensor forward_with_recompute(Tensor input) {
vector<Tensor> saved_values;
// 前向传播,保存必要的检查点
for (int i = 0; i < num_layers; i++) {
if (is_checkpoint_layer(i)) {
saved_values.push_back(input);
}
input = layer[i]->forward(input);
}
// 反向传播时,重新计算非检查点层
// ...
}
};5.4.2 混合精度与梯度缩放
// 混合精度训练
class MixedPrecisionOptimizer {
void step() {
// 保存FP32权重副本
Tensor weight_fp32 = model->weight.to(torch::kFloat32);
// FP16前向/反向传播
model->weight = model->weight.to(torch::kFloat16);
auto loss = model->forward(input);
loss.backward();
// 梯度缩放防止下溢
scaler.scale(loss).backward();
// 更新FP32权重
optimizer->step(weight_fp32);
// 转换回FP16
model->weight = weight_fp32.to(torch::kFloat16);
}
};6. 长上下文优化
6.1 稀疏注意力
标准注意力的 复杂度在长序列上成为瓶颈。稀疏注意力通过只计算部分注意力来降低复杂度。
稀疏模式分类:
| 模式 | 复杂度 | 精度损失 | 适用场景 |
|---|---|---|---|
| 滑动窗口 | 局部信息丢失 | 短距离依赖 | |
| 扩张窗口 | 跳跃模式 | 稀疏模式 | |
| 全局注意力 | 需选择全局token | 关键信息汇聚 | |
| 随机注意力 | 随机性 | 近似计算 |
BigBird模式:
// BigBird稀疏注意力实现
Tensor bigbird_attention(Tensor Q, Tensor K, Tensor V,
int window_size, int num_global) {
int N = Q.size(0);
auto output = torch::zeros_like(Q);
// 1. 全局注意力(CLS token等)
auto Q_global = Q.narrow(0, 0, num_global);
output.narrow(0, 0, num_global) =
global_attention(Q_global, K, V);
// 2. 滑动窗口注意力
for (int i = num_global; i < N; i++) {
int start = max(num_global, i - window_size);
auto Q_local = Q.narrow(0, i, 1);
auto K_local = K.narrow(0, start, window_size);
auto V_local = V.narrow(0, start, window_size);
output.narrow(0, i, 1) = local_attention(Q_local, K_local, V_local);
}
// 3. 随机注意力
auto random_indices = torch::randperm(N - num_global).narrow(0, 0, num_random);
auto K_random = K.index(random_indices);
auto V_random = V.index(random_indices);
output += random_attention(Q, K_random, V_random);
return output;
}6.2 线性注意力
线性注意力7通过核函数近似将复杂度降至 :
标准注意力的Softmax:
线性注意力的分解:
其中 是特征映射函数。
Performer的核函数:
// Performer的正交随机特征
Tensor orthogonal_random_features(Tensor K, int d_prime) {
// 生成随机投影矩阵 R
// 使得 E[R] = 0, Cov[R] = I
Tensor R = torch::randn({K.size(-1), d_prime}) / sqrt(d_prime);
// 随机正交化(改善估计方差)
R = orthogonalize(R);
// 指数函数映射
Tensor K_prime = torch::exp(K) @ R;
return K_prime;
}
Tensor linear_attention(Tensor Q, Tensor K, Tensor V) {
int d_prime = d_model; // 特征维度
// 映射到高维空间
auto Q_prime = orthogonal_random_features(Q, d_prime);
auto K_prime = orthogonal_random_features(K, d_prime);
// O(Nd')的计算
Tensor KV = K_prime.t() @ V; // [d', d]
Tensor QKV = Q_prime @ KV; // [N, d]
// 归一化
Tensor Z = Q_prime @ K_prime.t().sum(-1); // [N]
return QKV / Z.unsqueeze(-1);
}6.3 KV Cache压缩
6.3.1 H2O(Heavy-Hitter Oracle)
H2O8基于信息论原则选择需要保留的KV Cache条目:
核心洞察:少数”重型打击者”(Heavy Hitter)token贡献了大部分注意力。
// H2O缓存策略
class H2OKVCache {
int max_cache_size;
vector<pair<int, float>> attention_scores; // (token_id, score)
void update(int token_id, float attention) {
attention_scores.push_back({token_id, attention});
// 保持最多max_cache_size个token
if (attention_scores.size() > max_cache_size) {
// 移除注意力分数最低的token
auto min_it = min_element(attention_scores.begin(),
attention_scores.end(),
[](auto& a, auto& b) {
return a.second < b.second;
});
attention_scores.erase(min_it);
}
}
vector<int> get_cached_tokens() {
vector<int> cached;
for (auto& [id, score] : attention_scores) {
cached.push_back(id);
}
return cached;
}
};6.3.2 StreamingLLM策略
StreamingLLM的核心观察是Attention Sink现象:
- 模型对初始token(特别是第一个token)的注意力异常高
- 这是因为模型需要一个”锚点”来汇聚信息
// StreamingLLM缓存策略
class StreamingLLMCache {
int num_sink_tokens; // 通常为4
int num_recent_tokens; // 最近的K个token
vector<int> cached_tokens;
void update(int new_token) {
cached_tokens.push_back(new_token);
// 策略:保留sink + 最近的token
if (cached_tokens.size() > num_sink_tokens + num_recent_tokens) {
// 删除中间的token
cached_tokens.erase(
cached_tokens.begin() + num_sink_tokens,
cached_tokens.end() - num_recent_tokens
);
}
}
};6.4 LongRoPE应用
LongRoPE是微软提出的革命性位置编码插值方法,可将LLM上下文窗口从4K扩展到256K甚至2M。
LongRoPE的关键技术:
- 非均匀位置缩放:不同频率维度采用不同缩放因子
- 渐进式扩展:两阶段训练实现平滑过渡
- 位置去纠缠:消除不同频率维度间的干扰
在长上下文推理中的应用:
// LongRoPE配置示例
struct LongRoPEConfig {
int original_ctx_len = 4096; // 原始上下文长度
int extended_ctx_len = 262144; // 扩展后长度(256K)
vector<float> dim_scales; // 每个维度的缩放因子
// 低频维度使用更大的缩放因子
// 高频维度使用较小的缩放因子
};7. 综合实践指南
7.1 方法选择决策树
LLM推理优化
│
↓
┌─────────────────────────────┐
│ 优化目标是什么? │
└─────────────┬───────────────┘
↓ ↓
降低延迟 提升吞吐量
↓ ↓
┌──────────────────┐ ┌──────────────────┐
│ 推测解码 + 连续 │ │ 量化 + 连续批处理 │
│ 批处理 │ │ + 流水线并行 │
└────────┬─────────┘ └────────┬─────────┘
│ │
↓ ↓
┌──────────────────┐ ┌──────────────────┐
│ EAGLE-2 (高接受率)│ │ FP8/INT8量化 │
│ 或 Medusa │ │ vLLM部署 │
└──────────────────┘ └──────────────────┘
7.2 硬件适配建议
| GPU型号 | 推荐优化方案 | 备注 |
|---|---|---|
| NVIDIA H100 | FP8量化 + PagedAttention | 原生FP8支持 |
| NVIDIA A100 | INT8量化 + FlashAttention | 成熟方案 |
| NVIDIA 4090 | INT4量化 + 推测解码 | 显存受限 |
| AMD MI300 | FP8量化 + 连续批处理 | ROCm支持 |
多卡部署建议:
| 参数量 | 推荐并行策略 |
|---|---|
| 7B | 单卡 + INT8 |
| 13B | 单卡INT4 / 2卡TP |
| 70B | 4卡TP + 连续批处理 |
| 100B+ | 8+卡 + 流水线并行 |
7.3 常见陷阱与解决方案
| 陷阱 | 症状 | 解决方案 |
|---|---|---|
| 批处理气泡过多 | GPU利用率<50% | 增大batch size;使用虚拟批处理 |
| KV Cache溢出 | OOM错误 | 启用PagedAttention;降低max_seq_len |
| 量化精度崩溃 | 输出质量下降 | 使用GPTQ/AWQ;混合精度(关键层FP16) |
| 推测解码接受率低 | 加速比<1.2 | 更换draft模型;调整temperature |
| 长上下文性能下降 | 延迟随长度线性增长 | 使用稀疏注意力;启用Ring Attention |
调试工具推荐:
// 推理性能监控
class InferenceProfiler {
public:
struct TimingStats {
float prefill_time; // Prefill阶段耗时
float decode_time; // Decode阶段耗时
float total_time; // 总耗时
int num_tokens; // 生成的token数
float throughput; // tokens/s
};
TimingStats profile(const Request& req) {
auto start = high_resolution_clock::now();
// 执行推理
auto tokens = run_inference(req);
auto end = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(end - start);
return {
.total_time = duration.count() / 1000.0f,
.num_tokens = tokens.size(),
.throughput = tokens.size() / (duration.count() / 1000000.0f)
};
}
};参考文献
Footnotes
-
Yu et al. (2023). “Efficient Memory Management for Large Language Model Serving with PagedAttention”. SOSP 2023. ↩
-
Yu et al. (2022). “Orca: A Distributed Serving System for Transformer-Based Generative Models”. OSDI 2022. ↩
-
Xiao et al. (2023). “Efficient Streaming Language Models with Attention Sinks”. arXiv preprint. ↩
-
Frantar et al. (2022). “GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers”. ICLR 2023. ↩
-
Esser et al. (2019). “Learned Step Size Quantization”. ICLR 2020. ↩
-
Li et al. (2023). “Ring Attention with Blockwise Transformers for Near-Infinite Context”. ICLR 2024. ↩
-
Katharopoulos et al. (2020). “Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention”. ICML 2020. ↩
-
Zhang et al. (2023). “H2O: Heavy-Hitter Oracle for Dynamically Constructing Attention Mechanism”. CVPR 2024. ↩