跳转至

推理加速技术

算子融合是什么?Transformer 中哪些操作可以融合?如线性层+激活,注意力融合。

算子融合是指将原本会被拆分成多个独立内核(kernel)执行的计算操作,在 GPU 上合并成一个内核来完成。这样做可以大幅减少全局内存(HBM)的中间结果读写和内核启动开销。由于 GPU 计算速度远高于内存带宽,融合是提升实际性能的关键手段。

在 Transformer 架构中,可以融合的典型操作有:

  • 线性层 + 激活函数:MatMul 的结果直接在寄存器/共享内存中应用 GELU、ReLU、SiLU 等,避免把大矩阵写回 HBM 再读回。

  • 线性层 + 偏置 + 激活:将 MatMulAddBiasActivation 三者一次完成。

  • QKV 投影融合:将自注意力中 Q、K、V 三个线性层权重在内存中拼接,变成一次大矩阵乘法,直接产生拼接后的 QKV 张量,然后通过视图拆分。

  • 注意力计算融合:FlashAttention 将 QK^T、softmax、乘以 V 甚至 mask、dropout 全部融合成极少数内核,内部通过分块和 online softmax 完成,完全不生成完整的 N×N 注意力矩阵。

  • LayerNorm / RMSNorm + 后续线性层:对归一化输出直接进行矩阵乘,省去一次写回。

  • 残差连接 + LayerNorm:将 Add 和 LayerNorm 融合为一个内核。

  • Logits + Softmax + CrossEntropy(训练时):把最后的线性层、softmax 和 loss 计算融合,减少内存占用。

  • 量化/反量化与计算的融合:例如权重 INT8/FP8 加载后即时反量化并执行 MatMul,无需存储完整高精度中间张量。

  • MLP 块整体融合:如将 gate_projup_proj 后的激活和与 down_proj 之间的乘法合并。

通过算子融合,一个 Transformer 块中原来几十个小内核可以被压缩到个位数,大幅削减带宽瓶颈。


FlashAttention 的核心思想是什么?如何通过 IO 感知和分块计算减少显存读写?

核心思想:标准注意力需要计算并存储一个大小为 N×N 的注意力得分矩阵(N 为序列长度),这导致对 GPU 高带宽内存(HBM)的读写复杂度为 O(N²)。FlashAttention 提出一种 IO 感知(IO-aware) 的算法,充分利用 GPU 内存层次(小容量高带宽的 SRAM 和大容量低带宽的 HBM),通过 分块(tiling) 计算注意力,把主要的中间结果留在 SRAM 中,并将 HBM 读写量从 O(N²) 降到 O(N²·d²/M) 级别(d 为头维度,M 为 SRAM 大小),实际效果接近线性。

IO 感知与分块计算的具体做法:

  • 将 Q 分成若干块,K、V 也分成若干块。每一对 Q 块和 K 块的计算结果只暂存在 SRAM 中,不写回 HBM。

  • 使用 online softmax 技术维护局部分母和最大值,通过递推公式逐步修正之前的 softmax 缩放。这样在遍历 K、V 块时可以一边计算局部注意力权重、一边更新输出,而无需保存完整的 N×N 注意力矩阵。

  • 前向只输出最后的 attention output(形状 N×d),反向时通过重计算注意力矩阵节省存储,无需缓存大规模中间结果。

  • 所有分块大小严格按照 SRAM 容量选取,确保每一步的内存访问最优。

这使得 FlashAttention 在长序列场景下内存占用和耗时都远低于标准实现。


FlashAttention-2 相比 FlashAttention 做了哪些改进?为什么能进一步提速?

FlashAttention-2 主要在 并行策略、工作调度 和 减少非矩阵乘操作 三方面做了优化,具体包括:

  • 更优的并行化:FlashAttention-1 的并行主要集中在 batch 和 head 维度,序列长度方向的并行度不够。FlashAttention-2 在 Q 的块序列维度上也引入并行,使单个 attention head 的计算能利用更多线程块,提升 GPU 占用率。

  • 减少非矩阵乘计算占比:调整了 online softmax 的循环顺序(外层循环改为 Q 的块),减少了 rescale 操作的调用频率和寄存器压力,使矩阵乘法(Tensor Core 擅长)的比例大幅提升。

  • 更精细的 warp 调度:重新设计了线程块和 warp 的映射关系,让数据复用更充分,同步开销更小,减少了共享内存的 bank conflict。

  • 去除冗余操作:简化了 softmax 统计量(max、sum)的更新流程,前向计算中减少了不必要的乘法和加法。

由于这些改动,FlashAttention-2 在大规模注意力计算时能将 Tensor Core 的利用率推得更高,相比于第一版有约 2 倍的加速。


FlashAttention-3 在 H100 GPU 上利用了哪些新特性?异步和低精度方面有何优化?

FlashAttention-3 充分利用 NVIDIA Hopper 架构(H100/H200)新增的硬件能力,将异步与低精度做到极致:

异步优化:

  • TMA(Tensor Memory Accelerator):用于在全局内存和共享内存之间异步拷贝数据块,完全由硬件完成地址生成和传输,释放计算单元的指令发射和寄存器资源。

  • WGMMA(Warp Group MMA):新的异步矩阵乘加指令,可以在一个 warp group 内直接对共享内存和寄存器中的数据进行矩阵乘,同时允许其他 warp group 执行 TMA 拷贝或别的计算,实现了 计算与数据搬运的完全流水线重叠。

  • 生产者-消费者 warp 特化:一部分 warp 专门负责用 TMA 搬运数据,另一部分 warp 专门负责 WGMMA 计算。通过异步 barrier 同步,内存延迟几乎被完全隐藏。

低精度优化:

  • 支持 FP8 数据格式(E4M3/E5M2),在 QK^T 和 PV 等计算中使用 FP8 乘加,大幅度提升计算吞吐(相比 FP16/BF16 理论翻倍)并降低带宽需求。

  • 采用块状量化(block-wise quantization)的缩放因子,对每个分块独立缩放,兼顾 FP8 的动态范围和精度。

  • 在 FP16/BF16 路径下同样利用 TMA 和 WGMMA,相对 FlashAttention-2 仍有 1.5–2 倍提速。

综上,FlashAttention-3 通过软硬件协同设计,将 H100 的硬件特性发挥到接近极限。


投机采样(Speculative Decoding)的原理是什么?草稿模型和大模型是如何协作的?

自回归生成中,大模型(目标模型)每次只能串行生成一个 token,造成高延迟。投机采样 通过引入一个轻量的 草稿模型(draft model),一次生成多个候选 token,再由大模型并行验证,大幅减少大模型的串行调用次数。

协作流程:

  1. 草稿生成:给定相同的前缀,草稿模型自回归生成 K 个候选 token(如 5–10 个),速度快但精度略低。

  2. 并行验证:大模型一次性接收“前缀 + K 个草稿 token”作为输入,经过一次前向计算得到这 K 个位置上的完整概率分布(以及一个额外的第 K+1 个分布)。

  3. 接受/拒绝判断:从左到右逐个位置比较草稿 token 与大模型分布。如果草稿 token 的生成概率不低于大模型,按概率接受;一旦出现拒绝,则根据修正分布采样一个新 token 替换,并丢弃后续所有草稿 token。

  4. 更新前缀:将接受的 token 和新采样的 token 拼接到原序列后面,形成新前缀,重复以上过程。

通过这种方式,大模型一次前向可能产生多个 token,在草稿模型接受率足够高时,有效解码吞吐成倍提升。


投机采样在什么条件下能带来显著的吞吐提升?草稿模型的准确率要求多高?

要获得显著吞吐提升,需要同时满足:

  • 大模型本身是自回归解码带宽瓶颈:如果大模型的小 batch 推理已经能跑满计算(如长序列 prefill 阶段),投机采样的收益就有限;当大模型推理处于内存带宽受限(大批次解码),加速效果才明显。

  • 草稿模型足够快:草稿模型单 token 生成延迟远低于大模型,并且生成 K 个 token 的总时间应小于大模型一次前向的时间,否则草稿阶段变成瓶颈。

  • 草稿模型接受率高:这是决定性因素。接受率 α 决定了平均每轮接受的 token 数。理论期望接受长度为 (1 - α^(K+1)) / (1 - α)。要获得>1 的实际加速,通常需要 α > 0.7~0.8,若要获得 2 倍以上加速,α 往往需要达到 0.9 以上。

  • 草稿长度 K 选择合适:K 太大会导致末尾 token 拒绝概率增大、浪费计算;K 太小则并行收益有限。需要根据接受率和延迟模型折中。

简言之,高接受率(通常 80%–90% 以上)是投机采样成功的前提。


如何设计一个高效的草稿模型?可以用原模型剪枝而来吗?

设计高效草稿模型的核心是 最大化与目标模型的输出分布相似性,同时保持极低推理成本。常见方法有:

  • 独立小模型:相同 tokenizer,更少层数、更小隐藏维度(如 TinyLLaMA)。可通过蒸馏使分布对齐。

  • 原模型剪枝 + 蒸馏:完全可以。通过结构化剪枝(去除某些层、注意力头或隐藏维度)得到一个子网络,再用目标模型的 logits 做知识蒸馏微调,能很好保持行为一致性,是高效的方案。

  • 提前退出(Early Exit):直接复用大模型的前若干层作为草稿模型,在某一中间层接一个预测头生成 token。如 EAGLE 等方法还结合了特征外推。

  • 多预测头:如 Medusa,在大模型顶部附加多个独立分类头,同时预测未来若干个 token,可视为一种“无草稿模型”的投机变体,但本质类似。

  • 蒸馏 + 量化:将剪枝或缩小后的模型进一步做 INT8/INT4 量化,使其在端侧或低算力设备上也能极快运行。

剪枝而来的草稿模型因为共享大部分参数初始化和架构,通常能获得很高的接受率,在实践中被广泛使用。


Continuous Batching(动态批处理)与传统的 Static Batching 有什么不同?为什么能提升 GPU 利用率?

Static Batching(静态批处理):一次收集一批请求,同时进行完整生成,必须等 所有请求生成结束 才返回结果。这导致:

  • 短序列被长序列严重阻塞(队头阻塞),延迟大幅增加。

  • 若填充(padding)到统一最大长度,会浪费大量计算和显存。

  • GPU 在批次末尾部分请求已完成时处于空闲。

Continuous Batching(连续/动态批处理):将推理过程拆分为 逐个 token 迭代,每次迭代只计算当前活跃请求的下一个 token。完成生成的请求立即移出批次,新到达的请求马上插入。其优势:

  • GPU 持续满载,没有“等最长请求”的空闲时间。

  • 短请求能迅速完成,延迟显著降低;长请求也无需单独等待。

  • 通过 KV cache 的动态管理,不同长度请求共存,无需过量填充,显存和计算资源利用率极高。

因此,在在线服务中,Continuous Batching 成为高吞吐低延迟的标准方案。


在 Continuous Batching 中,如何处理不同序列长度和生成状态的请求?

Continuous Batching 需要高效管理大量可变长度的序列及其 KV cache。核心方法:

  • PagedAttention(vLLM 提出):将每个序列的 KV cache 划分为固定大小的 内存块(block),块之间通过页表映射,可以不连续存储。这类似于操作系统的虚拟内存:
  • 新请求分配少量初始块。
  • 每生成新 token,若当前块满,则分配一个新块。
  • 完成生成后释放所有块。
  • 因为块大小固定,完全消除了因序列变长而预留超大连续内存的浪费,显存碎片率极低。

  • 批次的动态拼装:调度器在每次迭代时,从活跃请求池中选择一组请求构成 batch。由于每个序列长度不同,注意力计算需根据页表分别取用各自的 KV 块。内核层面通过传入块表(block table)实现可变长度注意力。

  • 状态机管理:每个请求维护状态(prefill 阶段 / decode 阶段 / 完成)。prefill 可以单独调度或与 decode 混合,混合时需处理计算不均衡。

  • Swap(换入换出):当显存紧张时,可以将部分请求的 KV 块换出到 CPU 内存,等 GPU 空闲时再换回,实现优先级抢占和过载保护。

通过以上机制,不同长度、不同生成阶段的请求可以在一个批次中无冲突地高效执行。


比较 vLLM、TensorRT-LLM 和 llama.cpp 的适用场景和核心优化技术。

查看内嵌表格

简评:vLLM 在易用和高吞吐间取得良好平衡;TensorRT-LLM 性能天花板最高,但使用门槛较高;llama.cpp 则是端侧推理的事实标准。


TensorRT-LLM 是如何对 Transformer 图进行编译优化的?使用了哪些技术?

TensorRT-LLM 将 PyTorch 等导出的模型转换为 TensorRT 计算图,并进行多层深度优化:

  • 图级优化:
  • 垂直融合:将逐点操作(激活、LayerNorm、残差相加等)与矩阵乘融合成单一内核。
  • 水平融合:将 QKV 矩阵乘合并为一次大矩阵乘。
  • 消除冗余操作:常量折叠、转置消除、reshape 优化等。

  • 内存优化:引入类似 PagedAttention 的 GptManager 管理 KV cache,使用分块池化存储;为中间张量分配最小所需内存并做显存复用。

  • 量化:支持 FP8、INT8、INT4(GPTQ/AWQ)。量化节点自动插入 Q/DQ 算子,通过校准实现最小精度损失。

  • 动态形状:利用 TensorRT 的 dynamic shape 能力处理可变序列长度和批次大小,在多次运行间无重新编译。

  • 分布式推理:自动插入 NCCL all-reduce/all-gather 通信内核,实现张量并行和流水线并行。

  • 内核生成:基于 CUTLASS 等库自动生成性能最优的 GEMM/卷积内核,并集成 FlashAttention。

  • Runtime 调度:In-flight Batching 支持,由 C++ runtime 管理请求调度和动态批处理。

通过图编译 + 手工/自动调优的内核,TensorRT-LLM 能针对特定 GPU 生成接近峰值性能的推理引擎。


对于端侧推理,如 llama.cpp 使用的 GGUF 格式和量化方案有什么特点?

GGUF(GPT-Generated Unified Format) 是一种自包含的模型文件格式,为高效端侧推理设计:

  • 单文件自描述:包含模型超参数、词表、权重和元数据,以键值对形式灵活扩展,向前兼容。

  • 内存映射(mmap):支持直接映射到虚拟内存,实现零拷贝加载和按需读取,加载速度极快且节省内存。

  • 丰富的量化类型:核心是 块状量化(block-wise quantization)。权重被分为若干块,每块独立拥有缩放因子(有时还有最小值),代表性的有:

  • Q4_0, Q4_1:将 32 个权重量化为 4-bit,共享一个 FP16/FP32 缩放因子。
  • Q4_K_M, Q5_K_M 等 K-quants:采用 超块(super-block) 结构,内部小块有更细粒度的缩放和最小值,且对参数张量的不同维度(如 token embedding 和 attention 权重)使用不同比特宽度,在模型体积和精度间取得更好平衡。

  • 面向 CPU 整型计算优化:量化后的推理使用整数乘加、移位等指令,配合 AVX2、AVX-512、NEON 等 SIMD 指令集,无需浮点单元。这使得在无 GPU 的笔记本甚至手机上都能流畅运行十亿参数级模型。

GGUF 量化方案本质是在极低算力硬件上,用带宽和容量换取可用的模型精度。


如何对扩散模型进行推理加速?与 LLM 的推理优化有何异同?

扩散模型加速方法:

  • 减少采样步数:使用高效数值求解器(DDIM、DPM-Solver、Euler、Heun),或通过知识蒸馏、渐进蒸馏、一致性模型(LCM)将步数压缩至极少(1~4 步)。

  • 模型压缩:结构剪枝、低秩分解、INT8/FP8 量化。

  • 算子融合与高效注意力:在 U-Net 或 Diffusion Transformer(DiT)中融合卷积+归一化+激活,使用 FlashAttention 加速自注意力/交叉注意力。

  • 缓存复用:如 DeepCache 跳过部分 U-Net 层,对某些步复用浅层特征;或对条件嵌入缓存。

  • 图编译:利用 TensorRT、AITemplate 等对扩散模型进行端到端编译优化。

  • 批处理:对于离线任务,可大批量并行生成图像。

与 LLM 推理优化的异同:

  • 相同点:都依赖算子融合、量化、图编译、FlashAttention 等;都面临大模型内存带宽挑战;都可利用蒸馏和小模型。

  • 不同点:

  • 推理范式:LLM 是自回归序列生成,存在 KV cache 和可变长度难题,主要瓶颈在解码;扩散模型是固定张量尺寸的多步迭代去噪,无 KV cache,但每步需处理全图,计算更密集。
  • 延迟关键点:扩散模型更侧重 单步延迟 和 总步数,用户感知到的是首图生成时间;LLM 侧重首 token 延迟和 token 生成速率(吞吐)。
  • 批处理机制:扩散模型可简单进行图批处理,LLM 需要 Continuous Batching 应对可变长度。
  • 加速技巧:扩散模型可通过大幅减少步数取得几十倍加速;LLM 的步数无法压缩,只能用投机采样变相提升每次前向的有效 token 数。

在推理服务中,如何做请求调度和优先级抢占以保障 SLA?

推理服务保障延迟与吞吐的 SLA,通常使用以下策略:

  • 优先级队列与抢占:
  • 请求按优先级分入不同队列(如实时交互、批量离线)。高优先级请求可 抢占 低优先级请求的 GPU 资源。
  • 抢占时,通常将低优先级请求的 KV cache 块换出(swap out)到 CPU 内存,释放显存给高优先级请求;完成后换回继续生成。vLLM 等框架已实现这种暂停/恢复机制。

  • 调度策略:

  • 截止时间最早优先(Earliest Deadline First):估算每个请求剩余生成时间,优先调度 SLA 压力大的请求,降低违约率。
  • 公平加权调度:防止饥饿,确保低优先级也有基础保证。

  • 迭代级延迟控制:

  • 动态限制每次迭代的批次总 token 数(batch token budget),因为迭代耗时正比于批次内 token 总数和最长序列长度。通过控制批次大小,可保证每次迭代不超过延迟上限。
  • 如果检测到迭代耗时可能超标,则推迟部分请求到下个迭代,或换出/暂停部分请求。

  • 分离式服务路由:

  • 部署多个模型实例,将短交互请求和长文档请求路由到不同实例,避免相互干扰。
  • 为高优先级用户预留专有实例。

  • 过载保护:

  • 请求超过阈值时,拒绝新请求或将其排队,优先保障已有请求 SLA。
  • 对等待队列也应用优先级和超时丢弃策略。

通过这些机制,推理服务能够在高并发下稳定地满足不同等级用户的延迟要求,实现资源利用率和 QoS 的平衡。

投机采样中,如果草稿模型生成的序列经常被大模型拒绝,效率会急剧下降,如何提高接受率?

当接受率过低时,大模型的大部分验证计算都被浪费在无效 token 上,加速比会小于 1。提高接受率的本质是让草稿模型的输出分布尽可能与大模型对齐。主要方法如下:

  • 通过知识蒸馏训练草稿模型:使用大模型作为教师,用其输出的完整概率分布(软标签)训练草稿模型,而不仅仅是硬标签。这能让草稿模型学习到大模型预测中的微小概率差异,显著提升分布相似度。

  • 结构对齐与剪枝:草稿模型最好与大模型同架构、同词表,可使用原始大模型进行结构化剪枝+蒸馏得到。由于共享参数基础,行为天然接近,接受率通常能达到 90% 以上。

  • 使用自投机采样(Self-Speculative Decoding):直接复用大模型的前几层作为草稿,几乎零额外模型代价,接受率极高(后续问题详述)。

  • 多头预测(如 Medusa):在大模型最后一层附加多个独立分类头,同时预测未来数个 token,不依赖独立的草稿模型,且这些头可通过蒸馏在冻结大模型参数的情况下训练,分布易于对齐,整体接受率高。

  • 动态调整采样参数:推理时适当降低草稿模型的温度(使分布更锐利),或对草稿采样使用 top-k/top-p 缩小候选集,可减少意外低概率 token 的产生,增加大模型接受的可能性。也可采用严格的确定性接受(只接受概率最高的草稿 token,若大模型的首选 token 与之相同则接受),这在牺牲一定多样性的情况下能稳住接受率。

  • 并行草稿与树验证(Speculative Tree):由草稿模型在每个位置生成多个候选 token,构成一棵候选树,大模型通过树注意力(Tree Attention)并行验证多条路径。这显著增大了每轮至少有一条路径被大量接受的概率,有效提高吞吐,即使单路接受率中等也能获得较好加速。

  • 动态草稿长度(K)调整:实时监测近期接受率,若连续出现低接受率,就缩短草稿长度 K,减少浪费;接受率恢复后再增长,以求在平均意义上最大化有效 token 数。


能否用前几层的输出来充当浅层草稿模型,实现无需额外模型的“自投机采样”?

完全可以,这就是自投机采样(Self-Speculative Decoding)或提前退出投机(Draft & Verify with Early Exit)的核心思想。其做法是:

  • 在原大模型的某一中间层(例如总层数的 1/3 或 1/2 处)添加一个轻量草稿预测头(通常就是复用原始词表分类头,或一个极小的线性层),仅凭前几层计算出的隐藏状态就可以生成下一个 token 的概率分布。

  • 草稿阶段:输入前缀先只通过前 L 层,用该预测头自回归生成 K 个草稿 token。由于层数远少于完整模型,单 token 延迟极低。

  • 验证阶段:将“前缀 + K 个草稿 token”一并送入完整大模型进行并行验证。为了进一步加速,可复用草稿阶段已计算的中间层隐藏状态,验证时直接从第 L+1 层开始执行,避免重复计算前缀的浅层。

  • 接受或拒绝的机制与标准投机采样完全相同。

这种方法的优势非常突出:

  • 无需单独维护草稿模型,节省显存和部署复杂度。

  • 接受率极高(通常在 95% 以上),因为草稿头与主体模型共享绝大多数参数,行为高度一致。

  • 典型实现如 EAGLE 系列,甚至不只用浅层输出,还利用大模型倒数第二层的特征进行外推预测,进一步提升了预测准确性。


Continuous Batching 如何与 PagedAttention 结合,实现高吞吐和低延迟?

Continuous Batching 负责调度,PagedAttention 负责内存管理,二者结合形成了现代 LLM 推理服务的核心范式。

结合机制:

  • 动态批次构成:Continuous Batching 在每次迭代(生成一个 token)时,从请求池中选择一组活跃请求构成 batch,而不必等待所有请求结束。当一个请求完成生成,立即将其移出;当有新请求到达,立即在下一次迭代中插入。

  • 非连续 KV Cache 分配:PagedAttention 把每个请求的 KV cache 划分为固定大小的物理块(如 16 个 token 一块),并通过页表进行逻辑到物理的映射。请求刚加入时只分配少量块(如对应其 prompt 长度),之后每生成一个新 token,若当前块已满,就动态申请一个新块。请求结束后立刻回收所有块,完全消除内部碎片和提前预留大块连续内存的浪费。

  • 无 padding 的异构批处理:因为每个请求拥有独立的页表,同一批次内序列的长度可以完全不同。注意力内核通过传入每个请求的块表,只读取实际有效的 KV 块,无需为对齐长度而进行 padding 计算。这从根本上消除了无效计算和显存浪费。

  • 抢占与过载保护:当需调度高优先级请求而显存不足时,可把某些低优先级请求的 KV 块从 GPU 换出(swap out)到 CPU 内存,释放出空间;之后再换回继续生成。该过程由 Continuous Batching 调度器与 PagedAttention 的内存管理器协同完成。

综合效果:GPU 始终保持满载,等待时间趋近于零;显存利用率可达 90% 以上;新请求几乎没有排队延迟,短请求可迅速完成并释放资源,系统性实现了高吞吐与低延迟的统一。


FlashDecoding 与 FlashAttention 是什么关系?它专门解决什么问题?

关系:FlashDecoding 是 FlashAttention 思想的推理专用扩展。两者都基于“在 SRAM 中分块计算 softmax 并缩减”的 IO 感知理念,但针对的是 Transformer 不同阶段的并行瓶颈。

  • FlashAttention 设计时主要优化训练和 prefill 阶段。此时通常有大量的 query token(序列长度 N 很大),并行度充足(沿 batch、head 和 query 序列维并行),它能把 QK^T 的计算做得非常高效。

  • 推理的解码阶段(特别是 Continuous Batching 中),每次迭代常只有一个新 query token(当前生成的 token),而 KV cache 长度却可能很长(如几千到上百万)。此时 FlashAttention 的并行策略暴露了问题:workload 集中在 batch 和 head 维度,序列长度维度上并行度几乎为零,一个 thread block 就要处理完整个 KV 序列,导致长序列时 GPU 大量流处理器闲置,延迟恶化。

FlashDecoding 专门解决的问题:

  • 它将长 KV 序列的注意力计算在序列长度维度上进行并行拆分:用不同的 thread block 分别计算不同 KV 分块的点积和局部 softmax,然后通过一个高效的跨块归约(reduction)内核合并最终结果。

  • 这样,即使 batch size 为 1、query token 只有一个,也能通过“拆分 KV 并多 block 并行”充分利用 GPU 的并行算力,将长序列解码的延迟降低一个数量级。

简言之,FlashAttention 让 attention 在 SRAM 完成,FlashDecoding 让解码时的 attention 也拥有巨大的并行度。现代推理引擎(如 FlashAttention-3、TensorRT-LLM)通常将二者整合,prefill 用 FlashAttention 的高并行分块,decode 用 FlashDecoding 的长 KV 拆分归约。


推理服务中,如何根据请求的紧急程度和预估长度,动态调度 batch 内序列?

动态调度器在每次迭代前选取进入 batch 的序列集合,其目标是在满足差异化 SLA 的前提下最大化吞吐。核心策略:

  • 多优先级抢占队列:为每个请求赋予紧急度等级(如实时交互 > 批量分析)。高优先级请求到来时,若 GPU 显存不足,可通过 PagedAttention 的 swap 机制将低优先级请求的部分 KV 块换出,立即腾出空间进行调度。

  • 基于预估长度的预期剩余时间建模:利用小型预测模型或启发式规则(如输入 prompt 越长,输出大概率也越长)估算每个请求的总生成 token 数。结合当前已有长度,算出剩余长度和预期迭代次数。紧急且短小的请求会被赋予更高权重,优先服务以快速完成。

  • 混合 score 的选取策略:调度器为每个活跃请求计算一个得分,综合:

  • 剩余 SLA 时间预算:越临近超时,得分越高。
  • 预估剩余 token 数:越短越优先(短作业优先降低平均延迟)。
  • 等待时间:防止饥饿。 每轮按得分排序,贪心选出 top-N 个请求进入 batch,直至达到单次迭代的 token 预算上限。

  • 总 token 预算控制(Token Budget):考虑到单次迭代延迟与“批次内总 token 数(所有请求序列长度之和)”强相关,调度器会预设一个最大 token 数(如 8192)。将 prefill 和 decode 阶段的请求分离或联合预算:如果某高优先级请求需要 prefill,可减少同时服务的 decode 请求数,保证迭代延迟不超标。通过限制 token budget,使得无论请求组合如何,单步延迟始终可控。

  • 分片 Prefill(Chunked Prefill):当一个紧急请求具有极长 prompt 时,如果把整个 prompt 一次性 prefill,会导致该步延迟尖刺。此时可将其 prefill 切分成多个 chunk,在多个迭代中与 decode 请求混合处理,既能保证该请求的首 token 延迟不爆炸,又不阻塞其他请求的正常解码。

通过这种感知紧急度与预估长度的动态调度,系统在高并发下依然可以稳定满足 95%、99% 分位的延迟要求。


在流式输出场景下,如何优化首令牌延迟?可以从哪些方面入手?

首令牌延迟(Time to First Token, TTFT)由输入处理(prefill)和生成第一个 token 的前向传播主导。优化可以从以下层面展开:

  • 计算加速:
  • 使用针对 prefill 高度优化的注意力内核(如 FlashAttention 的 varlen 接口),充分利用 GPU 并行度处理长 prompt。
  • 对 prefill 图进行深度编译(TensorRT-LLM、AITemplate),把所有小操作融合为大 kernel,减少内核启动开销。
  • 采用 FP8/INT8 量化加速矩阵乘,降低预填充带宽压力。

  • 利用前缀缓存(Prefix Caching):

  • 许多应用存在公共系统提示或共享前缀。将已计算过的 prompt 前缀的 KV cache 以块的形式缓存起来,新请求可直接命中并复用,完全跳过这部分计算。结合 PagedAttention 的块化管理,前缀缓存的匹配和命中十分高效,对 RAG、多轮对话等场景可减少 90% 以上的首 token 延迟。

  • 预填充拆分(Chunked Prefill):

  • 对于超长输入,不一次性计算全部 prompt,而是拆成多个块,在几个 decode 迭代中穿插计算。这虽然略微延长总首 token 时间,但避免了单步超长延迟,且让后续请求不被长时间阻塞,对整体服务质量和感知延迟有巨大改善。

  • 模型与架构选择:

  • 使用 MQA(Multi-Query Attention)或 GQA(Grouped-Query Attention)降低 KV cache 的内存和计算开销,prefill 更快。
  • 采用非 Transformer 的高效架构(如 Mamba、RWKV),它们处理长序列时的计算量与长度呈线性关系,首 token 延迟更低。

  • 调度与资源策略:

  • 在服务中可为 prefill 专门分配高优先级的 GPU 资源或独占实例,使新请求一到达就立即获得足额算力。
  • 避免与大规模 decode batch 争抢计算单元,或通过 token budget 精细控制并行度。

  • 系统与预处理:

  • 优化分词器和输入处理流水线,减少 CPU 瓶颈。
  • 使用 RDMA、NVLink 等高速传输,加速分布式推理中的 prefill 通信。

对于输入长度差异巨大的批次,如何在批处理时最小化无效计算?

在批处理中,如果简单地将长度不一的序列填充到最大长度,会导致大量计算浪费在 padding token 上。最小化无效计算的方法有:

  • 可变长度序列的不等长批处理(Ragged Batching):
  • 将批次内所有序列的 token 串联成一个长的一维序列,并记录每个序列的起始位置和长度(cu_seqlens 数组)。注意力计算时使用专门支持可变长度的内核(如 FlashAttention 的 varlen API),内部通过 cu_seqlens 确定序列边界,生成块对角形式的 attention mask,使各序列之间完全隔离。这从根本上消除了 padding 的需要,没有任何无效 token 被计算。
  • 层归一化、MLP 等操作则依然可以按 token 并行处理,因为 token 都是连续的,无需区分序列边界。

  • 分桶调度(Batching Bucketing):

  • 在 Ragged Batching 基础上,调度器尽量将长度相近的请求分到同一批次。虽然 Ragged Batching 本身无 padding 浪费,但序列长度差异过大会导致某些序列极短、某些极长,内核的负载不均衡(部分 thread block 提早做完等待)。通过分桶,能让每次迭代的计算负载更均衡,提高 SM 利用率。

  • PagedAttention 用于 Prefill:

  • vLLM 等框架会将长 prompt 的 prefill 也通过 PagedAttention 分块管理。与 decode 混合调度时,不同请求的 prefill 块可以与 decode 请求的 token 自由组合成 batch,不需要将整个 prompt 当作一个不可分割的巨型块。通过把 prefill 化整为零,自然就解决了输入长度差异带来的计算尖刺与浪费。

  • 分离 Prefill 和 Decode 阶段:

  • 在系统架构上,可将 prefill 与 decode 部署在不同 GPU 实例或不同 CUDA stream 上,避免输入长度差异巨大的 prefill 与需要低延迟的 decode 互相干扰。这样即使 prefill 批内有长度差异,也只影响 prefill 自己的吞吐,不会拉慢生成节奏。

综上,Ragged Batching 配合可变长度注意力内核是消除无效计算的核心,分桶和 PagedAttention 是进一步提效和均衡负载的增强手段。


你如何评估投机采样带来的实际加速比?受哪些参数影响最大?

加速比定义:在相同硬件、相同模型精度下,生成一定数量 token 的总时间之比:

image.png

影响最大的参数:

  1. 草稿模型接受率 αα:通过 EE 直接决定有效步长,是最关键因素。αα 从 0.8 提升到 0.95,加速比可能翻倍。

  2. 草稿长度 KEK 增大而增大(但增幅递减),同时草稿生成开销Ktdraft 线性增长。存在最优 K,通常需要根据实际接受率和时延比来动态调优。

  3. 草稿模型的速度比tdraft/Ttarget:如果草稿模型不够轻量,自回归生成 K 个 token 的时间甚至可能超过大模型直接生成 E 个 token,导致加速比崩塌。因此草稿模型必须比目标模型快 10 倍以上,最好在 100 倍量级。

  4. 目标模型单次前向时间Ttarget:TTtarget 越大,分摊草稿开销的能力越强,加速比越容易接近理论上限 EE。因此投机采样在内存带宽受限的大模型、高延迟场景(如批处理推理)下效果更显著;在算力已跑满的场景增益有限。

  5. 验证前向的额外开销:若大模型验证时需要重新计算前缀的 KV cache 或无法复用草稿阶段的隐状态,Tverify 可能显著大于 Ttarget,削弱加速。现代实现通常通过缓存前缀的 KV cache 或提前退出复用等方式将额外开销压到最低。

评估实践:通常先实测草稿模型时延、大模型时延和接受率,通过上述公式进行初步预测;再在真实推理框架中编写端到端测试,观察生成固定 token 的总延迟,并结合 profiler 检查是否出现通信、同步瓶颈,以此指导 K 值选取和草稿模型优化。