PagedAttention 对比传统 KV 缓存:vLLM 如何重塑 LLM 推理的 GPU 显存管理

作者
  • avatar
    姓名
    Nino
    职业
    Senior Tech Editor

在大语言模型(LLM)推理的过程中,生成的每一个 Token 都在悄无声息地消耗 GPU 显存。在高性能服务领域,显存往往是比计算能力更紧缺的瓶颈。传统的 Key-Value (KV) 缓存实现方式存在严重的资源浪费——大量显存被预分配却未被使用,导致系统无法承载更高的并发请求。

vLLM 及其核心创新 PagedAttention 的出现,彻底改变了 AI 基础设施的格局。它借鉴了操作系统中存在了数法十年的“分页”思想,解决了显存碎片化问题。在 n1n.ai,我们看到开发者在追求低延迟和高吞吐量之间不断权衡;深入理解 PagedAttention 是优化 AI 部署的第一步。本文将深度解析其工作原理,以及它为何能实现比传统方案高出 24 倍的吞吐量。

什么是 KV 缓存?为什么它至关重要?

要理解解决方案,首先要理解问题本身。像 Llama 3DeepSeek-V3 这样的模型是自回归的。这意味着为了生成第 NN 个 Token,模型需要之前所有 N1N-1 个 Token 的上下文信息。

如果没有缓存,模型在每一步生成时都必须重新计算前面所有 Token 的 Key 和 Value 张量。这将导致 O(n2)O(n^2) 的计算复杂度,使得实时生成变得不可能。为了解决这个问题,我们使用 KV 缓存:我们将之前计算好的 Key 和 Value 张量存储在 GPU 显存(VRAM)中。后续步骤只需计算新 Token 的 KV 值,并与缓存的值进行注意力计算(Attention)。

然而,这个缓存的体积非常庞大。以 Llama-2 7B 模型为例,我们可以粗略计算其显存占用:

# KV 缓存大小粗略估算
num_layers = 32
num_heads = 32
head_dim = 128
seq_len = 2048
batch_size = 8
dtype_bytes = 2 # float16 (半精度)

kv_cache_bytes = (
    2 * num_layers * num_heads * head_dim * seq_len * batch_size * dtype_bytes
)
print(f"KV 缓存占用: {kv_cache_bytes / 1e9:.2f} GB")
# 输出: KV 缓存占用: 8.59 GB

仅仅是 8 个并发用户在 2k 上下文长度下,就吃掉了 8.6 GB 的显存。如果你正在使用像 n1n.ai 这样的顶级 API 聚合器,这些底层优化已经在后台为你处理好了,但对于自建基础设施的团队来说,这正是挑战的开始。

痛点:传统 KV 缓存与显存碎片化

在传统的框架(如 HuggingFace Transformers)中,KV 缓存是作为连续内存块分配的。如果你设置 max_seq_len 为 2048,系统会在请求开始的那一刻,为这 2048 个 Token 预留空间。

这导致了三种类型的碎片:

  1. 内部碎片 (Internal Fragmentation):如果用户的请求只生成了 50 个 Token,但你预留了 2048 个位置,剩下的 1998 个位置就被浪费了。
  2. 预留碎片 (Reservation Fragmentation):即使模型最终可能会用到这些空间,但在生成的早期阶段,这些显存一直处于闲置状态。
  3. 外部碎片 (External Fragmentation):由于分配是连续的且长度不一,GPU 显存会变成像“瑞士奶酪”一样,充斥着细小的、无法利用的空隙。

在实际操作中,这意味着你的 GPU 可能显示已使用 80% 的显存,但其中只有 30-40% 真正存储了有用的数据。这种低效直接限制了 Batch Size(批处理大小),导致排队时间变长,成本飙升。

灵感来源:操作系统虚拟内存

vLLM 团队(Kwon 等人,2023)意识到,2023 年的 LLM 显存管理方式非常类似于 20 世纪 60 年代的计算机内存管理。解决方案也如出一辙:分页 (Paging)

在操作系统中,物理内存被划分为固定大小的“帧”(Frames),进程的虚拟地址空间被划分为“页”(Pages)。操作系统维护一个页表来将虚拟页映射到物理帧。这些帧在物理上不需要是连续的。

vLLM 将这一概念引入了 KV 缓存。KV 缓存不再是一个巨大的连续块,而是被划分为一个个 Block(块)(例如,每块存储 16 个 Token 的 KV 值)。

PagedAttention 的工作原理

PagedAttention 允许 KV 张量存储在非连续的物理显存中。其算法逻辑如下:

  1. 逻辑块:将一个序列的 KV 缓存划分为逻辑块。
  2. 块表 (Block Table):建立映射关系,记录每个逻辑块在 GPU 物理显存中的实际位置。
  3. 按需分配:当模型生成 Token 时,只有当当前物理块存满后,才会分配一个新的物理块。
概念操作系统虚拟内存PagedAttention
内存单元页 (Page)KV 块 (Block)
地址空间虚拟地址Token 索引
映射方式页表 (Page Table)块表 (Block Table)
存储介质物理内存 (RAM)GPU 显存 (VRAM)

实现逻辑代码示例

以下是一个简化的 PagedKVManager 概念模型,展示了如何不依赖连续空间进行分配:

from dataclasses import dataclass, field
from typing import List, Optional

BLOCK_SIZE = 16 # 每个块包含 16 个 token

@dataclass
class KVBlock:
    block_id: int
    token_count: int = 0

class PagedKVManager:
    def __init__(self, total_blocks: int):
        # 初始化空闲块池
        self.free_blocks = list(range(total_blocks))
        self.blocks = {i: KVBlock(block_id=i) for i in range(total_blocks)}

    def allocate_block(self) -> Optional[int]:
        if not self.free_blocks: return None
        return self.free_blocks.pop()

    def append_token(self, seq_block_table: List[int]) -> bool:
        # 如果最后一个块已满或尚无块,则分配新块
        if not seq_block_table or self.blocks[seq_block_table[-1]].token_count == BLOCK_SIZE:
            new_id = self.allocate_block()
            if new_id is None: return False # 显存不足 (OOM)
            seq_block_table.append(new_id)

        self.blocks[seq_block_table[-1]].token_count += 1
        return True

吞吐量提升:数据说明一切

通过消除几乎所有的显存碎片,vLLM 可以在同一个 Batch 中塞进更多的序列。根据原始论文的基准测试,vLLM 实现了:

  • 与 HuggingFace Transformers 相比,吞吐量提升 2-4 倍
  • 在长序列和高并发场景下,吞吐量提升高达 24 倍

当你使用 n1n.ai 时,你就在间接享受这些高效架构带来的红利。无论你是在调用 Claude 3.5 Sonnet 还是 OpenAI o3,底层的推理引擎往往都采用了类似的内存管理机制,以确保你的 API 请求能以极速响应。

隐藏的“超能力”:前缀缓存 (Prefix Caching)

PagedAttention 还实现了一个巨大的优化:写时复制 (Copy-on-Write)。如果你有 100 个请求都在使用同一个冗长的系统提示词(例如 RAG 系统中的长上下文),PagedAttention 允许这 100 个请求共享同一组物理显存块。

只有当某个请求开始生成不同的 Token 时,才会进行内存复制。这对于现代 AI 智能体(Agents)来说至关重要,能将公共前缀的显存占用降至近乎为零。

权衡与局限性

虽然 PagedAttention 是革命性的,但它也有其成本:

  1. 内核复杂性:标准的 FlashAttention 算子不支持分页内存。vLLM 团队必须编写自定义的 CUDA 内核,以便在非连续的显存块上执行注意力计算。
  2. 管理开销:在 CPU 端维护块表会带来微小的计算开销,但在 GPU 的巨大收益面前,这通常可以忽略不计。
  3. 计算密集型 vs 显存密集型:如果你的模型已经是计算饱和的(例如在小 Batch 下运行超大模型),PagedAttention 的边际收益会递减。

总结与核心要点

  • 传统 KV 缓存 极其浪费,因为它要求连续的预分配。
  • PagedAttention 将 GPU 显存视为虚拟内存,将其划分为灵活的块。
  • 显存利用率 从约 60% 提升至 95% 以上,从而支持更大的 Batch Size。
  • 吞吐量 显著提升,降低了每个 Token 的推理成本。

对于希望避开这些底层优化难题的开发者,n1n.ai 提供了一个统一的 API 接口,聚合了市场上最快、最高效的模型。

立即在 n1n.ai 获取免费 API 密钥。