- 大模型
- 深度学习
- 算子库
- 后端
- 高性能计算
【免费下载链接】flashinfer
FlashInfer: Kernel Library for LLM Serving
本文围绕 FlashInfer 中flashinfer.mhc模块展开,系统讲解其专为 Agentic 风格残差混合(residual mixing)设计的 Multi-head Residual Combination(mHC)系列 CUDA 核函数:后映射mhc_post与两大前映射融合核mhc_pre_big_fuse/mhc_pre_big_fuse_with_prenorm。你将掌握这三个 API 的数学语义、全部形状约束与参数默认值、CUDA 底层实现原理,以及它们在 tests/mhc 中的数值验证方法,可直接用于 Agentic 架构中"4 子头残差混合"场景的推理接入与调试。
1. 模块定位:Agentic 残差混合与固定 HC=4 子头布局
flashinfer.mhc是 FlashInfer 中专用于 Agentic 风格残差混合(Agentic-style residual mixing)的核函数集合,其官方定位见 docs/api/mhc.rst:
Multi-head residual Combination (mHC) kernels used by Agentic-style residual mixing with a fixed
HC=4sub-head layout.
核心要点是固定HC=4子头布局(hard-wired to 4 sub-heads)。所谓"残差混合",是指在一个 Transformer 层中,将上层的多层残差状态按可学习的混合系数重新组合后注入当前层。mHC 把这一过程拆成两个阶段:
- 前映射(pre-map):根据投影 logits 计算出混合权重(pre / post / comb 三组系数),并生成下一层的实际输入;
- 后映射(post-map):在当前层输出
x与旧的 4 头残差residual之间按系数完成最终组合,得到新的 4 头残差。
该模块共导出三个函数,全部以@flashinfer_api装饰并配套 trace 模板,因此同时支持 FlashInfer 的 trace 录制/回放机制(见 flashinfer/mhc.py 等处的trace=参数)。
模块中两个关键常量定义在 flashinfer/mhc.py:
_MHC_HC = 4 # 固定的子头数 _MHC_MIX = _MHC_HC * (2 + _MHC_HC) # = 4 * 6 = 24,即 pre(4) + post(4) + comb(16)24 维的投影 logits 布局贯穿全部三个 API:前 4 维给 pre、中间 4 维给 post、后 16 维按 4×4 排布给 comb(残差组合矩阵)。
2. 后映射核:flashinfer.mhc.mhc_post
2.1 数学语义
mhc_post完成 mHC 的 post 映射,其公式在 flashinfer/mhc.py 中明确给出:
out[..., new_hc, h] = x[..., h] * post_layer_mix[..., new_hc] + Σ_old residual[..., old_hc, h] * comb_res_mix[..., old_hc, new_hc]即:对每个新子头new_hc,输出 = 当前层输出x乘以其专属后混合系数post_layer_mix[..., new_hc],再加上 4 个旧残差子头按组合矩阵comb_res_mix[..., old_hc, new_hc]加权求和的结果。
2.2 函数签名与输入输出形状
flashinfer.mhc.mhc_post( x: torch.Tensor, # [..., H],层输出,bf16 residual: torch.Tensor, # [..., HC=4, H],多子头残差,bf16 post_layer_mix: torch.Tensor, # [..., HC=4] 或 [..., HC=4, 1],float32 comb_res_mix: torch.Tensor, # [..., HC=4, HC=4],float32 ) -> torch.Tensor # 与 residual 同形状 [..., HC=4, H]形状校验逻辑在_check_mhc_post_inputs(flashinfer/mhc.py)中:
| 输入 | 要求 | 说明 |
|---|---|---|
residual | ndim >= 3,shape[-2] == 4 | 倒数第二维即 HC 轴,必须等于 4,否则抛ValueError("residual.shape[-2] / HC must be 4") |
x | shape == residual.shape[:-2] + (residual.shape[-1],) | 外层维度需与 residual 一致,隐藏维 H 相同 |
post_layer_mix | [..., 4]或[..., 4, 1] | 两种形状都接受,内部统一 reshape |
comb_res_mix | [..., 4, 4] | 每个[old_hc, new_hc]条目是旧头old_hc混入新头new_hc的系数 |
内部会先做reshape(-1, ...)摊平 token 维度,保证 contiguous 后调用底层实现_mhc_post_impl,最后reshape_as(residual)还原形状(flashinfer/mhc.py)。
2.3 CUDA 实现:多路径分发的内存布局
底层实现在 csrc/mhc/mhc_post.cu,入口flashinfer::mhc::mhc_post(第 556-599 行)先做类型/形状强校验:
x、residual、out必须为dl_bfloat16,post_layer_mix、comb_res_mix必须为dl_float32;- 形状必须满足
residual [T, 4, H]、x [T, H]、post_layer_mix [T, 4]、comb_res_mix [T, 4, 4]、out [T, 4, H]; total_tokens == 0时直接返回,避免空启动。
该文件为 BF16 + HC=4 准备了五条 kernel 路径,由launch_bf16_hc4(第 513-552 行)按H是否 8 对齐和取值区间分发:
| 路径 | 适用场景 | 关键点 |
|---|---|---|
token-vec kernel(mhc_post_bf16_hc4_token_vec_kernel) | H ≤ 1536 / ≤ 2048 / ≤ 8192 / ≤kMaxTokenVecHidden | 每个 CTA 处理TokensPerBlock个 token(4 或 1),每 token 多线程并行;系数可经 warp shuffle(WarpCoeffLoad=true)或共享内存广播加载 |
split-vec kernel(mhc_post_bf16_hc4_split_vec_kernel) | 1536 < H ≤ 4096 等 | 按 H 维分 tile(TileElems=1280,每 CTA 160 线程 × vec8),grid.y 在 token 维上循环 |
| persistent split kernel | H = 4096、H = 7168(token ≥ 4096) | 静态 H 特化,compute_vec8_head_serial逐头串行写回以缓解访存压力,注释明确标注 "DeepSeek V4 / V4 Pro shape" 场景 |
scalar kernel(mhc_post_bf16_hc4_scalar_kernel) | H 非 8 的倍数 | 线性平铺逐元素回退路径 |
vec 化层面使用 8 元素(kVecWidth=8)BF16 向量:通过内联 PTXld.global.v4.u32/st.global.v4.u32完成 128 位访存(Bf16x8,第 47-59 行),每个 token 的 20 个混合系数(4 post + 16 comb)被组织为Hc4Mix(一个float4 post+ 四个float4 from0..from3),配合fmaf累加与__shfl_sync跨 lane 广播,将公式展开为 4 个新头并行、每次处理 8 个隐藏维的 FMA 流水(compute_vec8,第 144-191 行)。
3. 前映射大融合核:mhc_pre_big_fuse
3.1 功能与公式
mhc_pre_big_fuse完成 mHC 的 pre-map "大融合"(big-fuse):它接收外部已经算好的投影 logits(dot_mix)与残差平方和(sqrsum),在一个 kernel 内完成 RMS 归一化、三组系数激活/归一化(含 Sinkhorn 迭代),并直接生成下一层的输入layer_input。签名(flashinfer/mhc.py):
flashinfer.mhc.mhc_pre_big_fuse( dot_mix: torch.Tensor, # [..., 24] 或 [num_splits, ..., 24],float32 sqrsum: torch.Tensor, # [...] 或 [num_splits, ...],float32 residual: torch.Tensor, # [..., HC=4, H],bf16 mhc_scale: torch.Tensor, # [3],float32 mhc_base: torch.Tensor, # [24],float32 k: int, rms_eps: float = 1e-6, mhc_pre_eps: float = 1e-6, mhc_sinkhorn_eps: float = 1e-6, mhc_post_mult_value: float = 1.0, sinkhorn_repeat: int = 20, num_splits: int = 1, block_size: int = 0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor] # (post_mix, comb_mix, layer_input)返回三个张量,形状分别为[..., HC=4, 1]、[..., HC=4, HC=4]、[..., H],即后续mhc_post所需的 post 系数、comb 矩阵,以及下一层的输入向量。
3.2 参数语义与校验(可复制的默认值速查表)
| 参数 | 默认值 | 语义与约束 |
|---|---|---|
dot_mix | — | 原始投影 logits,尾部 24 = pre(4) + post(4) + comb(16);num_splits > 1时带前导 split 维 |
sqrsum | — | 每个 token 的残差平方和,用于 RMS 归一化;split 模式下 kernel 内部跨 split 归约 |
k | — | mHC 算法参数,直接传给 CUDA kernel;测试中取k = 4 * hidden_size(4 头 × H),作为 RMS 的归一化分母 |
mhc_scale | — | 形状必须为[3],分别缩放 pre / post / comb 三组 logits |
mhc_base | — | 形状必须为[24],三组 logits 的偏置 |
rms_eps | 1e-6 | RMSNorm 数值稳定项,必须严格为正(否则抛 ValueError) |
mhc_pre_eps | 1e-6 | pre-map 步的数值稳定项,必须严格为正 |
mhc_sinkhorn_eps | 1e-6 | Sinkhorn 迭代的稳定项,必须严格为正 |
mhc_post_mult_value | 1.0 | post 输出的乘性因子 |
sinkhorn_repeat | 20 | Sinkhorn 行/列归一化迭代次数,C++ 侧要求>= 1 |
num_splits | 1 | 前导维的 split 因子,只能是 {1, 2, 4, 8, 16},否则抛 ValueError;> 1时dot_mix/sqrsum各带前导 split 轴,kernel 内求和归约 |
block_size | 0 | CUDA block 大小提示;0表示用 kernel 默认(pre_big_fuse 为 256、with_prenorm 为 128),>=512/>=256/ 其余分别派发 512 / 256 / 128 |
3.3 split 模式的形状细节
num_splits == 1时(flashinfer/mhc.py):
dot_mix形状必须为outer_shape + (24,);sqrsum形状必须为outer_shape。
num_splits > 1时(第 280-292 行):
dot_mix形状必须为(num_splits,) + outer_shape + (24,);sqrsum形状必须为(num_splits,) + outer_shape;- 摊平后 kernel 在 split 轴上求和(见下文源码)。
3.4 CUDA 实现:逐 token 三阶段流水
核心 kernel 为mhc_pre_big_fuse_kernel(csrc/mhc/mhc_pre_big_fuse.cu),采用one CTA per token的 grid 布局(dim3 grid(total_tokens)),每个 block 内分三阶段协作:
- RMS 归一化(可选计算 sqrsum):
warp_sum+block_sum通过__shfl_down_sync与共享内存完成跨 warp 归约;rstd = rsqrtf(sq_total / K + rms_eps),其中K即传入的k。 - 系数生成(
write_token_metadata,第 101-164 行):仅 warp 0 的前 4 个 lane 参与,复用 32 线程 warp 的广播机制:pre_mix[lane] = sigmoid(pre_logit) + mhc_pre_eps;post_mix[lane] = sigmoid(post_logit) * mhc_post_mult_value;- comb 矩阵先做 softmax(行归一,减行最大值保证数值稳定),再用
__shfl_xor_sync在 lane 间完成列和归约,随后执行sinkhorn_repeat次"行归一 → 列归一"交替迭代,这正是测试参考实现中_sinkhorn_normalize_ref的双随机(doubly stochastic)归一化。
- 生成 layer_input(
write_layer_input,第 166-200 行):warp 0 之外的所有线程按 vec8 遍历隐藏维,累加layer_input[h] = Σ_j pre_mix[j] * residual[j, h]。
当num_splits > 1时,第 241-256 行的分支会把各 split 的sqrsum与dot_mix逐项累加后再进入归一化,实现跨 split 归约;而mhc_pre_big_fuse_with_prenorm则通过COMPUTE_SQRSUM=true的模板特化在 kernel 内直接计算sqrsum(residual_square_sum_vec8),省去外部预计算(static_assert保证该路径仅支持num_splits=1)。
C++ 侧公共形状校验集中在check_common_shapes(第 370-409 行):residual [T,4,H]、mhc_scale [3]、mhc_base [24],且H必须能被 8 整除(vec8 对齐前提);post_mix [T,4]、comb_mix [T,4,4]、layer_input [T,H]全部要与 token 数对齐。
4. 免预计算变体:mhc_pre_big_fuse_with_prenorm
当外部没有预先算好sqrsum时,应使用mhc_pre_big_fuse_with_prenorm。它对应 Agentic 的mhc_pre_finalize边界,在 kernel 内部从residual计算 RMS 平方和。签名(flashinfer/mhc.py):
flashinfer.mhc.mhc_pre_big_fuse_with_prenorm( dot_mix: torch.Tensor, # [..., 24] 或 [1, ..., 24],float32 residual: torch.Tensor, # [..., HC=4, H],bf16 mhc_scale: torch.Tensor, # [3] mhc_base: torch.Tensor, # [24] rms_eps: float = 1e-6, mhc_pre_eps: float = 1e-6, mhc_sinkhorn_eps: float = 1e-6, mhc_post_mult_value: float = 1.0, sinkhorn_repeat: int = 20, block_size: int = 0, ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]与mhc_pre_big_fuse的差异点:
- 无
k与num_splits参数:RMS 分母固定为kHc4 * H(即4 * H),且内部 sqrsum 路径只支持单 split; dot_mix可带前导1维:[..., 24]或[1, ..., 24]两种形状都接受(flashinfer/mhc.py),自动 squeeze/reshape 为[T, 24];- 默认 block size 为 128(
select_pre_big_fuse_with_prenorm_block_size,csrc/mhc/mhc_pre_big_fuse.cu),低于外部 sqrsum 路径的 256。
5. 完整调用示例与数值验证
5.1 一个可复现的最小调用
参照 tests/mhc/test_mhc_post.py 与 tests/mhc/test_mhc_pre_big_fuse.py 的输入构造方式(注意 mHC 的 BF16 核要求SM80+ GPU,测试中通过get_compute_capability跳过旧架构):
import torch import flashinfer # ---- 后映射:mhc_post ---- outer = (2, 3) # 任意外层维度(batch × seq 等) H = 4096 # 隐藏维(8 的倍数走向量化路径) x = torch.randn((*outer, H), dtype=torch.bfloat16, device="cuda") residual = torch.randn((*outer, 4, H), dtype=torch.bfloat16, device="cuda") post_layer_mix = torch.randn((*outer, 4), dtype=torch.float32, device="cuda") comb_res_mix = torch.randn((*outer, 4, 4), dtype=torch.float32, device="cuda") new_residual = flashinfer.mhc.mhc_post(x, residual, post_layer_mix, comb_res_mix) assert new_residual.shape == residual.shape # ---- 前映射(外部 sqrsum 版):mhc_pre_big_fuse ---- k = 4 * H dot_mix = torch.randn((*outer, 24), dtype=torch.float32, device="cuda") * 0.01 sqrsum = torch.rand(outer, dtype=torch.float32, device="cuda") * float(k) mhc_scale = torch.randn((3,), dtype=torch.float32, device="cuda") * 0.1 mhc_base = torch.randn((24,), dtype=torch.float32, device="cuda") * 0.1 post_mix, comb_mix, layer_input = flashinfer.mhc.mhc_pre_big_fuse( dot_mix, sqrsum, residual, mhc_scale, mhc_base, k, num_splits=1, sinkhorn_repeat=20, ) assert post_mix.shape == (*outer, 4, 1) assert comb_mix.shape == (*outer, 4, 4) assert layer_input.shape == (*outer, H) # ---- 前映射(免预计算版):mhc_pre_big_fuse_with_prenorm ---- post_mix2, comb_mix2, layer_input2 = flashinfer.mhc.mhc_pre_big_fuse_with_prenorm( dot_mix, residual, mhc_scale, mhc_base, )5.2 测试参考实现(数学定义的权威对照)
测试中的参考实现完整刻画了 mHC 的数学流程(tests/mhc/test_mhc_pre_big_fuse.py):
rstd = torch.rsqrt(sqrsum.float().unsqueeze(-1) / float(k) + RMS_EPS) # RMS 归一化 mixes = dot_mix.float() * rstd pre_logits = mixes[..., :4] * mhc_scale[0] + mhc_base[:4] # pre 头 post_logits = mixes[..., 4:8] * mhc_scale[1] + mhc_base[4:8] # post 头 comb_logits = mixes[..., 8:] * mhc_scale[2] + mhc_base[8:] # comb 矩阵 pre_mix = torch.sigmoid(pre_logits).unsqueeze(-1) + MHC_PRE_EPS post_mix = (torch.sigmoid(post_logits) * MHC_POST_MULT_VALUE).unsqueeze(-1) comb_mix = _sinkhorn_normalize_ref(comb_logits.view(..., 4, 4)) # Sinkhorn 双随机归一化 layer_input = (pre_mix * residual.float()).sum(dim=-2).bfloat16() # 加权求和生成下一层输入_sinkhorn_normalize_ref(第 21-31 行)展示了 Sinkhorn 迭代的语义:softmax(dim=-1) + eps后,交替执行"行归一(除以行和)→ 列归一(除以列和)",共sinkhorn_repeat轮。
5.3 测试覆盖与验收标准
test_mhc_post_matches_reference(tests/mhc/test_mhc_post.py)参数化覆盖了H ∈ {64, 127, 1536, 2048, 4096, 7168, 8192, 16392}与post_ndim ∈ {1, 2},其中 4096 / 7168 分别标注为 DeepSeek V4 Flash 与 V4 Pro 形状,用于触发 persistent split 与静态 vec 路径;127 这种非 8 对齐的 H 触发 scalar 回退路径。验收标准为相对范数误差< 0.005且atol=rtol=1e-2。test_mhc_pre_big_fuse_matches_reference(tests/mhc/test_mhc_pre_big_fuse.py)对num_splits ∈ {1, 2, 4, 8, 16}全枚举,验证 split 归约与[..., 4, 1]/[..., 4, 4]/[..., H]输出形状。test_mhc_pre_big_fuse_with_prenorm_matches_reference(第 166-202 行)验证dot_mix带/不带前导1维两种输入,以及 kernel 内计算 sqrsum 与参考实现的等价性,输出容差atol=rtol=2e-3(系数)与1e-2(layer_input)。
6. JIT 编译与内核装配
flashinfer.mhc的三个 API 均通过 flashinfer/jit/mhc.py 走 FlashInfer 的 JIT 流水线:gen_mhc_module()把csrc/mhc/mhc_post.cu与csrc/mhc/mhc_pre_big_fuse.cu两个翻译单元打包为一个名为"mhc"的JitSpec,经build_and_load()编译后由get_mhc_module()(functools.cache缓存,flashinfer/mhc.py)加载。
Python 侧的_mhc_post_impl、_mhc_pre_big_fuse_impl、_mhc_pre_big_fuse_with_prenorm_impl分别用@register_custom_op注册为flashinfer::mhc_post/flashinfer::mhc_pre_big_fuse/flashinfer::mhc_pre_big_fuse_with_prenorm自定义算子(mutates_args声明输出张量原地写入),并配套@register_fake_op假算子以支持 torch.compile 等元数据推导;对应的TVM_FFI_DLL_EXPORT_TYPED_FUNC在 C++ 侧导出同名 FFI 函数(csrc/mhc/mhc_post.cu、csrc/mhc/mhc_pre_big_fuse.cu)。
模块已在flashinfer/__init__.py(第 240 行)以from . import mhc as mhc顶层导出,因此直接import flashinfer后即可通过flashinfer.mhc.mhc_post等路径调用。
7. 使用要点小结
- 固定 HC=4:
residual的倒数第二维、post_layer_mix的尾维、comb_res_mix的两个尾维都硬编码为 4,违反即抛异常; - BF16 + SM80:所有核只接受 BF16 残差/输出,float32 仅用于混合系数与 scale/base;测试在 SM80 以下自动跳过;
- 8 对齐的 H 走向量化路径:
mhc_pre_big_fuse系列要求H % 8 == 0(vec8 访存),mhc_post对非对齐 H 会回退到 scalar kernel; - Sinkhorn 迭代:
sinkhorn_repeat控制双随机归一化轮数,默认 20,C++ 侧要求>= 1,更多迭代通常带来更接近双随机矩阵的 comb 系数但增加计算量; - split 归约:当投影 logits 过大需分片计算时使用
num_splits ∈ {1,2,4,8,16},kernel 内部自动跨 split 求和,无需在 Python 侧预归约; - 两类前映射的选择:手头已有
sqrsum(例如与投影一起算好)用mhc_pre_big_fuse,否则用mhc_pre_big_fuse_with_prenorm让 kernel 内部现算,后者还兼容带前导1维的dot_mix。
若需深入内核细节,可直接阅读 csrc/mhc/mhc_post.cu(向量化加载与多路径分发)与 csrc/mhc/mhc_pre_big_fuse.cu(warp 内 Sinkhorn 归约与 block 协作),并以 tests/mhc 中的参考实现作为数值语义的权威对照。
- 大模型
- 深度学习
- 算子库
- 后端
- 高性能计算
【免费下载链接】flashinfer
FlashInfer: Kernel Library for LLM Serving
相关推荐
Gradle Kotlin DSL Samples最佳实践总结:避免常见陷阱的20个技巧
Gradle Kotlin DSL Samples最佳实践总结:避免常见陷阱的20个技巧 Gradle Kotlin DSL Samples是Gradle官方提
如何在 KernelSU 上跑起 LSPosed:3 步装好 ZygiskNext 并配置 Xposed 模块
如何在 KernelSU 上跑起 LSPosed:3 步装好 ZygiskNext 并配置 Xposed 模块 结论先说:LSPosed 和其他现代 Xpose
操作系统驱动开发Kubernetes component-helpers 模块深度解读:面向多组件复用的核心辅助函数库
Kubernetes component helpers 模块深度解读:面向多组件复用的核心辅助函数库 本文以仓库内 staging/src/k8s.io/co
云原生容器编排集群管理微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考