这是本系列的第二篇,讨论推理技术这一方向。本文作为一个初学者在学习过程中的记录,可能不一定能做到非常完整和系统。
性能模型与瓶颈分析:一切优化的判断基础
推理阶段拆解与特点
当前的生成式文本模型采用decoder only架构,先批量填充prompt token,再逐token做预测,分别称为prefill和decode。
- prefill:一次性输入并处理完整prompt,
个输入要计算 次注意力和 维的FFN - decode:用上一次输出的token作为输入预测下一个token,
个输入要计算 次注意力和 维的FFN
因此,prefill往往是算力受限(Compute-Bound),decode往往是访存受限(Memory-Bound)。
性能分析模型
一个计算设备想要能够计算,首先需要把数据从内存拉到ALU中。因此设备的实际计算性能,不仅受制于其浮点运算能力,还受制于其内存带宽。
Roofline Model是一个经典的性能分析模型,如下图1。
图为NVIDIA A6000在FP16计算下的Roofline Model。其中:
- 横坐标:算术强度(Arithmetic Intensity),表示一个计算任务(计算函数或算子)的浮点运算量与内存访存量(数据传输量)的比值
,或是在单位时间内比,即浮点运算速度与内存带宽的比值 - 纵坐标:计算性能(Performance),表示该计算任务在设备上能达到的浮点运算速率
因此,该图表示了在一个特定的设备上,不同算术强度的计算任务能达到的实际浮点性能,公式为:
- 上方横线为设备的峰值浮点性能,任何计算任务的实际性能都不会超过这条线,因为它代表硬件限制
- 左侧斜线表示在不同算数强度下的计算任务实际能够达到的最大浮点性能,(线性坐标系下)斜率为峰值内存带宽
- 斜线与横线的交点,代表同时达到峰值浮点性能和峰值内存带宽的条件,这要求计算任务发生在特定的算术强度下,即峰值浮点性能与峰值内存带宽的比值
根据Roofline Model,我们可以判定一个计算任务是算力受限还是访存受限:
- 如果其算数强度较小,在斜线区间中,则为访存受限
- 如果其算数强度较大,在横线区间中,则为算力受限
在实际的优化工作中,我们往往需要做算子级拆解和分析。
性能指标体系
介绍一些常见的指标。
在用户视角,常用指标:
- TTFT(Time To First Token):从用户提交查询到系统返回第一个输出内容所需的时间
- TPOT(Time Per Output Token):在生成第一个token后,后续每个输出token所需时间
- Goodput:在满足SLO的条件下的有效吞吐量(区别于Throughput)
在硬件利用率视角,常用指标:
- MFU(Model FLOPs Utilization):系统实际观测的运算速度与系统理论峰值运算速度的比值,即实际有效FLOPS/理论峰值FLOPS,一般在计算受限场景中使用
- MBU(Model Bandwidth Utilization):系统实际达到的带宽与系统理论峰值带宽的比值,即实际有效MBW/理论峰值MBW,一般在访存受限场景中使用
更细粒度的性能分析
Roofline Model只是一个粗粒度的性能分析模型,实际性能优化还要更深入考虑通信开销、kernel开销、KV Cache容量等等细节问题。
例如NVIDIA的AIConfigurator,将推理过程拆解为底层的算子原语(GEMM矩阵乘法、Attention机制、通信、内存等),利用内置的底层性能数据库进行组合预测,平均30秒即可评估成千上万种配置组合。
推理技术:逐层分析优化方向
请求编排
批处理与调度
prefill与decode瓶颈相反,在大模型推理中,我们需要考虑在什么粒度上拆分prefill与decode,以在满足SLO的前提下实现资源利用率最大化。粒度从单卡内的token级切片到集群级的隔离划分都需要考虑。
常见的拆分手段有:
- Chunked Prefill(分块预处理)
- 粒度:token级别拆分
- 做法:把一个长prefill阶段拆成多个固定大小的token块(Chunk),穿插在decode的各个时间步之间计算
- 作用:用于prefill,在PD不分离的情况下,略微牺牲了最好情况下的TTFT,但保障了最坏情况下的TTFT和TPOT稳定
- Continuous Batching(连续批处理)
- 粒度:请求级别拆分
- 做法:模型每完成一次计算任务(分块的prefill或一次decode),检查该请求是否完成,如果完成,调度器立刻将新请求添加进去(更准确地说,它工作在迭代级,每次迭代结束都可以更换任务)
- 作用:通常用于decode,在每个时间步的迭代中,将多个活跃请求组成batch(已有请求生成结束时即时补入新请求),提升算术强度,大幅减少GPU空转
- PD分离(Prefill-Decode Disaggregation)
- 粒度:集群架构级别拆分
- 做法:将整个集群的GPU划分为完全独立的prefill池与decode池,请求先到prefill实例算完输入并生成KV Cache,再通过高速网络(NVLink/RDMA等)将KV Cache传输给decode实例集群接力生成
- 作用:彻底消除算力和显存带宽的资源争抢,保障了稳定性,但是不直接提升性能(两池可采用各自最优的并行策略与批大小间接提升性能),代价是引入了KV Cache的跨机/跨卡网络传输开销
KV Cache的运行时管理与复用
先来回顾下KV Cache:KV Cache本质上缓存的是Transformer每一层用于自注意力计算的投影向量值。KV Cache并没有改变
可以说:KV Cache把算力问题变成了显存和带宽问题。正是这一特点,KV Cache逐步被改造成一个有分页器、有缓存命中率、有分层存储、有传输协议的独立被管理资源。
显存管理:从碎片化到分页
朴素的推理实现有三类典型的显存浪费2:
- reserved:为未来token产生的KV Cache预留的空间
- internal fragmentation:由于过度配置以应对潜在的最大序列长度而导致的内部碎片
- external fragmentation:来自内存分配器的外部碎片
因此,由vLLM提出的PagedAttention诞生,其核心思路是借鉴操作系统内存分页的思路,将KV Cache切成固定大小block(默认16个token),维护block table,物理块带引用计数,多序列共享时用 Copy-on-Write。
除非修改显卡驱动或者使用一些显卡的低级API,否则PagedAttention只能完全在用户态工作,无法像CPU内存一样利用MMU硬件处理,因此会有一定的性能损耗。很显然,这里后续还存在很多优化空间。
前缀复用(Prefix Caching)
在用户进行多轮对话,或者多Agent存在公共的System Prompt时,重复计算前缀非常耗时且是不必要的。
因此,我们可以引入前缀缓存机制,对于每个输入,检查其是否包含已经计算过的历史片段,如果缓存命中,就直接复用KV Cache。
典型的代表是SGLang的RadixAttention3,将请求序列用基数树(Radix Tree)来维护。它跟字典树(Trie Tree)非常类似,核心区别在于字典树的每个节点只存储单个字符(或固定大小的片段),而基数树会将只有一个子节点的连续链条进行压缩合并,从而节省大量空间。
通过基数树这种数据结构,我们可以很轻松地维护和查询最长公共前缀,从而实现KV Cache的前缀复用。
另外,vLLM也支持前缀缓存,不过是用哈希的方式实现的。但是vLLM的哈希前缀缓存也是递归的,所以从本质上讲也是个树形结构,所以两个方案可以说是殊途同归。
分层存储与卸载
随着上下文长度飙升,单个GPU存不下海量的KV Cache。除了并行化拆分,KV Cache也开始走向异构存储体系。
分层存储即为将KV Cache按需求分别放在HBM、CPU RAM、SSD、网络RDMA等,有点类似CPU多级缓存。
因此,不同层级的带宽和容量是核心需要考虑的问题。方案制定的核心原则是取回来重算是否比重新prefill更快。
这是一份AI给的不同存储层级大致的特点:
| 存储层级 | 典型带宽范围 (单机/单卡) | 典型容量规模 | 访问延迟级别 | 在 KV Cache 中的角色与权衡 |
|---|---|---|---|---|
| HBM (显存) | 2 TB/s - 8 TB/s | 80 GB - 192 GB /卡 | 百纳秒级 (ns) | 核心层。Prefill(首字)与 Decode(生成)的计算核心区。容量极小、成本极高、速度极快。 |
| CPU RAM (DDR) | 100 GB/s - 400 GB/s (不考虑PCIe通信限制) |
512 GB - 2 TB /机 | 百纳秒级 (ns) (不含跨PCIe通信) |
温数据层。作为 HBM 的延伸(Offloading)。带宽缩减10-20倍,但容量大一个数量级,成本较低。 |
| 网络 RDMA (集群) | 50 GB/s - 100 GB/s (400G/800G) | 视集群规模而定 | 微秒级 (µs) | 共享/协作层。用于多卡/多机之间的 KV Cache 交换(如 Disaggregated Attention 架构),受限于网络拓扑。 |
| SSD (NVMe) | 7 GB/s - 32 GB/s (PCIe 4.0/5.0) | 几TB - 数十TB /机 | 几十微秒级 (µs) | 冷数据层。用于长文本、多轮对话的历史缓存(Context Caching)。带宽太低,直接读取会严重拖慢 Decode 速度。 |
与此同时,业界也开始开发专门针对KV Cache的传输协议与通信框架,例如通过定制化的RDMA协议,实现KV Cache在集群内部的高速路由与传输,降低网络带宽带来的延迟。
运行时压缩:推理时稀疏化
这里主要是指淘汰与稀疏化系列方法。
核心基于两个观察:attention sink(首几个token恒定吸走大量权重)与heavy hitters(累计注意力分数呈幂律分布,少量token贡献了绝大部分权重)。
因此可以通过一些算法,计算出哪些token对结果的影响不大,让这些token不参与注意力的计算。相当于对结果做近似,但是省下了KV Cache的容量和读取量。
然而这类方法在生产中很难落地,核心原因是:
- FlashAttention不落盘注意力分数,导致部分依赖注意力分数的算法失效
- 反复的token淘汰在生产系统里可能根本释放不了显存:PagedAttention中一个物理block约16个token,只有整块全空才能回收
当然也有一些补救办法:使用不依赖注意力分数的算法,以及一些碎片整理方案或是忍受页内碎片。
投机解码
投机解码的思想与CPU分支预测非常像:使用一个外部小模型(或是模型自身的MTP)做多token预测,同时计算出多个输出token的概率分布并在大模型中批量decode并验证,如果有其中一个token验证失败,那这个token和其后的生成结果都要丢弃。
最朴素的方式是利用大模型输出的概率分布采样得到最终token后,与小模型预测的token逐一对比。但这个方法在top 1的token概率不高时非常容易验证失败,例如:
大模型分布
小模型分布
尽管两者预测的概率分布差异不大,经采样后验证成功的概率只有
因此,投机解码采用一种叫做拒绝采样的策略,通过比对概率而非比对结果,能在保证大模型原始输出概率分布完全不变(无损)的前提下,大幅提升小模型预测token的通过率。我们记大模型输出概率分布为
从左往右检查每个候选token
- 如果
,说明大模型比小模型更认可它,直接接受该候选token - 如果
,以的概率接受该候选token,一旦拒绝,立刻丢弃该位置及其后所有候选,并从修正分布 中重采一个token。其中,修正分布 这里的修正分布本质上是:大模型把所有被小模型“高估”的token概率压低,计算出一个新的分布(残差分布),然后做归一化(即
的分母部分),再采样。
下面证明一下这样的策略可以使得最终输出token的分布恰好等于直接从大模型
某个token
- 路径A:候选恰好是
且被接受 - 概率为:
- 解释:抽到
的概率是 ,接受的条件概率是 ,两者相乘即为候选恰好是 且被接受的概率
- 概率为:
- 路径B:候选被拒绝,重采样采到了
- 概率为:
- 解释:
- 等式第一段:由路径A可知,候选恰好是
且被接受的概率为 ,我们对候选边缘化,得到
接受率 ,
则拒绝率为 ,所以候选被拒绝且重采样采到了的概率就是 - 等式第二段:
- 利用恒等式
,我们可以得到
修正分布的分子部分 - 再次利用该恒等式,我们可以得到
修正分布的分母部分
相乘后分母抵消,只剩修正分布的分子部分
- 利用恒等式
- 等式第一段:由路径A可知,候选恰好是
- 概率为:
两个路径概率相加
模型表示
量化与低精度推理
模型量化通常指用更少的数据位来表示模型的参数(权重)或中间计算结果(激活值),从而减小模型的体积和计算量。其关键在于怎么在降低数据位的同时,尽可能降低计算精度的损失。
它是推理部署中性价比最高的单点优化,因为它同时放宽显存容量与访存带宽两个硬约束,代价是可控的质量损失。但收益能否落地,完全取决于目标精度是否有成熟kernel与张量核支持。
量化主要有三类:
| 量化对象 | 省什么 | 难度 | 为什么 |
|---|---|---|---|
| 权重量化 | 压缩模型的参数,显存占用+权重访存带宽 | 低 | 权重分布规整、范围已知,量化后误差可控 |
| 激活量化 | 压缩模型的中间计算结果,让张量计算能用低精度计算,加速prefill和大batch | 高 | 激活有离群值(Outliers),动态范围大,直接量化精度损失严重 |
| KV Cache量化 | 随batch和上下文线性增长的那部分显存 | 中 | 省下来的显存可以换更大并发(同时服务更多用户/更长上下文) |
在量化时机选择上,主要有两种:
- PTQ(Post-Training Quantization):模型训练好之后再进行量化。优点是方便快捷,不需要重新训练。缺点是可能会有精度损失。使用最广泛。
- QAT(Quantization-Aware Training):在模型训练(或微调)过程中就引入模拟量化的操作。优点是精度损失通常更小。缺点是需要额外的训练时间和计算资源,还需要原始训练流程或数据。
在量化方法上,最朴素的方式是用张量的最大值进行缩放,然后用更低精度存储,以实现尽可能保留精度。但这样的方式容易受到离群值的影响,离群值在权重中还好一点,如果在激活值里就麻烦了,因为激活值是动态变化的。
AWQ(Activation-aware Weight Quantization,激活感知量化),是一种典型的权重量化方法。AWQ方法认为:经常与较大激活值相乘的权重,才是对模型性能影响最大的“敏感权重”。关键步骤如下:
- 感知激活:通过小数据集跑前向传播,观察输入数据的激活值分布,找出那些数值特别大、对模型输出影响极大的“显著特征通道”(约占0.1%-1%)
- 保护权重:与这些大激活值相连的权重非常敏感,如果直接强行低比特量化会导致性能严重下滑,因此通过数学等价变换,在量化前把这些重要权重按比例放大(并在推理时把与它相乘的激活值缩小),让它们在量化时保留更高的精度,从而大幅降低量化误差,代价由其余相对不重要的通道分摊
工程实现上通常将激活值的缩小操作融合进前一层,降低额外开销。
在部署模式上,典型地可以采用W4A16方案(即权重为4-bit,激活值保持FP16/BF16等高精度),在推理时无需特殊的混合精度硬件支持,主要节省的是显存和访存带宽而非计算量。
MoE与稀疏推理
MoE把参数量与每token算力消耗解耦,代价是推理系统可能面临显存驻留、路由不均、小张量计算(算术强度变低)等新问题,因此MoE推理本质上是一个分布式放置与负载均衡问题,而非单卡算子问题。
列举一些应对这类问题的常见方法:
- EP(专家并行)、AFD(Attention-FFN Disaggregation,注意力-前馈网络分离)等并行化策略:提升利用率,后面会介绍。
- 冗余专家:针对热点专家增加副本(即冗余专家)以实现更好的负载均衡,榨干硬件性能。
- Grouped GEMM(分组通用矩阵乘法):在同一个GPU Kernel中,同时并行计算多个独立、形状(尺寸)各不相同的矩阵乘法。每个专家收到的token数量动态变化,导致多组不同大小的矩阵乘法并行需求,使用Grouped GEMM可以完美将这些零散的专家计算合并加速。
- MoE与量化的协同:专家FFN占参数绝大头,量化收益集中于此。
- 根据P/D制定不同的策略:prefill和decode的计算特性不同,可能需要分开制定策略。
集群与部署
并行化策略
训练的并行化策略首先要回答显存是否够用的问题,而推理的模型态显存压力小很多,KV Cache使显存约束以另一种形式回归,取而代之的是SLO的硬约束。
因此,推理的并行化策略核心要考虑:在动态负载的场景下,在满足SLO约束的条件下,榨干硬件资源,最大化每GPU每瓦的有效吞吐。
常见的并行策略如下:
| 策略 | 含义 | 通常切什么 | 特点 |
|---|---|---|---|
| DP(Data Parallelism) 数据并行 |
将输入的批次数据切分到不同的显卡上并行处理,每张显卡上都复制完整的一份模型权重(如果没结合其他并行方式的话) | 请求 | 处理不同的独立并发请求,各卡互不干扰地生成各自的输出,最后汇总 |
| TP(Tensor Parallelism) 张量并行 |
将单层内的权重张量横向或纵向切分到不同的显卡上 | 层内权重、attention head、KV按head | 层内计算需要多卡频繁通信来拼凑完整结果。主要用于降低单张卡的显存占用,并加速单条长请求的计算延迟,通常受限于同一台机器内部的NVLink带宽 |
| PP(Pipeline Parallelism) 流水线并行 |
将模型的不同网络层(Layer) 垂直切分到不同的显卡上 | 连续层区间 | 数据像流水线一样顺序流过各张显卡。缺点是容易产生气泡(即某张显卡因等待前序显卡输出而闲置),需要通过微批次等调度技术优化 |
| EP(Expert Parallelism) 专家并行 |
专门针对MoE架构设计的并行方式,将不同的专家网络分发到不同的显卡上 | MoE专家 | 当token被输入网络时,通过路由器将不同token指派给对应的专家显卡计算,卡间通过通信分发与收集数据 |
| CP(Context Parallelism) 上下文并行 |
针对长上下文场景,将上下文序列切分到不同显卡上 | KV(decode);Q或Q+KV(prefill) | 通过优化注意力机制(如 Ring Attention 或Megatron CP),让多张卡协同计算超长注意力矩阵,解决长文本带来的显存爆炸问题 |
| SP(Sequence Parallelism) 序列并行 |
通常作为TP的延伸或补充,在不使用TP的非切分区域(如LayerNorm或Dropout层),沿序列维度对激活值进行切分 | LayerNorm或Dropout | 与TP紧密配合,消除序列维度上的冗余显存,使模型能高效处理更长的序列 |
实际会结合硬件情况决定采用哪些并行模式的组合。通信是其中的重要考量因素,如何尽可能实现通信计算重叠也是一个重要的话题。
算子级分离式部署AFD
前面我们介绍了两种典型的分离式部署:按推理阶段拆分的PD分离、按模型结构拆分的EP专家并行(更准确地说它是并行模式,只是表现为专家之间分离的形态)。
在层内算子级也可以做分离式部署,典型的就是AFD(Attention-FFN Disaggregation,注意力-前馈网络分离)。
其做法是:将单层内的Attention(注意力机制)和FFN/MoE(前馈网络)部署在不同设备池上。
主要原因和优势是:在decode阶段,Attention受KV Cache影响严重,属于Memory-Bound,而FFN属于Compute-Bound。AFD可以让Attention和FFN能够独立扩展、使用不同的并行策略(如FFN用专家并行EP,Attention用TP/DP),甚至能混用异构硬件(如用不同代际的显卡适配各自的算力瓶颈)。
引擎生态与编排
当前主流的推理引擎主要是vLLM、SGLang、TensorRT-LLM,端侧主流是llama.cpp。
推理引擎一般可以分为引擎层(跑单节点模型)与编排与调度层(协调引擎集群)。上面介绍的分块预处理、前缀复用、专家并行等技术,都依赖编排与调度层的能力。
内核与硬件
硬件层
硬件涉及到的资源类型主要有:计算资源、存储资源、通信资源、控制与调度资源。
软件的优化离不开硬件的具体情况。硬件的偏科决定了算法优化的方向:哪里没涨,哪里就可能是新瓶颈。硬件很可能是非对称扩展的,例如张量核涨得比带宽和特殊函数单元快,这是很多设计的前提,这使得算子在硬件上的峰值利用率可能不再单调上升。硬件设计也在不断发展,例如专门设计的大SRAM型GPU。与此同时,在硬件资源层面考虑不只是如何把算法跑快,硬件还会反向淘汰算法。
计算资源
| 资源 | 说明 | 对内核设计的影响 |
|---|---|---|
| SM(Streaming Multiprocessor) 流式多处理器 |
GPU的并行处理单元总数(A100=108,H100=132,B200=148) | 并行度必须要能填满SM |
| 张量核(Tensor Core) | 矩阵乘专用单元,BF16/FP8 算力极高(BF16:H100 ~1 PFLOPS,B200 ~2.25 PFLOPS) | GEMM类算子几乎不受限,但注意力是GEMM+非GEMM混合,瓶颈会转移到非张量核部分 张量核对精度的支持也需要考虑 |
| CUDA核心 / FMA单元 | 通用浮点运算单元 | 单个SM里数量最多的计算单元 |
| SFU(特殊函数单元) | 执行exp/sin/cos/rsqrt等超越函数 | softmax前向的exp可能成为attention瓶颈,例如FlashAttention-4用FMA上的多项式近似模拟指数运算,绕开SFU瓶颈(把稀缺资源SFU上的活转移到富余资源FMA上) |
| 每SM的warp(线程束)调度器 | 决定每个时钟周期能发射多少条warp指令(指令发射带宽) | 每SM的warp调度器决定硬件的延迟隐藏能力:调度资源不足时,可能无法重叠加载与计算,失去优化收益 可同时驻留的活跃warp数(occupancy)由硬件上限、每线程寄存器用量、每block共享内存用量共同决定 |
存储资源
| 层级 | 典型容量 | 典型带宽 | 特点 |
|---|---|---|---|
| HBM显存 | 80–192 GB | 2–8 TB/s | 整卡全局共享,手动管理;大张量(权重、KV Cache)驻留于此;Memory-Bound算子(如decode阶段的GEMV)的天花板就是HBM带宽 |
| L2缓存 | 40–126 MB | 数TB/s | 跨SM共享,片上缓存,硬件自动管理;KV Cache重用、prefill阶段的Q/K分块策略直接影响L2命中率 |
| 常量缓存 | 64 KB | 广播式读取 | 每SM专用只读缓存,硬件自动管理;适合所有thread读同一个值的场景(如模型超参数) |
| L1/纹理缓存 | per SM: 48–256 KB | / | 现代架构L1和共享内存是同一物理SRAM,可配置划分比例;硬件自动管理;只读数据(如常量、查表)的自动缓存;对不规则访问模式有帮助 |
| 共享内存(SRAM) | per SM: 48–256 KB | ~10–20 TB/s | 每个SM私有,Block内所有线程共享;是FlashAttention/NSA/GDN等注意力内核最核心的片上缓存;FlashAttention就是把Q/K/V分块载入SRAM做online softmax,避免把 |
| 寄存器文件 | per SM: 65536个32-bit寄存器(每线程有上限,例如255,有1个被保留) | 极快(操作数直接取用) | 每个线程私有,由编译器分配;一个线程使用的寄存器越多,同一个SM里能同时放下的活跃warp数量就越少,因此每个thread能用的寄存器数决定了能同时驻留的warp数(occupancy);寄存器用太多会降低occupancy,用太少则需要反复丢弃、拉取中间值 |
通信资源
| 资源 | 说明 | 影响 |
|---|---|---|
| NVLink / NVSwitch | GPU间高速互联(H100 900 GB/s, B200 1800 GB/s) | TP、DP的all-reduce带宽上限;KV Cache的跨卡交换 |
| PCIe带宽 | GPU与CPU通信(PCIe 5.0: ~64 GB/s) | 比NVLink慢至少一个数量级;传输必须尽量异步重叠;传统RDMA依赖PCIe通信 |
| GPU拓扑 | 全互联 vs 树形 vs 混合 | 决定通信的最优算法(ring vs tree vs all-to-all) |
此外,还需要考虑多机网络硬件(InfiniBand/RoCE)、集合通信库与软件栈(NCCL)等。
控制与调度资源
| 资源 | 说明 | 影响 |
|---|---|---|
| Kernel Launch开销 | 发起计算:每次CPU发起GPU kernel的固定开销(~5-10 μs) | decode阶段每步只算1个token,单个kernel计算少但launch开销大,多层积累起来开销就会比较可观 CUDA Graph就是把一组kernel的launch合并为一张图,之后每步只提交1次,GPU内部按图执行;还可以通过算子融合合并kernel,减少kernel数量 |
| 异步拷贝引擎(DMA) | 数据搬运:独立于计算单元的数据搬运引擎 | 可以在计算的同时做HBM到SRAM的异步预取 |
| 硬件调度器 / 软件动态并行 | 算力分配:GPU内部的硬件逻辑,以及通过软件方式优化调度 | 编写kernel时需要考虑如何通过控制block数量、共享内存用量、寄存器数量等间接影响occupancy 软件调度例如Persistent Kernel设定固定数量(与SM数量相同)的常驻block,自己从任务队列里循环取工作,把调度逻辑写在kernel内部;设备端调度让分支判断留在GPU上,避免GPU与CPU的往返通信 |
内核、算子与编译
FLOPs决定了速度的上限,而转换层(工程实现+硬件特性)决定了能摸到上限的多少。本节主要介绍的就是纸面FLOPs与实际tokens/s之间的这个转换层。
- 算子是数学计算的抽象名字。它只关心输入什么、输出什么、做什么数学运算,而不关心是用什么硬件去算的。
- 内核是算子在特定硬件上的具体实现代码。它必须精准控制硬件的底层资源(如GPU的线程、共享内存、寄存器),以实现最极致的计算速度。
- 编译是把高层算子或计算图转化为硬件可执行的底层机器码(或特定设备指令)的翻译和优化过程。有动态化(JIT)、DSL化的趋势。
算子内核需要结合算子本身和硬件特性来设计,例如:
- 先通过Roofline定位瓶颈在memory还是compute
- 并行度能否填满SM
- 访存模式与内存层级的匹配
- 不同厂商不同代际硬件的专属特性
- 内核启动与调度开销
- 精度支持与量化路径
FlashAttention是一个经典的算子内核,简要介绍一下它的思想:
- 分块(tiling):把Q、K、V在序列维度切分成小块,只把少量块加载进GPU片上SRAM,不在HBM保存完整的注意力分数矩阵。
- 在线softmax:softmax不能直接分块,因为全局归一化依赖全部token的最大值与求和。FlashAttention使用在线softmax递推算法,逐块计算局部softmax,维护当前最大值与归一化分母,迭代修正输出,不用一次性拿到全部信息。
- 重计算换显存(推理中不涉及):不保存前向所有中间结果,反向传播时重新再跑一遍分块注意力来求梯度。用少量额外计算,省去存储大量中间张量的显存开销。
新场景与新变化
负载特性变化
负载重心从单轮短输出迁移到长思维链加多轮agent会话,推理系统的优化目标随之从request级吞吐转向decode效率、跨轮KV Cache管理、以及program级完成时间。
agentic负载最大的特点是decode占比压倒性上升,此外还有存在可复用公共前缀的特点。
长上下文也给KV Cache带来了新的挑战。例如:怎么做多级KV Cache以及过期设定,KV Cache过期后重新做长文本prefill导致的TTFT问题。以及agentic场景下变得更重要的一些问题:计费模型,怎么做推理时约束(JSON Schema转CFG),有状态的长会话不能只考虑TTFT,如何用内置工具降低整体时延。
端侧推理
端侧推理是另一套约束,它是把云端靠batch摊薄权重读取成本的经济模型彻底拿掉后的推理形态,即batch=1、内存带宽往往是唯一瓶颈(除了长上下文prefill),以及需要考虑功耗与散热。
低比特量化几乎是必需而非可选。在硬件上,需要考虑如何使用NPU等异构硬件加速推理和控制功耗。
总结
推理系统设计与优化是一个系统性工程。有三个关键的判断原则:
- 先判断瓶颈,再选择手段
- 论文加速比不等于生产收益,工程上能否实现才是关键
- 优化是相互耦合的,不一定能独立叠加,可能需要做一定取舍

欢迎留言