LLM推理加速方法综合指南(2025)

1. 概述:LLM推理的挑战

1.1 自回归生成的计算瓶颈

大语言模型的推理过程本质上是自回归生成(Autoregressive Generation)。在每个解码步骤中:

  1. 将已生成的 token 序列作为输入
  2. 执行完整的注意力计算
  3. 输出下一个 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-1EAGLE-2
验证策略贪婪验证树形验证
Draft长度固定4-8动态调整(4-16)
剪枝策略基于置信度的剪枝
多候选单链多分支树

EAGLE-2的树形注意力

其中 来自不同的draft分支。

2.3 Medusa多Token预测

Medusa通过在主模型上添加多个解码头来实现并行多Token预测。详见Medusa多Token预测

与EAGLE的对比

特性EAGLEMedusa
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通过以下方式解决

  1. 块级管理:固定大小的块减少内部碎片
  2. 动态分配:按需分配新块
  3. 块池(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量化面临更严重的精度损失:

量化级别理论压缩比精度损失风险适用场景
INT84x通用推理
INT48x中等显存受限场景
INT216x实验性应用
INT132x极高仅概念验证

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 bitsM bits范围精度
E4M343±448较高
E5M252±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 对比

特性FP8INT8
动态范围自动适应需手动设置
精度较高中等
硬件支持H100+/MI300+广泛支持
推理速度更快
实现复杂度较低中等

4.3 量化感知训练

量化感知训练(Quantization-Aware Training, QAT)通过在训练过程中模拟量化来减少精度损失。

QAT vs PTQ(训练后量化)

特性QATPTQ
训练成本高(需完整训练)低(无额外训练)
精度更高略低
适用场景极致性能要求快速部署
模型大小无限制受限于校准数据

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 或 INT81.5-2x< 1%
边缘部署INT4 (GPTQ)3-4x2-5%
极致压缩INT4 + 剪枝5-8x5-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

特性GPipePipeDream
调度方式同步异步
气泡数量较多较少
内存需求较低较高(需保存多个版本)
梯度一致性保证需特殊处理

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的关键技术

  1. 非均匀位置缩放:不同频率维度采用不同缩放因子
  2. 渐进式扩展:两阶段训练实现平滑过渡
  3. 位置去纠缠:消除不同频率维度间的干扰

在长上下文推理中的应用

// 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 H100FP8量化 + PagedAttention原生FP8支持
NVIDIA A100INT8量化 + FlashAttention成熟方案
NVIDIA 4090INT4量化 + 推测解码显存受限
AMD MI300FP8量化 + 连续批处理ROCm支持

多卡部署建议

参数量推荐并行策略
7B单卡 + INT8
13B单卡INT4 / 2卡TP
70B4卡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

  1. Yu et al. (2023). “Efficient Memory Management for Large Language Model Serving with PagedAttention”. SOSP 2023.

  2. Yu et al. (2022). “Orca: A Distributed Serving System for Transformer-Based Generative Models”. OSDI 2022.

  3. Xiao et al. (2023). “Efficient Streaming Language Models with Attention Sinks”. arXiv preprint.

  4. Frantar et al. (2022). “GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers”. ICLR 2023.

  5. Esser et al. (2019). “Learned Step Size Quantization”. ICLR 2020.

  6. Li et al. (2023). “Ring Attention with Blockwise Transformers for Near-Infinite Context”. ICLR 2024.

  7. Katharopoulos et al. (2020). “Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention”. ICML 2020.

  8. Zhang et al. (2023). “H2O: Heavy-Hitter Oracle for Dynamically Constructing Attention Mechanism”. CVPR 2024.