推理

整理各种面试中常见的八股问题,涵盖 Python 基础、PyTorch 基础、算法与数据结构等内容

推理

技术链路

  • Profiling 系统

    • 流程

      请求 → tokenize → prefill(大矩阵) → decode(循环) → sampling/输出
            |             |                |
            v             v                v
         TTFT相关     GPU算子/带宽       TPOT/尾延迟
            \______________  Profiling  ______________/
                       nsys(系统级) + ncu(kernel级)
    • 基础

    • 高级

    • 进阶

    • 验收标准

      • 能同时报告:QPS、TTFT、TPOT、token/s、P50/P95(至少 5 个指标)
      • 能用 nsys 说明:瓶颈在 host、PCIe/NVLink、kernel、mem 哪一层
      • 能用 ncu 说清:关键 kernel 的 memory bound vs compute bound
      • 能写出一份 “一页纸” 性能报告模板
      • 能给出优化优先级排序(不是 “我觉得”,是 “证据链”)
  • Runtime 运行时

    • 流程

      用户请求流
        ↓
      [Scheduler/Batcher] —— 决定并发与排队策略
        ↓
      [KV Cache Manager] —— 决定显存占用/碎片/复用
        ↓
      [Kernel Backend] —— FlashAttention/FlashInfer/TRT 等决定单步效率
        ↓
      输出指标:TTFT/TPOT/吞吐/稳定性
    • 基础

      • vLLM / PagedAttention:把 KV cache 当 “虚拟内存分页” 来管,显著降低碎片与浪费
      • vLLM repo:连续 batching、paged KV、speculative、chunked prefill 等实战特性全集
      • vLLM 官方博客(PagedAttention 入门):用最少背景理解 “为什么吞吐能起来”
    • 高级

      • SGLang:前端语言 + 运行时,靠 RadixAttention(KV 复用) 和 cFSM(结构化输出加速) 提升吞吐
      • SGLang repo:把 “复杂 agent / 多次生成 / JSON 输出” 从工程灾难变成可控系统
      • SGLang 文档站:把 runtime 能力、部署方式、特性讲清楚
    • 进阶

      • TensorRT-LLM:NVIDIA 官方 “重装机甲”,含 paged KV、inflight batching、量化、speculative 等
      • TensorRT-LLM 文档:工程落地导向(部署、serve、API)
      • MLC LLM + microserving:把 prefill-decode 解耦等 “跨引擎编排” 做成可编程 API
    • 验收标准

      • 能用同一模型在 vLLM 与 SGLang 跑出可对比的 TTFT/TPOT
      • 能解释:continuous batching 与传统 batching 的差异与收益点
      • 能说清楚:KV cache 的生命周期、碎片问题、以及框架如何缓解(PagedAttention/RadixAttention)
      • 能把推理链路拆成:tokenize→prefill→decode→sampling→postprocess(并标出每段耗时)
      • 能给出 “选型建议”:低延迟、长上下文、多并发、结构化输出,各用哪个栈更合理
  • Attention & KV Cache

    • 流程

      每步 decode:
      token → Q
      历史 → K,V (KV cache 读取)  ←—— 真正的大头:带宽/布局/碎片
              ↓
         Attention(Q,K,V)  (FlashAttention-3 / FlashInfer 等优化这里)
              ↓
         写回新的 K,V 到 KV cache
    • 基础

    • 高级

    • 进阶

    • 验收标准

      • 能解释 attention 推理时的主要数据流:Q/K/V、KV cache、softmax、写回
      • 能在长上下文场景下,复现实测:FlashAttention/FlashInfer 带来的收益(至少一个指标提升)
      • 能用 “带宽 / 算力” 两句话说明:为什么 decode 往往更 memory-bound
      • 能给出 KV cache 的显存占用公式,并据此估算 batch 上限
      • 能提出 2 个 “KV cache 相关” 的优化动作,并说明副作用(精度 / 延迟 / 工程复杂度)
  • Quantization

    • 流程

      模型(权重/激活/KV)
        ↓  选择目标:省显存?省带宽?提吞吐?
      [W8A8: SmoothQuant] —— 更通用、工程友好
      [INT4 weight-only: AWQ/GPTQ] —— 更省显存/带宽,但看kernel与batch
      [KV Quant: KIVI] —— 长上下文/大并发时更“对症”
        ↓
      上线前必须:质量/稳定性 + 性能回归(TTFT/TPOT)
    • 基础

      • SmoothQuant(W8A8):把 activation outlier 难题 “搬家” 到 weights,工程可落地
      • GPTQ:经典 PTQ weight-only(3/4bit)路线,精度与速度的常用起点
      • AWQ:用 activation 分布找 “关键权重通道”,4-bit 更稳更硬件友好
    • 高级

    • 进阶

    • 验收标准

      • 能区分并选择:W8A8 vs weight-only INT4(场景、代价、收益)
      • 能在同模型上跑通:FP16 baseline 与 INT4(至少一种)并输出精度对比
      • 能解释:为什么有时 “更低 bit 反而更慢”(kernel/packing/ 带宽 / 并行度)
      • 能给出 “量化失败排查清单”(数值爆炸、输出退化、吞吐无提升)
      • 能写出一个可复用的 “量化评测脚本”(吞吐 + 延迟 + 简单质量指标)
  • Decode

    • Speculative 的关键不是 “猜”,而是 “猜一串、验证一串”:Speculative Sampling 给了经典无偏加速框架;Medusa 用多头直接预测多 token;EAGLE-2 用动态 draft tree 更激进;Block Verification 在验证侧继续抠出 5–8% 的稳定增益

    • 流程

      Target模型(慢但准) : 负责验证
      Draft/Heads(快但糙) : 负责批量提案
      
      循环:
      Draft 生成 token1..k  →  Target 一次性验证  →  接受前缀 →  继续
               |                         |
               v                         v
          提案越准/越快             验证越“成块”越省(Block Verification)
    • 基础

      • Speculative Sampling:经典 speculative decoding,2–2.5× 解码加速的起点
      • Medusa:不用外部 draft model,通过多 decoding heads 预测多 token,再并行验证
      • Medusa repo:快速复现 “多头解码” 的工程入口
    • 高级

      • EAGLE-2:动态 draft tree,靠校准的置信度更激进地产生可接受 token
      • Block Verification:把 token-level 验证升级为 block-level 联合验证,稳定榨出额外速度
      • vLLM / TensorRT-LLM 对 speculative 的支持:关注 “能不能在 serving 上稳定收益”
    • 进阶

      • “Decoding Speculative Decoding” 系统化研究:当你要在产线上挑 draft 模型时,这类研究更值钱
      • SGLang 的结构化输出加速(cFSM):很多场景不是 “生成文本”,而是 “生成可解析结构”
      • GenAI-Perf:把 speculative 的收益拆成 TTFT/TPOT/acceptance 等可对比指标
    • 验收标准

      • 能解释 speculative 的正确性保证(分布不变)与实现要点
      • 能在同模型上跑出:baseline vs speculative 的 TPOT 改善,并说明 TTFT 变化
      • 能给出 “什么时候 speculative 不赚” 的判断(任务、温度、draft 质量、batch)
      • 能输出一个 “acceptance length/ratio” 的统计图或表
      • 能提出 1 个 “与量化 / 批处理 / 长上下文耦合” 的风险点,并给规避策略
  • 系统架构

    • 流程

      (传统聚合)
      请求 → [同一组GPU] prefill + decode 交织 → 互扰、尾延迟上天
      
      (解耦 / 混合)
      请求 → Prefill GPU池(擅长大矩阵) → KV/状态转移 → Decode GPU池(擅长循环出token)
                               ↑
                          调度器按 SLO 做 goodput 优化(TaiChi 思路)
    • 基础

    • 高级

      • TaiChi(2025):把 prefill-decode 的 “聚合 / 解耦” 统一起来,面向不同 SLO 组合做最优 goodput
      • DistServe 公开视频、课件:适合把论文落到 “怎么配资源、怎么调度” 的细节
      • MLC microserving:把解耦、跨引擎编排做成 “几行 Python 可表达”
    • 进阶

      • 从 “goodput” 视角重读 DistServe/TaiChi:把系统目标从 QPS 升级为 “满足 SLO 的有效吞吐”
      • SGLang / vLLM 的 kernel & runtime 生态:关注 “解耦之后的 KV、batch、graph、kernel” 如何协同
      • 用真实 trace 做仿真 / 回放(参考 Splitwise/FlashInfer-Bench,Inference Perf,VIDUR,MorphServe,Mooncake):你的系统最终要对抗真实分布
    • 验收标准

      • 能清楚定义并计算:TTFT SLO、TPOT SLO、goodput(满足 SLO 的吞吐)
      • 能解释 prefill/decode 互扰来自哪里(batch / 资源耦合 / 调度)
      • 能给出一个简化的资源配置方案:prefill-heavy vs decode-heavy 的配比(并解释依据)
      • 能复现一个 “小规模解耦” 实验(哪怕 2 机 / 2 卡),并报告 goodput 变化
      • 能列出 3 个系统风险点(KV 迁移、网络、尾延迟、队列震荡)及缓解手段
  • Benchmark & 回归

    • 把 GenAI-Perf(TTFT/TPOT/ 吞吐) 与 MLPerf Inference 这类权威口径纳入回归;MLCommons 也在持续迭代 Inference 结果与规则

    • 流程

      改动(量化/内核/调度/解耦)
        ↓
      固定压测配置(模型/上下文/并发/温度)
        ↓
      GenAI-Perf 输出 TTFT/TPOT/P50/P95/token吞吐
        ↓
      对齐/参考 MLPerf 规则与结果(能比才有意义)
        ↓
      上线门禁:回退就拦截
    • 基础

    • 高级

    • 进阶

    • 验收标准

      • 有一套固定的 benchmark 配置(模型、batch、context、并发、硬件)
      • 每次改动都能输出同一张 “指标对比表”(至少 TTFT/TPOT/token/s)
      • 能把 1 次性能回退定位到具体 commit / 配置 / 驱动变化
      • 能解释 “为什么 MLPerf/GenAI-Perf 的指标口径与你线上一致 / 不一致”
      • 能写出 “上线门禁”:什么指标退化会阻止合并
  • 优化路线

    你现在最痛的是哪一个?
    ├─ TTFT 很大(第一口慢)
    │  ├─ prompt 很长/上下文很长 → 优先:Prefill 优化/Chunked Prefill/更快的 GEMM/更强 prefill GPU
    │  ├─ CPU/调度慢 → 优先:nsys 找 host bottleneck;换更成熟 runtime(vLLM/TRT-LLM)
    │  └─ 频繁重复前缀 → 优先:prefix/KV 复用(运行时能力)
    │
    ├─ TPOT 很大(续杯慢)
    │  ├─ decode 明显 memory-bound → 优先:Attention/KV(FlashAttention-3/FlashInfer)+ KV cache 管理
    │  ├─ token-by-token 串行顶死 → 优先:Speculative / Medusa / EAGLE-2 + Block Verification
    │  └─ batch 太小吃不到量化收益 → 优先:continuous batching + 合理并发;必要时上 Marlin 类 INT4 kernel
    │
    ├─ 显存爆了(batch 上不去/长上下文撑不住)
    │  ├─ 先看 KV cache 占用 → 优先:PagedAttention + KV Quant(KIVI)
    │  └─ 再看权重占用 → INT4 weight-only(AWQ/GPTQ) 或 W8A8(SmoothQuant)
    │
    └─ 尾延迟/P95 爆炸(“最难伺候那 5%”)
       ├─ prefill/decode 互扰明显 → 直接考虑解耦(DistServe/Splitwise 思路)
       └─ SLO 既要 TTFT 又要 TPOT → TaiChi 类“聚合+解耦统一”路线

常见指标

简称全称含义优化思路
TTFTTime To First Token从请求发起到第一个 token 输出的时延用户感知 “快不快”1. 降低 prefill 延迟 2. 减少排队调度延迟 3. 启用动态批处理、优先短请求 4. 模型量化、KV cache 预分配 5. 使用 PagedAttention 减少碎片
TPOTTime Per Output Token生成阶段平均每个输出 token 的耗时决定 “打字流畅度”1. 提升 decode 吞吐 2. 增大 batch、提高 GPU 利用率 3. 使用 Tensor Core、FP8/INT4 量化 4. 减少 KV cache 拷贝与访存 5. 连续批处理 Continuous Batching
ITLInter-Token Latency连续两个输出 token 之间的间隔基本等价 TPOT同 TPOT
E2ELEnd-to-End Latency从请求开始到最后一个 token 输出完成的总耗时1. 优化 TTFT + TPOT2. 限制 max tokens3. 减少超长序列拖慢整体
RPSRequest Per Second每秒处理的请求数1. 提升引擎并发能力 2. 动态批、优先级队列 3. 多实例 / 多卡部署
QPSQuery Per Second每秒查询数(常与 RPS 混用)同 RPS
TPSToken Per Second每秒模型输出的总 token 数(引擎核心吞吐)1. 提高 GPU 算力利用率 2. 增大有效 batch size3. 优化调度、减少空跑
GoodputGood Put符合时延 / 正确性要求的有效吞吐≠ 总吞吐(排除超时、错误、质量差)1. 降低 P99 尾延迟 2. 避免 OOM、重试、超时 3. 合理流控,不超负载
SLOService Level Objective服务等级目标(性能 / 可用性承诺)按目标优化:如 P95 TTFT < 200ms、可用性 99.9%
P5050th Percentile中位数延迟:50% 请求 ≤ 该值代表 “正常体验”优化平均路径即可
P9090th Percentile90% 请求 ≤ 该值优化常见慢路径、减少小波动
P9595th Percentile95% 请求 ≤ 该值线上核心体验指标优化批处理策略、避免请求阻塞
P9999th Percentile99% 请求 ≤ 该值代表尾延迟(Tail Latency)1. 解决超长序列、异常请求 2. 防抖动、防内存碎片 3. 队列隔离、超时控制 4. 预分配、pipeline 流水化

FlashAttention

  • 介绍一下 FlashAttention,它主要优化了什么?
    • FlashAttention 是 2022 年斯坦福提出的 IO 感知精确注意力算法,核心是通过分块计算、在线 Softmax、反向重计算,把标准 Attention 的显存复杂度从 O (N²) 降到 O (N)、速度提升 2–4 倍,数学完全等价、不丢精度
    • FlashAttention 的出发点是:标准 attention 的瓶颈不是计算,而是 “attention matrix(n×n)” 的巨大 IO 开销,并且需要存储 QKTQK^T、softmax 输出等中间结果,导致显存爆炸和带宽瓶颈
    • FlashAttention 的核心优化点是:
      • 按 tile(块)分块计算 QKTQK^T,在 SRAM 中完成 partial max、partial sum 和累积输出,最终直接得到结果,而不需要显式计算并写回 n×n attention matrix
      • 在线 softmax(online softmax):在计算过程中动态维护 max 和 sum,避免了对整个 attention matrix 的存储和多次读写
      • 反向传播时重计算 QKTQK^T 和 softmax 输出,节省了反向传播的显存需求
  • FlashAttention-V1 的计算流程?反向传播的流程?访存量和通信量?需要写回 HBM 的有哪些内容?
    • 前向传播:
      • 按 tile 分块计算 QKTQK^T,得到 partial attention scores
      • 在 SRAM 中维护 partial max 和 partial sum,完成在线 softmax
      • 直接乘 V 得到输出,不写回 attention matrix
    • 反向传播:
      • 重计算 QKTQK^T 和 softmax 输出
      • 计算 dQ、dK、dV,写回 dQ、dK、dV 到 HBM
    • 访存量:
      • 前向传播:读 Q/K/V,写输出(不写 attention matrix)
      • 反向传播:重计算 QKTQK^T 和 softmax 输出,读 Q/K/V,写 dQ/dK/dV
    • 需要写回 HBM 的内容:
      • 前向传播:输出(不写 attention matrix)
      • 反向传播:dQ、dK、dV
  • 在线 Softmax 的推导
    • 在线 Softmax 的核心是维护 partial max 和 partial sum,避免了对整个 attention matrix 的存储和多次读写
    • 设当前 tile 的 attention scores 为 S,之前的 partial max 为 M,partial sum 为 SsumS_sum
    • 更新 partial max:Mnew=max⁡(M,max⁡(S))M_{new} = \max (M, \max (S))
    • 更新 partial sum:Ssum_new=Ssum⋅exp⁡(M−Mnew)+∑(exp⁡(S−Mnew))S_{sum\_new} = S_{sum} \cdot \exp (M - M_{new}) + \sum (\exp (S - M_{new}))
    • 这样在计算过程中就动态维护了 max 和 sum,最终得到的 softmax 输出是正确的,而不需要存储整个 attention matrix
  • FlashAttention-V2 又做了什么改进?访存量和通信量?需要写回 HBM 的有哪些内容?
    • 循环顺序调换:V1 外层 K/V、V2 外层 Q
    • 输出只写回 HBM 1 次,消除 O (N) 重复读写
    • 并行度更高、FLOP 利用率从 25–40% → 更高
    • 访存量:
      • 前向传播:读 Q/K/V,写输出(不写 attention matrix)
      • 反向传播:重计算 QKTQK^T 和 softmax 输出,读 Q/K/V,写 dQ/dK/dV
    • 需要写回 HBM 的内容:
      • 前向传播:输出(不写 attention matrix)
      • 反向传播:dQ、dK、dV
  • 了解 FlashAttention-V3 吗?
  • 了解 FlashAttention-V4 吗?
  • 为什么 decode 是 memory-bound?
    • 在自回归推理(decode)阶段,每一步只生成 1 个 token,此时计算的特点是:
    • Q 只有一个(当前 token)
    • K/V 是历史所有 token(长度 t)
    • attention 计算变成 QTK→softmax→VQ^T K \rightarrow \text{softmax} \rightarrow V
    • 这里 FLOPs 很少(O (t・d)),但问题在于:
      • 每一步都要把整个 KV cache 从 HBM 读出来
      • 计算量小,但访存量大
    • GPU 的计算单元是没吃满的,瓶颈变成了 HBM 带宽 → 典型 memory-bound
  • KV cache 为什么越来越大?
    • KV cache 的大小是 layers × heads × seq_len × head_dim × 2(K + V)
    • 随着序列长度增长(长上下文)、模型层数增加、head 数增加,KV cache 线性甚至接近指数级膨胀(在多 batch 时)
    • 而 decode 是逐 token 增长的,cache 只会 “越来越大”,不会释放,这也是推理显存的核心压力来源
  • IO-aware 是什么意思?具体怎么减少 IO?为什么标准 attention 慢?
    • IO-aware 的核心意思是算法设计以 “减少 HBM ↔ SRAM 数据搬运” 为目标,而不是只看 FLOPs
    • 标准 attention 的问题不在于计算,而在 IO:
      • 先算 QKTQK^T → 写回 HBM(n×n)
      • 再读回来做 softmax
      • 再写回
      • 再读出来乘 V
    • attention matrix(n2n^2)被反复读写,这是灾难性的 IO 开销
    • 所以标准 attention 慢,本质是不是算不动,而是 “搬数据太多”
  • FlashAttention 为什么不需要存 attention matrix?
    • FlashAttention 的关键思想是按 tile(块)分块计算 + 在线 softmax(online softmax)
    • 也就是说不一次性算完整 QK^T,而是分块算一部分(block of K/V),在 SRAM 中完成 partial max、partial sum 和累积输出
    • 最终直接得到结果,而不需要显式 materialize n×n attention matrix
  • tile size 怎么选?
    • tile 的大小取决于:
      • SRAM(shared memory /register)容量
      • head_dim
      • 是否能让数据完全驻留在片上
    • 原则是:尽量让一个 tile 的 Q/K/V + 中间变量都放进 SRAM
    • 通常会选一个让 occupancy 和带宽利用率最优的 block size,而不是纯理论最优
  • SRAM / HBM 访问次数是多少?
    • 标准 attention:
      • Q/K/V:读一次
      • attention matrix:写 + 读 + 写 + 读(多次)→ IO 是 O(n2)O (n^2)
    • FlashAttention:
      • Q:读一次
      • K/V:按 tile 逐块读
      • 不写 attention matrix → IO 变成 O(n⋅d)O (n\cdot d),这就是数量级优化
  • 为什么是 IO-aware?
    • 因为它优化的不是 FLOPs,而是 HBM 访问次数、数据复用(SRAM 内循环使用)
    • 本质上是把 attention 从 compute + huge IO 变成 compute + minimal IO
  • FlashAttention 什么时候不明显快?FlashAttention 为什么在短序列不明显?
    • 短序列(比如 n < 512)时,attention matrix 本来就不大
      • IO 不是瓶颈
      • kernel launch / 调度开销占比更高
    • 此时 FlashAttention 的优势(减少 IO)体现不出来,甚至可能略慢
      • 长序列 → IO 成为瓶颈 → FlashAttention 很有用
      • 短序列 → compute /launch 成为瓶颈 → 提升有限
  • FlashAttention v2 相较 v1 的改进点
  • 你的框架里用了 FlashAttention 吗?显存占用和 MFU 的改进如何?
    • 我使用了类似 FlashAttention 或 xFormers fused kernel
    • 显存:attention 不再需要 n² 中间矩阵,激活显存显著下降(尤其长序列)
    • 吞吐:因为减少 IO + kernel fusion,step time 明显下降
    • MFU:从 13.4% 提升到 34%(取决于模型和序列长度)
  • FlashAttention + TP 怎么结合?
    • FlashAttention 本质是单卡 kernel 优化,而 TP(Tensor Parallel)是跨卡切分
    • 两者结合方式是:
      • 每个 TP shard 上独立做 FlashAttention
      • Q/K/V 已经按 head 或 hidden dim 切分
    • 关键点在于:
      • head parallel(最常见)
        • 每张卡负责一部分 heads
        • FlashAttention 直接在本地执行
        • 不需要额外通信
      • sequence parallel(更复杂)
        • 需要跨卡 gather K/V
        • 会引入通信 → 抵消一部分优化
    • 工程上最佳实践是 TP 按 head 切分 + 每卡用 FlashAttention
    • 这样无额外通信,而 IO 优化完全保留

Sparse Attention

  • 介绍一下稀疏注意力?有哪些类型?稀疏注意力主要用在什么场景?

  • BigBird 的稀疏注意力结构?

    • 全局注意力:选定 g 个特殊 token(如 CLS、问题句),双向全连接:全局 token 关注所有 token,所有 token 也关注全局 token
    • 窗口注意力:每个 token 仅关注左右各 w/2 个邻域 token(固定窗口),类似 CNN 感受野
    • 随机注意力:每个 token 随机选择 r 个其他 token 建立连接(全局 + 窗口外的补充)
  • Longformer 的稀疏注意力结构?

    • 窗口注意力:每个 token 只关注左右各 w/2 个相邻 token
    • Global Attention(全局注意力)
  • Performer

  • Linformer

  • 稀疏注意力对 KV Cache、推理速度的影响?

    • 稀疏注意力几乎不减少 KV Cache 显存占用,甚至可能略微增加
    • 不管你注意力是不是稀疏:
      • 每一层、每个 token 的 K、V 依然要完整保存
      • 因为下一层、下一个 token 生成时还会用到
      • 稀疏只是 “不算某些 Q-K 对”,不是 “不存某些 KV”
    • KV Cache 大小 = O (n × d × layers),和是否稀疏完全无关
    • 预填充阶段(Prefill)速度明显变快
    • 解码生成阶段(Decode)几乎没加速,甚至变慢
    • 稀疏索引、不规则访存、掩码判断 → Kernel 开销变大 → 实际速度常常比普通注意力还慢一点
  • 稀疏注意力和 FlashAttention 能不能一起用?

    • FlashAttention-2 已原生支持块稀疏(Block-Sparse):
      • 把注意力矩阵按块划分(如 128×128)
      • 按稀疏模式(窗口 / 全局 / 随机)标记哪些块需要计算、哪些跳过
      • 内核只加载 / 计算非零块,跳过零块,保持分块重计算、流水、数值稳定
  • 为什么现在主流大模型不怎么用纯稀疏注意力?

    • 表达能力与长程依赖丢失
    • 训练不稳定、难收敛、难扩展
    • 训练 - 推理不一致:训练用稠密 + 稀疏混合,推理纯稀疏
    • 硬件效率 “理论美、实际差”:不规则内存访问,KV Cache 仍旧需要计算
  • 介绍一下 Sliding Window Attention?

  • 介绍一下 Linear Attention?Linear Attention 的原理和代码实现?

    • Linear Attention 的核心是把 softmax(QKT)Vsoftmax (QK^T) V 改写为:ϕ(Q)(ϕ(K)TV)\phi (Q)(\phi (K) TV)

    • 其中 ϕ\phi 是 feature map,使 attention 可以重排为:(QKT)V→Q(KTV)(QK^T) V \rightarrow Q (KTV),从而把复杂度从 O(n2)O (n^2) 变成 O(n)O (n)​

    • 简单实现

      def linear_attention(q, k, v, eps=1e-6):
          q = torch.relu(q) + eps
          k = torch.relu(k) + eps
      
          kv = k.transpose(-2, -1) @ v   # [d, d]
          z = 1 / (q @ k.sum(dim=-2, keepdim=True).transpose(-2, -1) + eps)
      
          out = (q @ kv) * z
          return out
    • softmax 为什么不能 kernelize?

      • softmax(QKT)softmax (QK^T) 的问题在于 exp(q⋅k)exp (q\cdot k) 不是一个有限维的核函数展开(没有低维 ϕ\phi 使得 exp(q⋅k)=ϕ(q)Tϕ(k)exp (q\cdot k)=\phi (q)^T\phi (k) 精确成立)
      • 所以 Linear Attention 只能做近似
    • feature map 怎么选?

      • feature map 的选择(如 ReLU、ELU+1)需要满足非负(避免 cancellation)、数值稳定、近似 exp kernel
    • 数值稳定性怎么保证?

      • 稳定性不足主要来自于:分母可能接近 0、累积误差(尤其长序列)
      • 常见解决方案包括加 eps、normalization、分块计算等
  • Linear Attention 为什么能做到 O(n)O (n)?代价是什么?

    • 关键在于把 QKTVQK^T V → Q(KTV)Q (K^T V),其中 KTVK^T V 是一个 “全局 summary”,只需要 O(n)O (n) 累加
    • 代价主要有两个
    • 第一,表达能力下降(softmax 的 sharp attention 没了,核函数抹平了 Softmax 的 “聚焦” 与 “对比”,导致注意力稀释、非线性表达受限,关键信息丢失)
    • 第二,数值稳定性变差,尤其长序列(隐式欧拉离散化引入累积误差,更新矩阵低秩导致刚性系统不稳,长序列与大学习率下易发散)
  • 为什么 Linear Attention 没取代 Softmax?为什么大模型最终还是用 softmax attention?

    • 本质原因是:效果不够好
    • softmax attention 的优势在于:
      • 能做 “稀疏选择”(某些 token 权重大,其他接近 0)
      • 表达能力强
    • 而 linear attention 更像是 “加权平均”,区分能力弱
    • 在大模型 scaling 下,这种差距会被放大,所以最终主流 LLM 还是回到 softmax(配合 FlashAttention 做工程优化)

KV Cache 优化

  • KV cache 是什么?KV cache 为什么只在推理用?

    • KV cache 是在自回归推理时,把历史 token 的 Key 和 Value 缓存下来
    • 这样生成第 t 个 token 时,不用重新计算前面 1~t-1 的 K/V,只需要算当前 token 的 Q,然后和缓存做 attention
    • 它只在推理用,是因为:
      • 训练时是 full sequence 并行(teacher forcing),所有 token 一次性算完,不需要 cache
      • cache 会增加显存,而且训练不需要重复利用历史
    • KV Cache 本质是空间换时间,用显存存历史 Key/Value,避免重复计算
  • 为什么 Multi-Query attention 能优化 KV cache?

    • Multi-Query Attention(MQA)优化 KV cache 的核心是多个 head 共享同一份 K/V,只保留不同的 Q
    • 这样 KV cache 的大小从 num_heads × seq_len × head_dim 变成 1 × seq_len × head_dim,显存直接下降一个数量级
  • GQA 与 MQA 的区别、显存对比、适用场景?

    • MQA 适用
      • 极致低显存 / 高吞吐:移动端、边缘设备、超大规模批处理
      • 延迟敏感、短文本为主:对话机器人、简单生成
      • 代表:PaLM、GPT-4 推理优化、早期 T5
    • GQA 适用(当前大模型主流)
      • 长上下文 + 高效推理:LLaMA 2/3、Qwen、GLM、Falcon 标配
      • 平衡:显存省 + 精度几乎不掉
      • 推荐:7B/13B 用 GQA-4,70B 用 GQA-8
  • KV Cache 需要占用多少显存?公式计算?

    KV Cache 显存 (Bytes)=2×NL×B×S×Nkv×dh×Db\text{KV Cache 显存 (Bytes)} = 2 \times N_L \times B \times S \times N_{kv} \times d_h \times D_b
    • 2:同时存储 Key 与 Value 两份矩阵
    • NLN_L (num_layers):Transformer 模型总层数
    • B (batch_size):批大小 / 并发请求数
    • S (seq_len):上下文序列长度(Prompt + 已生成 Token 总数)
    • NkvN_{kv} (num_kv_heads):KV 头数
      • 标准 MHA:Nkv=NqN_{kv} = N_q
      • GQA/MQA:Nkv<NqN_{kv} <N_q(如 LLaMA 3 70B: Nkv=64N_{kv}=64)
    • dhd_h (head_dim):单个头维度 (dh=hidden_size÷Nqd_h = \text{hidden\_size} \div N_q)
    • DbD_b (dtype_bytes):精度字节数
      • FP16/BF16: 2 Bytes
      • INT8/FP8: 1 Byte
      • FP32: 4 Bytes
  • KV Cache 导致了哪些需要优化的问题?相应地有哪些优化手段?

    • 显存占用过大:KV Cache 随着序列长度、层数、batch 大小线性增长;相应地,可以通过 GQA/MQA、KV Cache 量化、Offloading 等方式优化
    • 内存带宽瓶颈:KV Cache 体积大,推理时要反复读写缓存;相应优化手段如 PagedAttention 减少内存拷贝和碎片、连续内存排布 / 紧凑布局来提高缓存命中率、FlashAttention 减少 HBM 读写,同时不写回完整的 KV
    • 长文本下性能急剧衰减:随着序列长度越大,生成 token 延迟越高,可以采用 SlidingWindowAttention、KV Cache 稀疏化、Chunked Attention 等方式优化
    • 多并发调度低效(Batch 乱序、碎片):不同请求序列长度不同,会导致内存碎片严重,而且在动态 batch 下无法高效复用与扩容;相应地,可以通过 Continuous Batching、PagedAttention、Prefix Cache 等方式缓解
    • 质量问题(KV Cache 带来的精度下降):选择式刷新 KV、动态压缩不重要的 KV
  • KV Cache 为什么不能跨请求共享?什么情况下可以共享?

    • KV Cache 存储的是对每一层、每个位置的 token,计算出的 Key & Value,这两个值完全由输入 token 决定,不同输入就不同 KV
    • 当两个请求的前缀 token 序列完全相同时,这 n 个位置的 KV 就可以完全复用、共享 —— 也即 Prefix Cache
    • 在 PagedAttention 中,相同内容的块直接引用计数共享,不重复存储
  • 为什么 KV Cache 量化比权重量化更容易掉点?

    • 对权重量化是静态的,训练时就固定,可以通过量化感知训练(QAT)、微调 去适应低精度,即使量化误差大,模型也能学到 “容错表示”;而且权重全局共享,误差被大量 token 平均抵消
    • 对 KV Cache 量化则是对推理时实时计算的激活值进行量化,误差会直接进入注意力 softmax,噪声会随生成一步步累积放大,而且没法修正,所以更容易掉点
  • 什么是 PagedAttention?

    • vllm 中的技术,把 KV Cache 像操作系统 “虚拟内存分页” 一样切成固定大小块,用块表映射、非连续存储、支持跨请求前缀共享,彻底解决显存碎片与浪费
    • 实现细节
      • 切块:把每个序列的 KV 分成固定大小 block(如 16 token)
      • 块表(Block Table):每个请求维护一张表,记录逻辑 token → 物理 block 映射
      • 非连续存储:KV 不必在显存里连续,允许碎片化但无浪费
      • 前缀共享:多请求相同前缀共用物理 block(引用计数)
      • 可换入换出:block 可在 GPU/CPU 间换入换出,支持超长上下文
  • 什么是 RadixAttention?

    • SGLang 中的技术,是一种 KV Cache 全局索引结构,用基数树(Radix Tree / 压缩前缀树)全局组织所有请求的 KV Cache,自动发现、共享、复用所有公共前缀,比 PagedAttention 前缀共享更彻底、更自动化
    • 实现细节
      • 所有请求序列看作 “字符串”,插入一棵共享基数树
      • 相同路径(前缀)共用同一组 KV block
      • 新请求进来:先在树里匹配最长前缀 → 直接复用 KV → 只计算新增后缀
      • 带 LRU 驱逐、引用计数、跨 GPU 共享
  • 什么是 Continuous Batching?

    • 迭代级动态批调度 —— 请求随时进、随时出,GPU 永远满载,彻底解决静态批 “等最慢请求” 的浪费(也叫 Inflight Batching / Iteration Batching)
    • 传统静态批:一批请求必须全部生成完才释放资源
    • Continuous Batching:
      • 每一步(iteration)只生成一个 token
      • 某请求完成 → 立即踢出、新请求立即补上
      • 批大小动态浮动、GPU 利用率接近 100%
    • 配合 PagedAttention 内存管理,吞吐量提升 3–5 倍
  • 什么是 Chunked Prefill?

    • 把长 Prompt 的 Prefill 切成小 chunk 分步执行,避免长 Prompt 独占 GPU、阻塞解码、导致 OOM,并与 Decode 交错调度
    • LLM 推理分为两个阶段,Prefill 计算 Prompt,其计算密集耗时较长,而 Decode 逐个生成 token,其访存密集 GPU 利用率低
    • 超长 Prompt 一次性 Prefill 会 “霸占” GPU,导致正在 Decode 的请求卡住
    • 因此可以把 Prompt 切成 chunk(如 512/1024 token)、逐块 Prefill、累积 KV Cache,调度器把 Prefill chunk 与 Decode 请求交错执行( Stall-Free Batching)
    • 这样可以防止 OOM,降低首 Token 延迟(TTFT),提升吞吐
  • 什么是 Speculative Decoding(投机解码)?

    • 小模型草拟 + 大模型并行验证 —— 用廉价小模型快速猜多个 token,大模型一次验证通过就批量接受,大幅减少大模型前向次数
    • 验证能成立的关键是小模型(Draft)和大模型(Target)基于同一套 Token 词典、同一类概率计算规则(如 Softmax 输出 Token 的条件概率),且二者的推理都是自回归式的(下一个 Token 的生成依赖前序所有 Token)
    • 流程
      • 小模型的作用:基于原始输入 x0,x1,⋯ ,xnx_0,x_1,\cdots,x_n,快速生成 k 个候选 Token 序列 y1,y2,⋯ ,yky_1,y_2,\cdots,y_k(即 “草拟”,贪心 / 采样生成,速度极快)
      • 大模型的验证基础:不单独生成 Token,而是直接把「原始输入 + 小模型候选序列」拼接成完整序列 x0,x1,⋯ ,xn,y1,y2,⋯ ,ykx_0,x_1,\cdots,x_n,y_1,y_2,\cdots,y_k,作为自身的输入做一次前向计算,一次性输出每个候选位置上的 Token 条件概率分布
      • 接受规则:
        • 逐 token 比对:一致则接受、直到第一个分歧点
        • 接受部分直接输出;分歧点回退,大模型重生成
      • 效果:平均每步生成 >1 token,速度提升 2–4 倍、输出分布不变(无损)
  • 什么是 Prefix Cache / Prefix KV Cache?

    • 跨请求复用公共前缀的 KV Cache—— 把系统提示、长文档、共享模板的 KV 缓存下来,后续请求直接复用,避免重复计算
    • 缓存从开头连续 token 序列 的 KV,而新请求匹配最长公共前缀 → 直接加载缓存 KV,这样只需要计算后缀部分 → TTFT 大幅降低(常 2×+)
  • 什么是 FlashDecoding?它和 FlashAttention 有什么区别?

    • 长序列推理专用加速 —— 在 FlashAttention 基础上,对 KV 序列长度维度做并行拆分,让单 batch、长上下文也能打满 GPU 算力
    • 原理
      • 把长 KV 切成多个 chunk
      • 并行用 FlashAttention 计算 Q 与每个 KV chunk 的注意力
      • 记录每个块的 log-sum-exp(缩放因子)
      • 最终合并所有块结果(reduction)
      • 优势:长上下文(1k–128k)速度提升 4–8 倍,单 batch 也满卡
  • 长文本下 KV Cache 稀疏化方法有哪些?

    • 稀疏化:只保留 / 只计算对当前生成最关键的 KV,丢弃 / 压缩不重要部分
    • 时间维度的稀疏化 —— 按 “重要性” 只保留部分 token 的 KV,丢弃其余
      • Sliding Window Attention:只保留最近 N 个 token,完全丢失长程依赖
      • StreamingLLM / Attention Sink:保留 初始 4–6 个 token(Sink)+ 最近窗口,丢弃中间,然而中间段落信息丢失,复杂推理仍掉点
    • 通道 / 特征维度稀疏化 —— 对 KV 向量的维度(通道)做稀疏 / 剪枝
      • MQA/GQA(分组查询注意力)
      • MLA(Multi-Head Latent Attention):KV 投影到极低维隐空间,需要在模型训练时就支持
    • 低秩与稀疏编码压缩:用更少参数近似表示 KV
      • 量化 + 稀疏结合:先量化(FP8/FP4)减显存,再做 token/channel 稀疏
  • 什么是 Attention Free Cache?

    • 跳过完整的 Softmax 注意力计算,用更轻量的规则直接判断哪些 KV 重要、哪些可删
    • 静态 AFC:滑动窗口、StreamingLLM、向量相似度 / 范数淘汰
    • 动态 AFC:HashEvict / LSH 基(Attention Free 淘汰)
      • 把 Q/K 向量哈希成短二进制码(SimHash)
      • 用汉明距离近似 Q-K 相似度,直接淘汰汉明距离最大(最不相关)的 KV
  • 什么是 pd 分离?什么是 AF 分离?

    • pd 分离(Prefill-Decode)
      • Prefill 阶段处理整个 prompt,计算密集,并行度较高
      • Decode 阶段每次生成一个 token,内存带宽密集,需要反复读 KV Cache,算力闲置
    • pd 分离把 Prefill 和 Decode 拆到不同硬件 / 集群
      • Prefill 集群:强算力,只做 prompt 编码 + 生成 KV Cache
      • Decode 集群:高带宽,只做逐 token 生成
      • KV Cache 通过 RDMA/NVLink 高速迁移
    • pd 分离主要解决同一 GPU 上 Prefill 与 Decode 资源争抢、互相干扰的问题,无法分别优化 TTFT(首 token)和 TPOT(逐 token 延迟)
    • AF 分离(Attention-FFN)
      • 把 Decode 阶段拆分为两块,把 Attention 和 FFN 分到不同硬件
      • Attention 计算是状态密集型,依赖巨大且动态变化的 KV Cache,瓶颈主要是内存带宽、显存容量,负载不稳定且会随着上下文长度增长
      • FFN 计算是与 KV Cache 无关的密集型计算,瓶颈主要是算力和批次大小,负载稳定
    • AF 分离架构
      • A 集群(Attention):存所有 KV Cache、做 QKV+Softmax
      • F 集群(FFN):只跑两层线性 + 激活(占模型 80%+ 权重)
      • 每一层:A → 通信 → F → 通信 → 下一层 A
    • 为什么还需要 AF 分离?
      • PD 只解决了 阶段间资源错配,但 Decode 内部依然严重浪费
      • 通过 AF 分离,A 集群和 F 集群可以独立扩展
      • A 集群:只加显存 / 带宽,应对 KV Cache 膨胀
      • F 集群:只加算力 / 卡数,应对 FFN 计算量
      • 同时,FFN 可以超批次进行并行,多个 A 实例的输出可以定向到同一个 F 实例,FFN 算力利用率拉满
  • MTP 是什么?怎么应用?

    • LLM 训练 + 推理加速技术,核心是一次前向,并行预测未来 N 个 token,不是只预测下 1 个
    • 主干 Transformer 不变,加 N 个辅助预测头(共享底层,独立 Linear)
    • 训练时:同时预测 t+1,t+2,…,t+Nt+1, t+2, \ldots, t+N,多损失加权联合训练
    • 推理时:用辅助头一次性生成 N 个候选 token,主头验证 / 接受 → 减少解码步数
    • 训练:样本效率↑、长程依赖建模更强、收敛更快
    • 推理:解码步数减少 N 倍 → 延迟 - 40%70%、吞吐↑23×
    • 无损 / 微损精度,几乎无额外显存开销
  • MTP 是什么?它的基本原理?

    • DeepSeek 参照 Meta 的 MTP 设计,在实现上保留了序列推理的连接关系(causal chain)

    • 整体架构

      • Main Model:传统 decoder-only 主干网络,负责 next token 预测(第 0 个预测深度)
      • MTP Module 1D:D 个顺序串联的模块,分别负责预测第 1D 个后续 token(next next token、next³ token 等)
      • 共享机制:词表投影层(vocab_predict_head)全局共享,embedding 层在模块间共享参数
    • 单个 MTP 模块结构

      • Embedding Layer:接收输入 token 序列,生成 token embedding
      • 双路 RMSNorm:
        • 对前序模块的隐层输出做 RMSNorm 归一化
        • 对当前位置的 token embedding 做 RMSNorm 归一化
      • Concatenation + Linear Projection:拼接两路归一化结果,通过投影矩阵做线性变换,融合特征
      • Transformer Block:独立的 Transformer 层,处理融合后的特征,提取序列上下文
      • Output Head:共享的词表投影层 + Softmax,输出词表概率分布
      • Cross-Entropy Loss:计算当前模块的预测损失
    • 变量定义

      • TT:输入序列总长度
      • kk:预测深度(k=0k=0 对应 Main Model,k≥1k \geq 1 对应 MTP Module kk)
      • ii:输入 token 的位置索引
      • dd:隐层维度,VV:词表大小
    • 第一步特征融合

      hik=Mk[RMSNorm(hik−1);RMSNorm(Emb(ti+k))],\mathbf {h}_i^k = M_k [\text{RMSNorm}(\mathbf {h}_i^{k-1}); \text{RMSNorm}(Emb (t_{i+k}))],
      • 输入:
        • hik−1h_{i}^{k-1}:第 k−1k-1 层的隐层输出(k=1k=1 时为 Main Model 的隐层表征)
        • Emb(ti+k)Emb (t_{i+k}):第 i+ki+k 位置的 token embedding
      • 处理:两路分别做 RMSNorm 归一化 → 拼接 → 投影矩阵 Mk∈Rd×2dM_k \in \mathbb {R}^{d \times 2d} 线性变换
      • 输出:融合后的特征 hik∈Rd\mathbf {h}_i^k \in \mathbb {R}^d
    • 第二步特征提取

      h1:T−kk=TRMk(h1:T−k′k),\mathbf {h}_{1:T-k}^k = \text{TRM}_k (\mathbf {h}_{1:T-k}'^k),
      • 输入:融合特征序列 h1:T−k′k\mathbf {h}_{1:T-k}'^k(切片范围 [1,T−k][1, T-k],保证 i+k≤Ti+k \leq T)
      • 处理:第 kk 个独立 Transformer 层 TRMk\text{TRM}_k 提取上下文特征
      • 输出:第 kk 预测深度的隐层表征 h1:T−kk\mathbf {h}_{1:T-k}^k
      • 1:T−k1:T-k:第 kk 预测深度的输入 token 位置 ii 需满足 i+k≤Ti+k \leq T,因此有效输入范围为 i∈[1,T−k]i \in [1, T-k]
    • 第三步词表概率输出

      Pi+k+1k=OutHead(hik).P_{i+k+1}^k = \text{OutHead}(\mathbf {h}_i^k).
      • 输入:隐层表征 hik\mathbf {h}_i^k
      • 处理:共享的映射矩阵 OutHead∈RV×d 变换 + Softmax
      • 输出:第 ii 个输入 token 对应预测的第 i+k+1i+k+1 位置 token 的概率分布 Pi+k+1kP_{i+k+1}^k
      • 下标 i+k+1i+k+1:输入 tit_i 预测的是序列中第 i+k+1i+k+1 位的 token,对应 input 与 label 序列错位 k+1k+1 位(Main Model 错位 1 位,MTP Module 1 错位 2 位,以此类推)
    • 损失计算

      LMTPk=CrossEntropy(P2+k:T+1k,t2+k:T+1)=−1T∑i=2+kTlog⁡Pik[ti]\mathcal {L}_{\text{MTP}}^k = \text{CrossEntropy}(P_{2+k:T+1}^k, t_{2+k:T+1}) = -\frac {1}{T} \sum_{i=2+k}^{T} \log P_i^k [t_i]
      • 2+k:T+12+k:T+1:label 范围,起始下标 2+k2+k(MTP Module k 第一个预测 token 为 t2+kt_{2+k}),结束下标 T+1T+1(eos token 位置)
      • i=2+ki=2+k 到 TT:遍历所有有效预测位置,计算交叉熵损失
      • 总损失:Main Model 损失 + 所有 MTP Module 损失加权求和,联合训练
    • 训练模式

      • 训练时直接使用样本中的 ground truth token 作为 MTP 模块的输入,而非上一模块的预测输出
      • 避免预测误差累积,提升训练效果;推理时切换为 free-running 模式,用上一模块输出作为输入
  • MTP 的缺点是什么?

    • MTP 训练时每一步都用 ground truth 输入(teacher forcing),但真实推理时前面的 token 是模型自己生成的,有误差
    • MTP 因为预测更远,对误差累积更敏感,所以推理时更容易偏离训练分布 → 生成质量下降,这比普通 next-token 模型的 exposure bias 更严重
  • 了解 vllm / SGLang 的架构设计吗?

    • 最主流的 LLM 推理服务框架,核心都是解决高吞吐、低延迟、高显存利用率
    • vllm:PagedAttention + Continuous Batching
      • API Server:提供 AsyncLLMEngine 接口
      • EngineCore
        • Scheduler(Continuous Batching):动态混合不同阶段的请求,负责抢占、优先级、Token 级资源预算
        • KVCacheManager(PagedAttention):负责将逻辑连续的 KV Cache 映射到物理非连续的地址空间中
        • Executor:分发任务到 GPU Worker
      • GPU Worker:一个 GPU 一个 Worker,负责模型权重、KV Cache、CUDA 核执行,并支持 TP/PP 分布式
      • 缺点:KV Cache 仅请求内复用,跨请求前缀无法共享;复杂工作流支持弱
    • SGLang:RadixAttention + 结构化编程
      • 前端 Python DSL,负责结构化生成、多轮对话、Agent
      • 后端
        • Compiler:TVM 图优化、内存规划、代码生成
        • Scheduler:CPU-GPU 双循环、前缀匹配、PD 分离
        • RadixAttention:基数树(Radix Tree)组织 KV Cache,跨请求公共前缀全局复用,LRU 驱逐、热点 / 冷区分级
        • 分布式引擎:TP/PP/DP 混合并行
  • KV 量化压缩

    • 量化:FP8/INT8/INT4/2bit 混合
    • 剪枝:Titanus(ACM 2025):级联剪枝 + 量化,KV 传输 -80%

模型剪枝

  • 结构化剪枝
    • 按通道 / 头 / 层剪:GPU 友好,无需稀疏库
    • 典型:注意力头剪枝(保留 60–80% 头)、FFN 中间维度压缩
  • 非结构化剪枝
    • 随机 / 按重要性置零单个权重 → 稀疏矩阵
    • 需 cuSPARSELt 等库,实际部署很少用

CUDA

  • bank conflict、Memory Coalescing、occupancy、Kernel Launch Overhead、SM/SP/Block/Grid/Warp、SIMT/SIMD、Shared Memory / L2 / HBM
  • SIMD、SPMD、SIMT 模型对比
  • 什么是 memory coalescing?对访存带宽有什么影响?
    • 内存合并的目标是让同一个 warp 内的线程访问连续且对齐的全局内存地址
    • 这样 GPU 硬件可以把这些访问请求 “合并” 为一次大的 DRAM 访问请求(一次突发传输),效率比每个线程单独访问高很多
  • 什么是 bank conflict?为什么会影响性能?如何避免?
    • Shared Memory 分成 32 个 bank,而每个 bank 一个周期只能服务一个线程
    • 同一个 warp 里,多个线程访问同一个 bank 的不同地址,硬件只能串行化服务,不能并行,这就叫 bank conflict
    • 要避免 bank conflict,就要保证访问连续、不重叠,并利用填充(Padding)使得错开 bank,也可以利用 stride 互质、广播
  • 如何在 CUDA kernel 里实现连续、合并的全局内存访问?
    • 同一个 warp 内 32 个线程,访问全局内存的地址必须:连续 + 按线程顺序递增
    • 线程 tid → 访问 base_addr + tid
    • 线程 tid+1 → 访问 base_addr + tid+1
    • 硬件自动合并为一次 128B 突发传输(最理想)
    • 同时,必须让线程沿着行方向连续访问,不能按列访问,否则会导致无法合并内存访问
    • 全局内存用 Memory Coalescing 读入 → 存入 Shared Memory → 计算时避免 Bank Conflict → 写回时再 Coalescing
  • GPU 上的 occupancy 是什么?受哪些因素影响?
    • Occupancy = 活跃 warp 占硬件最大可并发 warp 数的比例
    • 一个 SM(流式多处理器)同一时间能 “挂起 / 切换” 的 warp 越多,隐藏访存延迟的能力越强
    • Occupancy 本质衡量 GPU 有多忙、能不能用计算掩盖全局内存延迟
    • CUDA 会取最小的那个作为实际活跃线程数:
      • 线程块数量限制
      • 每个线程块的线程数(block size)
      • 寄存器使用数量(regs per thread)
      • 共享内存使用量(shared memory per block)
  • 是不是 occupancy 越高,kernel 性能一定越好?为什么?
    • Occupancy 只解决 “延迟隐藏”,不解决 “实际工作量”
      • 如果 kernel 是计算密集型,此时运行速度主要看算力,occupancy 够用来隐藏延迟即可
      • 如果 kernel 是带宽密集型,运行速度主要看全局内存带宽
    • 为了拉高 occupancy 往往要牺牲效率
  • 解释 SM、SP、warp、thread block、grid 之间的关系
  • 什么是 SIMT?和 SIMD 的区别是什么?
  • warp 基本执行单元是什么?warp divergence 为什么会降低性能?
  • shared memory、L2 cache、HBM 之间的延迟与带宽差距大概是多少?
  • shared memory 主要用来优化什么场景?有哪些局限?
  • 什么是 kernel launch overhead?哪些情况会放大这个开销?
  • 如何减少频繁 launch 小 kernel 带来的性能损失?
  • 同一个 block 内的线程如何高效通信?
  • 为什么 global memory 访问必须对齐才能高效?
  • 如何在代码中判断 / 定位是否出现了 bank conflict?
  • 动态并行(dynamic parallelism)会带来哪些额外 overhead?
  • 如何通过调整 block size、grid size 来提升 occupancy?
  • 为什么有些 kernel 明明算力没跑满,速度却上不去?
  • warp shuffle 指令的作用是什么?相比 shared memory 有什么优势?
  • L2 cache 对 Transformer 类算子(如 Attention、FFN)有什么影响?
  • HBM 带宽瓶颈通常出现在哪些算子上?如何缓解?
  • 什么是 latency hiding?GPU 是如何通过 warp 调度实现的?
  • 为什么在 FlashAttention 中 shared memory 大小会直接限制分块策略?
  • 寄存器 spilling 是什么?对 kernel 性能有什么影响?

量化

  • GPTQ、AWQ、SmoothQuant、INT8/INT4 权重 / 激活量化、量化对精度 / 速度的影响

  • 什么是权重量化?什么是激活量化?

    • 权重量化
      • 把模型权重(Weight)从 FP32/FP16 映射到低精度整数(如 INT8/INT4)
      • 权重是静态的,推理前就固定,通过量化可以减小模型体积、降低显存占用、加速矩阵乘法
    • 激活量化
      • 把模型推理过程中产生的特征图 / 激活值量化为低精度
      • 激活是动态的,随着输入变化,主要是为了让整个计算链都用整数运算,实现端到端整数推理
      • 量化难度更高,因为激活分布变化大,容易掉精度
  • INT8 量化和 INT4 量化的主要区别是什么?

    对比项INT8 量化INT4 量化
    表示范围256 个级别,精度高16 个级别,精度低
    模型压缩比约为 FP16 的 1/2,FP32 的 1/4约为 FP16 的 1/4,FP32 的 1/8
    速度 / 显存加速明显,显存降低中等显存大幅降低,推理速度极快
    精度损失小,几乎不影响效果大,容易显著掉点,需配合校准 / 微调
    适用场景生产环境、通用部署、追求稳极端资源受限设备、大模型快速推理
  • 对称量化和非对称量化有什么差异?各自适用场景?

    • 对称量化

      xquant=round(xfloatscale)scale=max⁡(∣xfloat∣)max⁡(∣qmin∣,qmax)xfloat=xquant×scale\begin {align*} x_{quant} &= \text{round}\left (\frac {x_{float}}{scale} \right)\\ scale &= \frac {\max (|x_{float}|)}{\max (|q_{min}|, q_{max})}\\ x_{float} &= x_{quant} \times scale \end {align*}
      • 零点为 0,浮点 0 直接映射到整数 0,计算简单,适合权重量化
      • 缺点是对分布不对称的数据(如激活 ReLU 输出全正)利用率低
    • 非对称量化

      xquant=round(xfloatscale+zero_point)scale=xmax−xminqmax−qminzero_point=qmax−xmaxscalexfloat=scale×(xquant−zero_point)\begin {align*} x_{quant} &= \text{round}\left (\frac {x_{float}}{scale} + zero\_point \right)\\ scale &= \frac {x_{max} - x_{min}}{q_{max} - q_{min}}\\ zero\_point &= q_{max} - \frac {x_{max}}{scale}\\ x_{float} &= scale \times (x_{quant} - zero\_point) \end {align*}
      • 零点可任意偏移,能更好地贴合实际数据分布
      • 精度更高,适合激活值,能覆盖非对称分布
      • 缺点是计算多一步减法,推理过程稍复杂
  • 量化中的 scale 和 zero point 分别是什么作用?

    • scale:浮点值域与整数值域的比例系数
    • zero_point:浮点 0 对应的整数值
  • 计算得到的中间值是什么精度?

    • 标准流程
      • INT4 权重、INT8 激活
      • 乘积累加 → INT32 中间结果
      • 再缩放 + 偏移 → 输出为下一层的激活(如 INT8/FP16)
  • 什么是 per-channel 量化?什么是 per-token 量化?

    • per-tensor
      • 整个 tensor 共用一个 scale
      • 但是不同输出通道的数值分布差异极大,为了包住大值,scale 会很大,此时小值通道会被量化成 0,信息完全丢失
    • per-channel
      • 对权重的每个输出通道单独算一组 scale /zero_point
      • 常用于权重量化,计算精度较高,计算开销增加较少
    • per-token
      • 对激活的每个 token(每个位置)单独量化,这是由于大模型里不同 token 的激活分布天差地别
      • 如果整个激活 tensor 共用一个 scale,大值会把 scale 拉得很大,大量小值 token 直接量化崩掉,生成效果烂
      • 主要用于 LLM 激活量化,不同 token 分布差异大,per-token 能显著提升精度
      • 成本较高,推理时动态计算 scale
  • 为什么激活量化比权重量化更难、更容易掉点?

    • 权重是静态固定分布,激活是动态、剧烈变化、极端分布,所以更难量化
    • 权重在训练完成后固定不变,分布相对平滑、均匀、有界,通道之间差异虽大,但 per-channel 就能解决
    • 激活随着输入动态变化,不同句子、不同 token 分布天差地别,经常出现长尾、 outliers(极端大值),经常高度稀疏、高度不对称
    • 激活对异常值极度敏感,同时权重量化误差是静态一次性引入的,而激活量化误差是每层都产生、逐层累积、不断放大
    • 因此激活必须在线动态量化,无法提前遍历所有可能分布,不可能把世界上所有文本都拿来算激活分布,所以激活永远只能实时估算,天生不准
    • 同时,激活是非线性输出,分布高度不均匀,这种分布用对称量化会浪费一半表示空间,用非对称量化又增加计算复杂度,还容易引入偏移误差
    • 而权重分布一般接近对称、近似高斯,很好量化
  • 什么是 PTQ?

    • PTQ 即 Post-Training Quantization,训练后量化
    • 模型已经训练完了,不再反向传播更新参数,只通过少量校准数据 + 简单算法,把权重 / 激活从浮点量化成低精度整数(INT8/INT4)
    • 不需要训练、不需要大量数据、不需要梯度,速度快、成本极低、工程友好,精度一般略低于原浮点模型,但损失可控,是目前大模型落地最主流的量化方式
    • LLM 相关的 PTQ 技术
      • GPTQ
      • AWQ(Activation-aware Weight Quantization)
      • SqueezeLLM
      • ZeroQuant / LLM.int8 ()
      • GGUF/GGML
  • GPTQ 量化的核心思想是什么?主要优化目标是什么?

    • 以 “量化后模型输出尽量不变” 为目标,用二阶信息(海森矩阵)指导权重量化,逐列量化并把量化误差补偿到未量化列,让 INT4 量化几乎无损

    • 不修改模型结构、不重新训练,把权重量化看成一个层输出重构问题,用 H≈XTX 海森矩阵衡量每个权重对输出的重要性

    • 主要步骤

      • GPTQ 采用非对称量化,并逐层进行,以便在进入下一层之前,每一层都能独立处理

      • GPTQ 将层的权重转换为逆海森矩阵(inverse-Hessian),这是模型损失函数的二阶导数,表示模型的输出对每个权重变化的敏感程度

      • H 越大,说明这一列权重越敏感,量化误差影响越大

      • GPTQ 不是一次性量化整个矩阵,而是一列一列量化,即按照 [out_features, in_features] 的 in_features 方向一列一列来

      • 对当前权重列 ww

        • 根据分组(per-group)或 per-channel 计算 scale
        • 量化得到 w^\hat {w}
        • 计算得到量化误差 w=w−w^w=w−\hat {w}
      • 不丢弃误差,而是把误差 “扔” 给后面还没量化的列,抵消掉

        Wremaining=Wremaining−e⋅g⊤W_{\text{remaining}} = W_{\text{remaining}} - e \cdot g^\top
      • 其中 g 由海森矩阵 H 计算得到,用于加权分配误差

      • 这样,量化这一列带来的误差,被后续列抵消,从而避免误差累积爆炸

      • 所有列量化完成后,得到整层 INT4 权重,存储每组 / 每通道的 scale、zero_point

    • 按列逐个量化,量化后的误差直接补偿到剩余列,避免误差累积

    • 优化目标

      min⁡W^∥(W−W^)X∥22\min_{\hat {W}} \| (W - \hat {W}) X \|_2^2
      • 最小化量化前后层输出的差异,而不是简单最小化权重本身的误差
      • 最终目标:INT4 下仍保持接近 FP16 的生成效果
  • GPTQ 是静态量化还是动态量化?支持在线推理吗?

    • GPTQ 是典型的静态量化,scale、zero_point 都是离线一次性算好
    • 在进行推理时,不再重新计算,不关心输入,属于 weight-only static quantization
    • 进行在线推理时,INT4 权重存储 → 算子里反量化为 FP16 → 正常矩阵乘
  • AWQ 相比 GPTQ 改进了什么?为什么精度更高?

    • GPTQ 只关注权重误差;AWQ 关注 “激活敏感的权重”,对重要权重保护,不强行量化
    • 引入激活感知:GPTQ 不看激活大小,只看权重对输出的二阶影响;而 AWQ 显式统计激活,知道哪些通道的激活大、对结果关键
    • 保护重要通道(channel-wise protection):对激活大、贡献大的通道,保留高精度(FP16),只对不重要通道量化,从而整体精度更高
    • 速度更快,不需要像 GPTQ 那样逐列精细优化,量化速度快很多
    • 对异常值更鲁棒,LLM 激活里的 outliers 不会轻易毁掉整层量化
  • AWQ 的激活感知权重量化具体在做什么?

    • 根据激活值,找出重要的通道,即运行少量校准数据,统计每个输入通道的激活幅度

      si=mean(∣Xi∣)s_i = \text{mean}(|X_i|)
    • 激活越大,说明这个通道对模型输出越关键

    • 不是均匀量化所有权重,而是根据激活重要性做 “非均匀混合精度量化”

  • 动态量化和静态量化的区别?GPTQ 和 AWQ 是什么类型的?

    • 静态量化:离线提前算好 scale、zero_point,用一小批校准数据跑前向,统计分布,推理时不再变化、不再计算,和输入无关,如 GPTQ、AWQ
    • 动态量化:推理时才计算 scale /zero_point,根据当前真实输入实时统计最大最小值,每次输入不一样,量化参数也不一样,能够适配各种输入分布
    • 静态量化对分布变化大的激活不够精准,而动态量化则在推理时多一步统计开销,硬件不友好,速度慢
  • 什么是 GGUF/GGML?

    • GGUF/GGML 是为 CPU 设计的量化格式,不是为了极致精度,而是为了小体积 + CPU 跑得快
  • SmoothQuant 解决了什么问题?原理是什么?

    • LLM 量化的难点

      • 权重 W:分布均匀、容易量化(INT4/INT8 都稳)
      • 激活 X:存在固定通道的巨大离群点(比正常值大 101000 倍),直接量化会把 scale 拉得极大,大部分值只有 23 个有效量化级,精度崩掉
    • 传统方案

      • 只量化权重(W4A16):如 GPTQ/AWQ → 内存减半,但激活还是 FP16,计算不加速
      • 强行 W8A8:精度掉太多,没法用
      • LLM.int8 ():把离群点用 FP16 算 → 硬件不友好、速度慢
    • SmoothQuant 的目标是用数学等价变换,让激活和权重都能稳定 INT8 量化(W8A8),且兼容硬件 INT8 GEMM

    • SmoothQuant 引入逐通道缩放因子 s(向量):

      Y=X⋅diag(s)−1⏟X^⋅diag(s)⋅W⏟W^=X^⋅W^Y = \underbrace {X \cdot \text{diag}(s)^{-1}}_{\hat {X}} \cdot \underbrace {\text{diag}(s) \cdot W}_{\hat {W}} = \hat {X} \cdot \hat {W}
      • 激活侧:X 除以 s → 把大离群点 “压平”(平滑)
      • 权重侧:W 乘以 s → 难度转移到权重(权重本来就好量化)
      • 数学完全等价,输出 Y 不变
    • 如何计算平滑因子 s

      • 用校准数据(几十~几百条)离线统计激活每通道最大绝对值:∣X∣max|X|_{\text{max}} 和权重对应输入通道最大绝对值:∣W∣max|W|_{\text{max}}

      • 平滑因子计算公式

        si=(∣Xi∣max)α/(∣Wi∣max)1−αs_i = \left (|X_i|_{\text{max}}\right)^\alpha \bigg/ \left (|W_i|_{\text{max}}\right)^{1-\alpha}
      • α:平衡系数(通常 0.5~0.8)

        • α=1:只缩激活 → 回到传统量化
        • α=0:只缩权重 → 权重变大,但激活完全平滑
      • 目标:让 X^\hat {X} 和 W^\hat {W} 的动态范围尽量均衡、都适合 INT8

  • SmoothQuant 为什么能同时稳定权重和激活量化?为什么它比直接剪异常值、动态量化更好?

    • 剪去异常值会丢失信息,而动态量化则推理速度慢,硬件不友好
    • 而 SmoothQuant 则离线静态量化,推理零开销,完全等价
  • 什么是量化感知训练 QAT?和 PTQ 有什么区别?

    • QAT 即 Quantization-Aware Training,量化感知训练,模拟量化噪声再微调,精度高但麻烦

    • QAT 通过在模型训练 / 微调阶段,前向传播里模拟量化噪声,反向传播正常更新权重,让模型 “习惯” 量化误差,最终推理时量化几乎不掉点

    • 在训练过程中,QAT 会引入所谓的伪量化(fake quants):首先将权重量化为如 INT4,然后再反量化为 FP32

      • 首先,找到输入数据的分布,即找到 MIN 和 MAX 值

      • 对于每一个算子,量化参数通过以下公式得到:

        • 缩放因子 S:

          S=Rmax−RminQmax−QminS = \frac {R_{max} - R_{min}}{Q_{max} - Q_{min}}
        • 零点偏移 Z:

          Z=Qmax−RmaxSZ = Q_{max} - \frac {R_{max}}{S}
        • 量化值 Q:

          Q=RS+ZQ = \frac {R}{S} + Z
        • Rmin,Rmax:原始浮点数据的最小 / 最大值

        • Qmin,Qmax:目标量化格式的最小 / 最大值(如 INT8 为 -128 和 127)

        • R:原始浮点数据

        • S:缩放因子,用于将浮点范围映射到整数范围

        • Z:零点偏移,用于对齐浮点零点与整数零点

      • FakeQuant 量化和反量化的完整过程可表示为:

        Q(x)=FakeQuant(x)=DeQuant(Quant(x))=s∗(Clamp(round(x/s)−z)+z)\begin {align*} Q (x) &= \text{FakeQuant}(x) \\ &= \text{DeQuant}(\text{Quant}(x)) \\ &= s * \left (\text{Clamp}(\text{round}(x/s) - z) + z \right) \end {align*}
      • 量化:将浮点输入 x 除以缩放因子 s,四舍五入为整数,再减去零点 z,并通过 Clamp 限制在整数范围内

      • 反量化:将量化后的整数加上零点 z,再乘以缩放因子 s,恢复为浮点数据

      • 这一过程能够模拟量化到低比特操作的时候的精度损失,把该损失作用到网络模型中,传递给损失函数,让优化器去在训练过程中对该损失值进行优化

    • 反向传播

      • 在反向传播过程中,模型需要计算损失函数相对于每个权重和输入的梯度,梯度通过 FakeQuant 节点进行传递,这些节点将量化误差反映到梯度计算中,模型参数的更新因此包含了量化误差的影响,使模型更适应量化后的部署环境

      • 量化操作里有 round、clamp,都是不可导 / 梯度为 0 的操作

      • 按照前向传播的公式,因为量化后的权重是离散的,反向传播的时候对 W 求导数为 0:

        ∂Q(W)∂W=0\frac {\partial Q (W)}{\partial W} = 0
      • 因为梯度为 0,所以网络学习不到任何内容,权重 W 也不会更新:

        gW=∂L∂W=∂L∂Q(W)⋅∂Q(W)∂W=0g_W = \frac {\partial L}{\partial W} = \frac {\partial L}{\partial Q (W)} \cdot \frac {\partial Q (W)}{\partial W} = 0
      • 可以使用直通估计器(Straight-Through Estimator,简 STE)简单地将梯度通过量化层传递,近似来计算梯度,这使得模型能够在前向传播中进行量化模拟,但在反向传播中仍然更新高精度的浮点参数

      • STE 近似假设量化操作的梯度为 1,从而允许梯度直接通过量化节点:

        gW=∂L∂W=∂L∂Q(W)g_W = \frac {\partial L}{\partial W} = \frac {\partial L}{\partial Q (W)}
      • 如果被量化的值在 [xmin,xmax][x_{min},x_{max}] 范围内,STE 近似的结果为 1,否则为 0

      • 这种方法使模型能够在训练期间适应量化噪声,从而在实际部署时能够更好地处理量化误差

    • 假设在反向传播过程中没有进行量化,会根据梯度下降选择损失最小的权重,然而,如果该权重位于狭窄的极小值点,会导致更大的量化误差,因此 QAT 试图探索更宽的极小值点对应的损失曲面,来最小化量化误差

    • PTQ 仅简单将浮点权重压缩为低比特,而浮点模型通常收敛于狭窄尖锐的极小值点,轻微的量化扰动就会导致损失急剧上升

    • QAT 则在训练过程中主动寻找更宽、更平坦的极小值点,使模型对权重扰动和量化误差更不敏感,因此量化后的损失远低于 PTQ

  • 哪些量化方法只量化权重?哪些同时量化激活?

    • 只量化权重(Weight-only Quantization):GPTQ、AWQ、GGUF/GGML、SqueezeLLM
    • 同时量化权重 + 激活(W&A Quantization):SmoothQuant、LLM.int8 ()、QAT、W8A8 全量化
  • 量化在什么情况下能提升推理速度?什么情况下反而变慢?

    • 提升推理速度
      • 权重 + 激活都量化(W8A8):GEMM 完全在整数域进行,访存和计算双重加速
      • 算子高度优化:专用 fused kernel 支持 INT4/INT8,访存效率显著提升
    • 变慢的情况
      • 只量化权重,不量化激活(W4A16 / W8A16):推理时权重需要实时反量化回 FP16,多了一步解量化开销,同时计算仍在 FP16,没有计算加速
      • 动态量化:需要根据输入实时计算 scale,引入大量额外计算
  • 为什么量化后模型在长文本下精度下降更明显?量化引入的误差为什么会在自回归生成中累积放大?

    • 长文本会让激活的动态范围更大、outlier 更多、量化误差逐层累积更严重,所以精度掉得比短文本更多
  • KV Cache 量化(INT8/INT4)和权重量化有什么不同?

  • 量化对 attention 以及 FFN 影响有什么不一样?

    • Attention 对量化误差更敏感,更容易崩,尤其是 INT4,这是因为 Attention 的核心是内积相似度,Q/K 微小的量化误差都会导致点积明显偏移,而 Softmax 会进一步放大微小差异
    • FFN 更耐量化,INT4 基本无损,因为 FFN 的本质是高维特征转换,只需要大致正确的赋值,量化带来的小幅噪声会被 GeLU/SiLU 平滑掉;FFN 的参数量也较大,分布平滑、无极端异常通道
  • 大模型推理中,GPTQ/AWQ/SmoothQuant 该如何选型?

    维度GPTQAWQSmoothQuant
    核心思想Hessian 感知,逐层误差补偿,优先量化影响小的权重激活感知,识别重要权重并保护,重排 / 缩放平滑因子转移量化难度,W+A 联合量化
    量化对象权重(W)权重(W)权重 + 激活(W+A)
    位宽支持INT4/3/2(极致压缩)INT4(主流)INT8(W8A8)
    校准需求中(512-2048 样本)低(128-512 样本,更快)低(轻量统计)
    精度表现4bit 下 PPL 损失最小(~<1%)4bit 稳定,略逊 GPTQ(~1.5%)W8A8 几乎无掉点
    推理速度中(反量化开销)中(反量化开销,AWQ 略快 GPTQ)最快(INT8 硬件加速,TensorRT 最优)
    部署栈AutoGPTQ、llama.cpp、GGUFAutoAWQ、llama.cpp、GGUFTensorRT、FasterTransformer、ONNX
    适用场景极限压缩、长文本、追求极致精度小样本 / 跨域、快速量化、稳定部署生产级 INT8、最大化硬件吞吐
这篇文档有帮助吗?

On this page

页面信息