27届大模型面试准备(五十九):大模型推理引擎内核深度剖析——PagedAttention、调度器与显存管理

27届大模型面试准备(五十九):大模型推理引擎内核深度剖析------PagedAttention、调度器与显存管理

引言:与上一篇、系列的关系

前面(二十五)讲推理服务化与投机解码、(五十三)讲多模态服务化、(五十四)讲成本与吞吐调优,都偏「架构与成本」。本篇往内核里再钻一层:一个推理引擎(vLLM、TensorRT-LLM、SGLang 之类)到底怎么把一次请求变成显存里的 KV Cache、怎么调度 token、怎么不浪费显存。这是面试「部署优化」这条线的硬核题,也是华为多模态 LLM 岗现场写代码/画架构高频考点。

复制代码
              推理引擎内核四大件
+--------------------------------------------------+
|  调度器 Scheduler                                |
|  waiting / running / finished 状态机             |
|  continuous batching (token级, 非request级)      |
+--------------------------------------------------+
                      | 下发
+--------------------------------------------------+
|  显存管理 KV Cache Manager                        |
|  PagedAttention: block 分页, 类似虚拟内存         |
|  预占/抢占(swap or recompute)                     |
+--------------------------------------------------+
                      | 计算
+--------------------------------------------------+
|  算子层 Operators                                 |
|  FlashAttention / fused kernel / CUDA Graph       |
|  量化算子(AWQ/GPTQ dequant)                       |
+--------------------------------------------------+
                      | 加速
+--------------------------------------------------+
|  投机解码 / 前缀缓存 / 并行 verify                |
+--------------------------------------------------+

第一节 推理的两阶段:prefill 与 decode 必须分开想

一次生成请求在引擎里分两段:

  • Prefill(预填充):把整段 prompt 并行喂进模型,一次性算出所有 prompt token 的 KV Cache,并产出第一个新 token。它是计算密集(compute-bound),能充分用满 GPU 矩阵算力。

  • Decode(解码):自回归逐个 token 生成,每步只算一个新 token 的 KV,但要读全部历史 KV Cache。它是访存密集(memory-bound),瓶颈在显存带宽而非算力。

    prompt: [t1 t2 t3 t4] -> Prefill 并行 -> KV(t1..t4) + 首token
    然后循环:
    Decode step: 读 KV(t1..t4) + 新KV(t5) -> 预测 t6
    Decode step: 读 KV(t1..t5) + 新KV(t6) -> 预测 t7
    ... 每步仅 1 个新 token, 但读全部历史 KV

工程含义:prefill 要大 batch、高并行;decode 要降访存、用投机解码/量化。把两者混在一起调度会互相拖累,所以很多引擎做 prefill/decode 分离(PD 分离,见五十四),把两段放到不同实例。

第二节 PagedAttention:给 KV Cache 做「虚拟内存」

朴素实现里,每个请求的 KV Cache 是预分配的一整块连续显存,长度按 max_length 算。问题:请求实际长度参差,短请求浪费大量预留显存;不同请求长度不一,连续分配产生外部碎片。PagedAttention 借鉴操作系统分页:

  • 把 KV Cache 切成固定大小的 block(如每 block 存 16 个 token 的 KV)。
  • 每个请求维护一张 block table,逻辑上连续的 token 映射到物理上可能离散的 block。
  • 生成时按需分配新 block,不再预留整段;请求间可共享相同前缀的 block(前缀缓存)。
python 复制代码
# PagedAttention 的 block table(概念骨架)
class Sequence:
    def __init__(self, block_size=16):
        self.block_size = block_size
        self.block_table = []          # 逻辑块 -> 物理块号
        self.tokens = []
    def append(self, tok, free_blocks):
        if len(self.tokens) % self.block_size == 0:
            self.block_table.append(free_blocks.pop())  # 按需分配物理块
        self.tokens.append(tok)

# 注意力计算时分块取 KV:attention(query, KV[block_table[b]])

收益:显存利用率从 ~20% 提到 ~90%+,同等显存能并发更多请求,吞吐翻数倍。这也是 vLLM 之所以快的核心。面试能画出「逻辑块→block table→物理块」三层映射,已基本拿分。

第三节 连续批处理:token 级调度,不等整批

传统 static batching:攒够 N 个请求一起跑,哪个先结束也得等最慢的,GPU 常空转。连续批处理(continuous batching) 在 token 级调度:每生成一个 token 步,就把刚结束的请求换出、把等待队列的新请求换入。

复制代码
时间步 ->  Batch 组成(动态变化)
step1: [A,B,C]        都在 decode
step2: [A,B,C]        C 结束, 换入 D
step3: [A,B,D]        B 结束, 换入 E
step4: [A,D,E]        ...
=> GPU 几乎不空转, 吞吐远高于 static

调度器状态机:

python 复制代码
class Scheduler:
    def step(self):
        self._admit()                  # waiting 按预算换入 running
        out = self._run_batch(self.running)   # token级batch
        for seq in self.running:
            if seq.finished:
                self._free(seq); self.finished.append(seq)

要点:连续批处理让「短请求不被长请求拖累」,P99 延迟更稳。它和 PagedAttention 是绝配------换入新请求只需分配 block,无需连续大块。

第四节 显存预算与抢占

引擎要维护一个显存预算 :KV Cache 池大小 = 总显存 − 模型权重 − 激活/临时量。当 waiting 队列请求需要的新 block 超出剩余预算,就要**抢占(preemption)**正在跑的某些序列:

  • Swap(换出):把被抢占序列的 KV block 搬到 CPU 内存,恢复时再搬回。不丢计算,但搬移有开销。
  • Recompute(重算):直接丢弃其 KV,恢复时重新 prefill。无搬移开销,但浪费已算的算力。
python 复制代码
def preempt(seq, mode, kv_pool, cpu_pool):
    if mode == "swap":
        cpu_pool[seq.id] = kv_pool.evict(seq.block_table)
    else:
        kv_pool.free(seq.block_table); seq.block_table = []
        seq.reset_to_prefill()

工程取舍:被抢占序列短 → recompute 更划算;长 → swap 更划算。引擎通常按序列长度自动选。

第五节 算子与 kernel:把每一拍都榨干

  • FlashAttention:把 QK^T、softmax、PV 融合进一个 CUDA kernel,不把巨大的中间注意力矩阵(N×N)写回显存,改在片上(SRAM)算。访存从 O(N²) 降到接近 O(N),既快又省显存。

  • Fused kernel:layernorm、激活、残差合并,减少 kernel 启动与中间读写。

  • CUDA Graph:把多步 kernel 录成一张图一次性回放,消除 Python/驱动调度开销,对 decode 这种小 batch 高频步收益大。

  • 量化算子:AWQ/GPTQ 权重以 INT4 存储,计算时按需 dequant 到 FP16/BF16 再乘,显存和带宽都省。

    无融合: Q,K,V Proj -> 写显存 -> Softmax -> 写显存 -> AttnOut -> 写显存
    Flash: Q,K,V Proj -> [片上: attn 全流程] -> 写一次显存

面试追问「FlashAttention 为什么省显存」------答:传统注意力要把 N×N 的 softmax 分数矩阵 materialize 到 HBM,Flash 用 tiling 在 SRAM 内分块算、只写最终 O(N) 的输出,显存从平方级降到线性。

第六节 投机解码与前缀缓存的内核实现

(二十五)讲过投机解码思想:小 draft 模型先猜 k 个 token,大 target 模型一次并行验证。内核上关键是 tree attention / 并行 verify:把 draft 猜出的多条候选(树状而非链状)拼成一个 batch,target 模型一次前向同时验证全部,accepted 前缀保留、第一个 reject 之后重采样。

复制代码
draft 猜:  t5 ->[t6a, t6b] -> t7a ...
target 一次验证整棵候选树, 接受最长正确前缀, 其余丢弃
=> 每步可能推进 >1 个 token, decode 步数下降

前缀缓存(prefix caching) 则是:相同系统 prompt(如多模态的系统指令 + 同一张图)的 KV 前缀跨请求复用(PagedAttention 的 block 共享),显著降低首 token 延迟(TTFT)。这在(五十三)多模态服务化里特别关键------同一张图被多个请求共用时,ViT 编码与 KV 前缀都无需重算。

第七节 多模态引擎特例

多模态推理引擎比纯文本多三件事(接五十三):

  1. ViT 解耦:视觉编码器可离线把图像编码成视觉 token 缓存,多个请求共用同一张图时直接命中缓存,避免重复编码。
  2. 图像 token 压缩:一张图可能产出几百个视觉 token,占满 KV。常用 token merging / 下采样 / 可学习压缩,把视觉 token 从 N 压到 M(M<<N)再进 LLM 的 KV 池。
  3. 跨模态前缀缓存:系统 prompt + 同一张图的 KV 前缀可跨会话复用,进一步降 TTFT。

第八节 与岗位结合:怎么调一个引擎

面试现场若被要求「压测并调优一个部署」,可给这套动作:

  1. 先定指标:TTFT(首 token 延迟)、TBT(token 间延迟)、吞吐(req/s)、显存占用。
  2. 用(五十四)的三本账找瓶颈:是算力墙、带宽墙还是显存墙。
  3. 调优杠杆:开 PagedAttention + 连续批、上量化(KV Cache 量化优先,几乎无损)、多模态开 ViT 缓存与 token 压缩、长 prompt 开前缀缓存、必要时 PD 分离。
  4. 压测用真实分布(不是均匀长度),盯 P95/P99 而非均值。

面试速答(本篇可直接背的 3 句)

  1. 推理分 prefill(计算密集、并行)和 decode(访存密集、逐 token),两者瓶颈不同,故常做 PD 分离。
  2. PagedAttention 把 KV Cache 分页成 block + block table 映射,像虚拟内存,消除碎片、提显存利用率到 90%+,支撑连续批处理。
  3. 加速三板斧:FlashAttention 融合算子省显存、量化降带宽、投机解码在 decode 段推进多 token;多模态额外要 ViT 缓存 + 视觉 token 压缩 + 前缀缓存。

高频追问清单

  • 为什么 KV Cache 是 decode 的瓶颈?答:每步要读全部历史 KV,计算量小但访存量随序列线性涨,受显存带宽限制。
  • PagedAttention 和操作系统分页像在哪?答:都用语义连续、物理离散的块表;支持按需分配、共享前缀、换出,避免预留与碎片。
  • 连续批处理比 static 好在哪?答:token 级换入换出,短请求不拖累长请求,GPU 空转少,吞吐与 P99 都更优。
  • 抢占选 swap 还是 recompute?答:短序列 recompute(重算便宜),长序列 swap(搬移比重算省)。
  • FlashAttention 为什么省显存?答:不 materialize N×N 注意力矩阵,分块在 SRAM 内算,显存从 O(N²) 降到 O(N)。
  • 投机解码何时不划算?答:在 compute-bound 或 prefill 阶段不划算,只在带宽受限的 decode 上收益明显。
  • 前缀缓存为什么对多模态特别有用?答:同一系统指令+同一张图的 KV 前缀可跨请求复用,省 ViT 重编码与 KV 重算,降 TTFT。
相关推荐
谢白羽42 分钟前
SGLang模型加载过程笔记
笔记·llm·论文·agent·vllm·大模型部署·sglang
SelectDB技术团队1 小时前
Apache Doris 在内容 AI 生产链路中的实践:从内容打标到可追溯数据链路
大数据·人工智能·大模型·向量检索·混合搜索·ai 打标·内容分析
像风一样自由20202 小时前
14.什么时候用pgvector什么时候单独部署Milvus
postgresql·大模型·微调·milvus
zhangfeng113315 小时前
AMD Instinct MI50(gfx906)上为 Qwen 系列模型优化并可用的 vLLM 相关仓库、Docker 镜像与实践指南。
人工智能·docker·ai编程·qwen·算子开发·vllm·mi50
像风一样自由202019 小时前
13.pgvector入门用PostgreSQL直接实现向量检
人工智能·postgresql·大模型·rag·智能体
liuyunshengsir19 小时前
基于海光DCU的AI音乐翻唱全流程落地实战
人工智能·大模型·音频·dcu
小七-七牛开发者19 小时前
Agent 小知识 | Skill 的设计与生命周期:从工具接口到能力模块
ai·大模型·agent·token·工作流·claudecode·ai coding
前沿在线20 小时前
2026世界机器人大会主论坛大咖观点(三)
人工智能·ai·大模型
寻道码路1 天前
大模型工程化实战(五):LLM 网关到底怎么选 - 2026 自研 / LiteLLM/Portkey/Kong/One API 全面对比
大模型·llmops·llm网关·ai网关·ai工程化