← 返回首页

LLM 推理引擎优化:KV Cache 与 Flash Attention

机器学习系统 2026-09-10 · 阅读约 14 分钟

大语言模型(LLM)推理的核心瓶颈在于显存带宽和计算效率。本文深入剖析 KV Cache 的内存管理策略与 Flash Attention 的 IO-aware 算子融合技术,展示如何将推理吞吐量提升 3-5 倍。

一、自回归推理的内存瓶颈

LLM 的自回归推理每次生成一个 token,需要计算所有历史 token 的 Key 和 Value。如果不缓存,每次都要重新计算整个序列,计算量呈 O(n²) 增长。KV Cache 将已计算的 K、V 矩阵缓存起来,每次只需计算新 token 的注意力。

class KVCache:
    def __init__(self, num_layers, num_heads, head_dim, max_seq_len):
        self.cache = [{
            'k': torch.zeros(max_seq_len, num_heads, head_dim),
            'v': torch.zeros(max_seq_len, num_heads, head_dim),
        } for _ in range(num_layers)]
        self.seq_len = 0

    def update(self, layer_idx, new_k, new_v):
        self.cache[layer_idx]['k'][self.seq_len] = new_k
        self.cache[layer_idx]['v'][self.seq_len] = new_v

    def get(self, layer_idx):
        return (self.cache[layer_idx]['k'][:self.seq_len],
                self.cache[layer_idx]['v'][:self.seq_len])

二、PagedAttention:分页式 KV Cache 管理

传统 KV Cache 为每个序列预分配连续的最大长度内存,造成严重浪费。PagedAttention(vLLM 采用)将 KV Cache 划分为固定大小的 Block,按需分配,类似操作系统的虚拟内存分页机制,显存利用率从 20-40% 提升到 96%。

class PagedKVCache:
    def __init__(self, block_size=16):
        self.block_size = block_size
        self.blocks = {}  # block_id -> (k_block, v_block)
        self.block_table = {}  # seq_id -> [block_ids]

    def allocate_block(self):
        block_id = len(self.blocks)
        self.blocks[block_id] = (
            torch.zeros(self.block_size, num_heads, head_dim),
            torch.zeros(self.block_size, num_heads, head_dim)
        )
        return block_id

    def append_token(self, seq_id, layer_idx, k, v):
        if seq_id not in self.block_table:
            self.block_table[seq_id] = []

        table = self.block_table[seq_id]
        if not table or self._block_full(table[-1]):
            table.append(self.allocate_block())

        block = self.blocks[table[-1]]
        offset = self._next_offset(table[-1])
        block[0][offset] = k
        block[1][offset] = v

三、Flash Attention:IO-Aware 精确注意力

标准 Attention 需要将完整的 QK^T 矩阵写入 HBM(显存),再读取进行 Softmax。Flash Attention 通过分块(Tiling)和重计算(Recomputation)技术,将中间结果保持在 SRAM 中,减少 HBM 读写次数,实现 2-4 倍加速。

def flash_attention(Q, K, V, block_size=128):
    B, H, N, d = Q.shape
    O = torch.zeros_like(Q)
    L = torch.zeros(B, H, N, 1)
    m = torch.full((B, H, N, 1), -float('inf'))

    for i in range(0, N, block_size):
        Qi = Q[:, :, i:i+block_size]
        Oi = torch.zeros(B, H, block_size, d)
        Li = torch.zeros(B, H, block_size, 1)
        mi = torch.full((B, H, block_size, 1), -float('inf'))

        for j in range(0, N, block_size):
            Kj = K[:, :, j:j+block_size]
            Vj = V[:, :, j:j+block_size]

            Sij = torch.matmul(Qi, Kj.transpose(-2, -1)) / math.sqrt(d)
            mij_new = torch.max(mi, Sij.max(dim=-1, keepdim=True).values)

            Pij = torch.exp(Sij - mij_new)
            Lij = torch.exp(mi - mij_new) * Li + Pij.sum(dim=-1, keepdim=True)

            Oi = torch.exp(mi - mij_new) * Oi + torch.matmul(Pij, Vj)
            mi, Li = mij_new, Lij

        O[:, :, i:i+block_size] = Oi / Li
        L[:, :, i:i+block_size] = Li + torch.log(Li)

    return O

四、INT4/INT8 量化技术

通过将 FP16 权重和激活值量化为 INT4/INT8,可将显存占用降低 4-8 倍。GPTQ 和 AWQ 是两种主流的权重量化方法,前者使用 OBQ 逐列量化,后者通过激活感知的缩放因子保护重要通道。

def quantize_int4(weight_fp16, group_size=128):
    """GPTQ-style 分组量化"""
    num_groups = weight_fp16.shape[0] // group_size
    scales = torch.zeros(num_groups, weight_fp16.shape[1])
    zeros = torch.zeros(num_groups, weight_fp16.shape[1], dtype=torch.int32)
    weight_int4 = torch.zeros_like(weight_fp16, dtype=torch.int8)

    for g in range(num_groups):
        group = weight_fp16[g*group_size:(g+1)*group_size]
        w_max = group.max()
        w_min = group.min()
        scale = (w_max - w_min) / 15.0
        scales[g] = scale
        zeros[g] = torch.round(-w_min / scale).int()
        weight_int4[g*group_size:(g+1)*group_size] = \
            torch.clamp(torch.round(group / scale) + zeros[g], 0, 15)

    return weight_int4, scales, zeros

五、Continuous Batching

传统静态批处理要求所有请求等长,造成 GPU 空闲。Continuous Batching 允许不同长度的请求动态加入/离开批次,在每次迭代中重新组批,将 GPU 利用率从 30% 提升到 80% 以上。

LLM 推理优化是一个快速发展的领域,掌握 KV Cache、Flash Attention 和量化技术是构建高性能推理服务的基础。

← 返回首页