字节跳动 AI Infra (1)
Q: KV Cache的原理和优化方法?
原理——为什么需要KV Cache:
Transformer自回归生成第t个token时,attention计算需要:
1 | Q_t × K_{0:t}^T → Score → Softmax → × V_{0:t} → Output_t |
没有KV Cache时,每步都要重新计算所有历史token的K和V(因为Q投影依赖当前token,但K/V投影只依赖各自的token embedding——不随新token变化)。
有KV Cache时:
1 | Step t: |
效果:每步从计算所有token的KV(O(t)次投影)变为只计算1个token的KV(O(1)投影)。总计算量从O(n^3)降为O(n^2)。
显存占用公式:
1 | 单请求KV Cache = 2(K+V) × num_layers × num_kv_heads × head_dim × seq_len × dtype_bytes |
示例(LLaMA-70B, GQA 8 KV heads, 128 head_dim, 80 layers, FP16, 4K seq):
1 | = 2 × 80 × 8 × 128 × 4096 × 2 bytes = 13.4 GB/请求 |
这就是为什么KV Cache优化如此关键——80GB的A100在加载模型参数(35GB for INT4 70B)后,KV Cache只能支持约3个4K长度的并发请求。
优化方法详解:
1. PagedAttention(内存管理革命):
- 灵感:OS虚拟内存分页——逻辑连续不要求物理连续。
- 实现:KV Cache按fixed block(如16 tokens/block)分配,block table做逻辑→物理映射。
- 收益:显存利用率从20-40%→接近100%,同样硬件下并发提升3-5x。
- 额外能力:Copy-on-Write支持beam search/parallel sampling不复制KV。
2. GQA/MQA(架构级减少KV量):
- MHA:Q和KV头数相同(如64Q/64KV)→ KV Cache最大。
- GQA:多个Q头共享一组KV头(如64Q/8KV)→ KV Cache减少8x。
- MQA:所有Q头共享1组KV(如64Q/1KV)→ KV Cache减少64x(但精度损失较大)。
- 现状:LLaMA-3/Qwen2/Mistral均使用GQA,是精度和效率的最佳平衡。
3. KV Cache量化:
- KV值的数值范围有限(通常[-3, +3]),INT8量化误差极小。
- Per-head量化:每个attention head独立scale factor。
- 效果:KV显存减半,perplexity增加<0.1。
- 实现:存储时量化(在KV生成后立即量化存入cache),使用时反量化。
4. Prefix Caching:
- 场景:1000个请求共享同一个2K token的system prompt。
- 无缓存:每请求都计算2K token的KV → 2000K token计算量。
- 有缓存:只计算一次2K KV,后续999个请求直接引用 → 省99.9%前缀计算。
- 实现:对prefix内容hash → 命中则复用已有physical blocks → miss则正常计算。
5. 稀疏KV Cache:
- H2O策略:保留累积注意力权重最高的K个token + 最近W个token的KV。
- 典型配置:K=128, W=256,总KV只保留384个token(vs 原始4K),压缩10x。
- 风险:丢弃的token如果被后续重要query需要,质量下降。
6. Offloading到CPU:
- 不活跃请求(用户思考中)的KV Cache卸载到CPU DRAM。
- 重新激活时通过PCIe加载回GPU(Gen4: 32GB/s,4K KV约50ms加载)。
- 适合交互式长session场景。
Q: PagedAttention的原理?为什么效果好?
原理——借鉴OS虚拟内存分页:
传统KV Cache管理的问题:
1 | 请求A (最终生成100 tokens): 预分配 max_seq_len(2048) tokens的连续显存 |
PagedAttention的解决方案:
1 | 物理block池: [Block 0] [Block 1] [Block 2] ... [Block N](每block存16 tokens的KV) |
Attention计算时的寻址:
1 | // 传统(连续内存) |
效果好的深层原因:
| 优势 | 机制 | 量化收益 |
|---|---|---|
| 消除碎片 | 按block按需分配,无内部碎片 | 利用率20-40% → 95%+ |
| 消除过度预留 | 不需要预留max_seq空间 | 短请求不浪费空间 |
| 灵活回收 | 请求完成即释放其所有block | 实时释放,无需等batch |
| 共享前缀 | 多请求指向同一物理block(COW) | beam search显存降N倍 |
| 动态增长 | 序列增长时按需追加block | 无需预知最终长度 |
性能数据:
- vLLM论文中:同样硬件,PagedAttention使吞吐量相比HuggingFace提升14-24x(主要来自更高并发)。
- 显存利用率对比:传统预分配20-40%,PagedAttention接近100%(仅~4%开销用于block table元数据)。
Copy-on-Write应用:
- Beam Search:K个beam共享前缀的block,只有当某beam修改时才复制。
- Parallel Sampling:同一prompt多次采样,prompt的KV block全部共享。
Q: FlashAttention的原理?
核心问题——标准Attention的内存瓶颈:
1 | 标准实现: |
当N=4K, d=128时:
- 计算量:O(N^2 × d) ≈ 4B FLOPs
- 标准实现HBM I/O:O(N^2) ≈ 32MB × 多次读写
- A100 HBM带宽2TB/s → I/O时间成为瓶颈(比纯计算时间更长)
FlashAttention的解决方案——Tiling + Online Softmax:
1 | 将Q分为Br块, K/V分为Bc块 (每块大小适配SRAM) |
关键创新——Online Softmax:
标准softmax需要两遍遍历(先求全局max,再算exp/sum)。FlashAttention使用在线算法:
- 逐块计算,每块更新运行中的max和sum统计量。
- 对已计算的部分输出用修正因子rescale。
- 数学上严格等价于标准softmax(证明可见论文Theorem 1)。
收益分析:
| 指标 | 标准Attention | FlashAttention |
|---|---|---|
| HBM I/O | O(N^2) | O(N^2 / M)(M=SRAM大小) |
| 额外显存 | O(N^2)(S和P矩阵) | O(N)(仅统计量m,l) |
| 实际加速 | baseline | 2-4x(A100, seq=2K) |
| 长序列 | OOM(>16K) | 可处理128K+ |
为什么快(虽然FLOPs不变甚至略多):
- FLOPs确实不变(还多了rescale计算),但I/O从O(N^2)降为O(N^2/M)。
- A100的计算-带宽比极高(312T/2T=156),I/O减少后计算管线被充分利用。
- 本质:将Attention从memory-bound变为(接近)compute-bound。
Q: vLLM的核心技术和推理加速策略?
vLLM是加州大学伯克利分校开发的高性能LLM推理引擎,其核心设计围绕”最大化GPU利用率”:
技术栈架构:
1 | 请求 → Scheduler(调度器) |
核心技术详解:
1. PagedAttention:见前述——KV Cache分页管理,利用率接近100%。
2. Continuous Batching(迭代级调度):
1 | Step t: [Req A (decode)] [Req B (decode)] [Req C (prefill)] [空] |
- 每个decode step后检查完成/新到达的请求。
- 不等待整个batch完成,逐请求进出。
- 效果:vs 静态batch吞吐提升2-3x。
3. Prefix Caching(APC):
- 自动检测请求间的共同前缀(通过token序列hash)。
- 命中时直接引用已有KV Cache block,跳过prefill计算。
- 对大量使用相同system prompt的场景(如企业ChatBot),可减少50-90%的prefill计算。
4. Chunked Prefill:
- 长prompt(如32K)的prefill分为多个chunk(如512 tokens/chunk)。
- 每个chunk之间穿插处理decode请求。
- 解决:长prefill不会阻塞所有decode请求,降低P99 TPOT。
- 配置:
--enable-chunked-prefill --max-num-batched-tokens 2048。
5. Speculative Decoding集成:
1 | from vllm import LLM |
- 内置draft model集成,自动处理验证和回退逻辑。
- 对batch中的每个请求独立做推测-验证。
6. 多模型格式支持:
- 原生FP16/BF16、GPTQ(4bit)、AWQ(4bit)、FP8(H100)。
- 加载量化模型零额外步骤(自动识别格式并调用对应kernel)。
- Marlin kernel加速W4A16(比通用kernel快2x)。
7. 多卡推理策略:
- Tensor Parallel:
--tensor-parallel-size 4(单请求低延迟)。 - Pipeline Parallel:
--pipeline-parallel-size 2(适合跨节点)。 - 二者可组合使用。
性能对比(A100-80GB, LLaMA-70B, ShareGPT数据集):
- vLLM vs HuggingFace: 14-24x吞吐提升。
- vLLM vs TGI: 2-3x吞吐提升。
- 主要来源:PagedAttention的高利用率 + Continuous Batching的高调度效率。
Q: Prefix Cache的作用?
场景和动机:
大量LLM应用中,多个请求共享相同的前缀文本:
- System Prompt:所有用户请求前都有相同的系统指令(如”你是一个有帮助的助手…”,可长达数千token)。
- Few-shot示例:相同的示例模板+不同的用户查询。
- 文档问答:同一文档+不同问题。
1 | 请求1: [System Prompt(2K tokens)] + [用户问题A(100 tokens)] |
无Prefix Cache:每个请求都要做2K token的prefill计算 → 1000次×2K = 200万token的compute(巨大浪费)。
有Prefix Cache:只计算一次2K前缀的KV Cache,后续999个请求直接引用。
实现机制(vLLM APC):
1 | # 1. 对prefix内容做hash |
收益量化:
| 指标 | 无Prefix Cache | 有Prefix Cache | 提升 |
|---|---|---|---|
| TTFT(首token延迟) | 200ms(含2K prefill) | 20ms(仅100 token) | 10x |
| Prefill计算量 | 100% | 5%(只算用户部分) | 20x |
| 显存占用 | N × prefix_kv | 1 × prefix_kv | Nx节省 |
注意事项:
- 缓存一致性:如果prefix内容变化,hash不同会自动miss。
- 缓存淘汰:LRU策略,不常用的prefix被淘汰释放显存。
- 适用条件:大量请求共享长前缀时收益最大;如果每个请求前缀都不同,则无收益。
- vLLM配置:
--enable-prefix-caching即可启用。
Q: AI应用(Agent)的构建大致包含哪些模块?
AI Agent是一个能自主规划、使用工具、持续交互的智能系统,其架构包含以下核心模块:
整体架构:
1 | ┌─────────── User Interface ───────────┐ |
各模块详解:
1. LLM推理引擎(核心大脑):
- 负责理解、推理和生成。
- 要求:低延迟(用户等待)、高稳定性(不崩溃)、支持结构化输出(JSON/工具调用格式)。
- 部署:自建(vLLM) 或 API调用(OpenAI/Claude)。
- 关键:prompt工程决定agent行为质量。
2. 工具系统(Tool Use):
- 定义:向LLM描述可用工具的名称、参数、用途。
- 调用:LLM决定用哪个工具+参数→执行→将结果注入上下文。
- 常见工具:代码执行、网络搜索、数据库查询、API调用、文件操作。
- 实现要点:工具描述要清晰(减少调用错误)、超时处理、结果截断(避免context过长)。
3. 记忆系统(Memory):
- 短期记忆:当前对话上下文(conversation history),受context window限制。
- 长期记忆:向量数据库存储历史交互/知识,需要时检索注入。
- 工作记忆:当前任务执行中的中间状态(如多步计划的进度)。
- 挑战:context window有限时如何压缩/选择最相关的历史信息。
4. 规划模块(Planning):
- ReAct:Reasoning + Acting交替——先思考再行动,观察结果再思考。
- Plan-and-Execute:先生成完整计划,再逐步执行(适合复杂任务)。
- Tree of Thought:多路径探索,选最优路径。
- 实现:通过prompt引导LLM输出结构化的计划步骤。
5. RAG检索增强:
- 将外部知识库切分为chunk,用embedding model编码存入向量数据库。
- 用户query时检索top-K相关chunk注入prompt。
- 解决LLM知识截止和幻觉问题。
- 关键参数:chunk size(512-1024 token)、overlap(50-100 token)、top-K(3-10)。
6. 安全护栏(Guardrails):
- 输入过滤:检测有害/越权请求(prompt injection防御)。
- 输出过滤:检测并拦截有害/不准确的回复。
- 行为约束:限制工具调用范围(如不允许执行rm -rf)。
- 实现:规则引擎 + 分类模型 + LLM自检。
7. 编排层(Orchestration):
- 控制多轮交互流程(何时调用LLM、何时执行工具、何时返回用户)。
- 错误处理和重试逻辑(工具调用失败时的graceful degradation)。
- 并发控制(多个独立子任务并行执行)。
- 框架:LangChain/LlamaIndex/AutoGen/CrewAI。
对AI Infra的要求:
- LLM推理:低延迟(<2s TTFT)、高可用(99.9%)、支持长context。
- 向量检索:ms级延迟、百万级向量库。
- 工具执行:沙箱隔离(代码执行安全)、超时控制。
- 可观测性:trace每步决策和执行结果,便于debug和优化。
Q: 手撕:环上n步回到原点的走法数?
(编程题)