PaddlePaddle PHI Fusion Kernel 开发指南:命名空间、后端适配与 API 设计规范
【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle
本文基于 paddle/phi/kernels/fusion/README.md 展开,并结合 Paddle(飞桨)核心框架中 PHI Kernel 体系的真实源码进行佐证与扩充。Fusion Kernel(融合算子内核)是飞桨 PHI 内核体系中的一类特殊 Kernel:它将多个算子组合合并为一次底层调用,用于在特定硬件后端上加速整体计算。阅读本文,你将掌握飞桨 Fusion Kernel 的三大核心设计约束(不推荐暴露 Python API、不要求全后端实现、必须位于
phi/fusion命名空间),理解其在 dy2static/静态图模式中的调用方式,并学会如何遵循后端命名后缀规范(如fused_matmul_onednn、fused_fc_xpu)组织多后端代码。
一、背景:什么是 Fusion Kernel,为什么它与普通 Kernel 不同
在飞桨 PHI(Paddle High-efficiency Infrastructure)内核体系中,Kernel 是算子(Op)在具体设备上执行的最小计算单元。绝大多数 Kernel 与 Python API 一一对应:用户在动态图或静态图里调用paddle.add、paddle.matmul,框架最终派发到对应的 PHI Kernel 上执行。
Fusion Kernel 则是一类特化的 Kernel。它的典型特征是把多个连续算子的计算合并进同一次底层库调用或同一段 GPU/CPU/XPU 代码中执行,例如:
- 把
matmul + bias_add + activation合并为一次融合 GEMM; - 把
scale + bias + relu合并为一次融合激活; - 把
LayerNorm + residual + dropout合并为一次融合前向; - 把 FlashAttention、多头注意力等大算子整体下沉为专用融合实现。
从源码结构看,飞桨把这类 Kernel 单独收敛在 paddle/phi/kernels/fusion/ 目录下,并按照后端划分为多个子目录:
| 子目录 | 目标后端 | 典型实现 |
|---|---|---|
cpu/ | CPU | fused_elemwise_activation_kernel.cc、fusion_gru_kernel.cc等 |
gpu/ | NVIDIA GPU(CUDA) | fc_kernel.cu、fused_bias_dropout_residual_layer_norm_kernel.cu等 |
onednn/ | oneDNN(含 CPU 与 Intel GPU 场景) | fused_matmul_kernel.cc、fused_conv_kernel.cc等 |
xpu/ | 百度昆仑 XPU | fc_xpu_kernel.cc、multi_encoder_xpu_kernel.cc等 |
cutlass/ | NVIDIA GPU(基于 CUTLASS 模板库) | fused_conv2d_add_act_kernel.cu、memory_efficient_attention_kernel.cu等 |
fp8_gemm/ | NVIDIA GPU(FP8 GEMM) | fp8_gemm_with_cublasLt/cublaslt_gemm.h等 |
正因为 Fusion Kernel 面向“合并计算 + 特定后端加速”,它的接口形态、发布方式与后端覆盖策略都不同于普通 Kernel。原文档 README.md 用三组规则给出了明确界定,下面逐条展开并结合源码解读。
二、规则一:不为 Fusion Kernel 实现 Python API,通过图优化/静态图调用
原文档首先强调:不推荐为 Fusion Kernel 实现 Python API。原因非常直接——Fusion Kernel 通常拥有大量输入/输出参数(例如把 bias、scale、两个 max 指针、多个可选项都收进来),如果暴露为 Python API,用户使用和理解的门槛都会很高,参数顺序、可选参数语义也难以与常规算子对齐。
对应地,飞桨推荐 Fusion Kernel 的调用方式是由 pass(图优化)在 dy2static(动转静)模式或静态图模式下自动完成匹配与替换:
- 用户仍然编写普通算子组合(如
matmul → add → relu); - 动转静或静态图的图优化 pass 识别出可融合子图;
- pass 将子图替换为对应的 Fusion Op,并在运行时派发到 Fusion Kernel。
这种设计把“融合”从用户手工操作中剥离出来,由框架自动完成,用户无感知地获得性能收益。
原文档同时给出了第二条子规则,规定了Kernel 间的复用方向:
不推荐在其它 Kernel 的实现中复用(reuse)Fusion Kernel;但推荐 Fusion Kernel 的实现复用其它 Kernel。
这一约束的逻辑在于:Fusion Kernel 往往针对特定后端做了强特化(如直接调用xblas::fc_fusion等底层融合接口),参数语义与普通 Kernel 差异很大,如果普通 Kernel 依赖它,会形成脆弱的反向耦合;反过来,Fusion Kernel 在“无法进一步融合/降级”的分支路径上可以回退调用普通 Kernel,作为保底实现,这是允许且推荐的降级策略。
三、规则二:不要求全后端实现,用后端后缀命名
与普通 Kernel 不同,Fusion Kernel不要求在所有设备(后端)上都有实现。原因在原文档中写得很清楚:Fusion Kernel 通常是为“在某个后端上加速组合运算”而生的,如果要求每个后端都实现一遍,开发与维护成本过高。
由此派生出两条实践约束:
- 不推荐实现一个只抛异常的伪 Kernel(pseudo kernel)来“占位”。如果某个后端当前不需要该融合,就直接不实现,而不是放一个
PADDLE_THROW(Unimplemented(...))的空壳。占位代码只会增加维护负担和误导性(用户可能以为支持,运行时才报错)。 - 如果 Kernel 只在某几个后端实现,建议在 Kernel 名称中加上后端后缀,原文档给出的示例正是:
fused_matmul_onednn:oneDNN 后端的融合矩阵乘;fused_fc_xpu:XPU 后端的融合全连接。
这一命名惯例在 paddle/phi/kernels/fusion/ 目录中得到了大规模印证:
- 在 xpu/ 下,几乎每个文件都以
_xpu_kernel.cc结尾,如 fc_xpu_kernel.cc、add_act_xpu_kernel.cc、multi_encoder_xpu_kernel.cc、weight_only_linear_kernel_xpu.cc等; - 在 onednn/ 下,则是一组
fused_*内核,如fused_matmul_kernel.cc、fused_conv_kernel.cc、fused_elementwise_kernel.cc等; - GPU 端以
fused_*前缀 +.cu为主,如fused_bias_dropout_residual_layer_norm_kernel.cu、fused_gemm_epilogue_kernel.cu、fused_rope_kernel.cu。
3.1 源码印证:fused_matmul_onednn风格
以 oneDNN 的融合矩阵乘为例,onednn/fused_matmul_kernel.cc 中,实现类FusedMatmulOneDNNHandler与主内核FusedMatmulKernel都位于phi::fusion命名空间内(第 24-25 行namespace phi { namespace fusion {),文件末尾的注册宏为:
PD_REGISTER_KERNEL( fused_matmul, /* backend */, ALL_LAYOUT, phi::fusion::FusedMatmulKernel, /* dtypes */);这种fused_<op>_<backend>的命名方式让后端实现一目了然,也让 pass 在进行算子替换时可以按后端精确检索目标 Kernel。
3.2 源码印证:fused_fc_xpu风格与多 dtype 分派
XPU 端的全连接融合内核 xpu/fc_xpu_kernel.cc 是一个很有代表性的“单后端融合 Kernel”案例:
- 内核入口为
phi::fusion::FcXPUKernel(第 24-25 行进入phi::fusion命名空间),注册名为fc_xpu(第 497-504 行PD_REGISTER_KERNEL(fc_xpu, XPU, ALL_LAYOUT, phi::fusion::FcXPUKernel, float, phi::float16, int8_t, phi::bfloat16)); - 它支持
x/w/out三种张量类型与内部T_GEMM计算类型解耦,并在运行时按x.dtype()、w.dtype()、out_dtype的组合进行分派(第 358-492 行),覆盖 FP32/FP16/BF16/INT8/INT16 等多种混合精度组合; - 针对 XPU 硬件特性做了专门优化分支,例如在
PADDLE_WITH_XPU_XRE5条件下(第 93-289 行)使用xblas::FcFusionTensor/FcFusionDesc/FcFusionEpilogue组合调用fc_fusion,甚至支持通过环境变量XPU_PADDLE_FC_BFLOAT16_XTE开启 bf16 的 XTE(Tensor Engine)加速路径——这正是“Fusion Kernel 为特定后端加速而生”的典型体现。
对照同一目录下的 GPU 实现 gpu/fc_kernel.cu(注册名fc,命名空间同为phi::fusion),可以看到:同一个融合语义(全连接 + 可选 bias/activation/量化参数),在不同后端拥有独立命名、独立实现、独立注册的 Kernel,后端之间互不强制、互不阻塞。
四、规则三:Fusion Kernel 必须位于phi/fusion命名空间
原文档第三条规则非常简短但至关重要:
Fusion Kernel 需要位于
phi/fusion命名空间中。
也就是说,所有 Fusion Kernel 的 C++ 实现都应写在
namespace phi { namespace fusion { // kernel implementation } // namespace fusion } // namespace phi这样的命名空间层级中。这一约束的工程意义在于:
- 语义隔离:
phi/fusion命名空间把“融合类内核”与普通算子内核(通常位于phi或phi::funcs等)明确区分,防止同名符号冲突; - 检索与注册一致:Kernel 注册宏(
PD_REGISTER_KERNEL)中通过phi::fusion::XxxKernel引用实现函数,命名空间既是实现归属也是注册时的符号地址; - 与目录结构对应:从源码结构看,
paddle/phi/kernels/fusion/目录与phi::fusion命名空间一一对应,属于飞桨 PHI 内核组织约定的一部分。
4.1 源码印证
- gpu/fc_kernel.cu 第 19-20 行:
PD_REGISTER_KERNEL(fc, GPU, ALL_LAYOUT, phi::fusion::FCKernel, float, double, phi::float16) {}; - xpu/fc_xpu_kernel.cc 第 24-25 行与第 494-495 行:
namespace phi { namespace fusion { ... } },注册时使用phi::fusion::FcXPUKernel; - onednn/fused_matmul_kernel.cc 第 24-25 行与第 607-608 行:同样以
phi::fusion包裹并在注册宏中引用。
三个后端、三种实现,命名空间规范完全一致——这是检验一个 Kernel 是否“按 Fusion 规范开发”的最直接判据。
五、从源码结构看 Fusion Kernel 的实践要点
结合 fusion/ 目录的整体组织,可以总结出若干值得新内核开发者参考的实践规律:
- 按后端分目录:
cpu/、gpu/、onednn/、xpu/、cutlass/、fp8_gemm/各自独立,后端专属实现只放进对应目录,互不干扰,也天然满足“不要求全后端实现”的规则。 - 命名传达信息:文件名与注册名同时携带“融合语义 + 后端”双重信息(
fused_bias_dropout_residual_layer_norm_kernel.cu、fc_xpu、fused_matmul等),便于图优化 pass 与开发者快速定位。 - 一个融合语义可有多个后端变体:如
fc(GPU)、fc_xpu(XPU)、fused_matmul(oneDNN)并存,各自独立注册,Kernel 名不冲突。 - 复用与降级:Fusion Kernel 内部可在不支持的分支上回退或组合普通 Kernel(符合“融合实现复用其它 Kernel”的方向),而不是反向让普通 Kernel 依赖融合实现。
- 不写伪实现:未覆盖的后端不做注册、不写抛异常的空 Kernel,保持注册表干净。
六、开发 Fusion Kernel 的速查清单
如果你要在飞桨仓库中新增一个 Fusion Kernel,可以按以下清单自查,全部符合即满足 fusion/README.md 的规范:
| 检查项 | 规范要求 | 依据 |
|---|---|---|
| 命名空间 | 实现位于phi::fusion内,注册宏引用phi::fusion::XxxKernel | gpu/fc_kernel.cu、xpu/fc_xpu_kernel.cc |
| Python API | 不暴露独立 Python API,由 dy2static/静态图 pass 自动匹配替换 | 原文档规则一 |
| Kernel 复用方向 | 不被他核复用;自己可复用其它普通 Kernel 做降级 | 原文档规则一 |
| 后端覆盖 | 不要求全后端;未实现的后端不注册、不写抛异常伪 Kernel | 原文档规则二 |
| 后端命名 | 仅部分后端实现时,名称加后端后缀,如fused_matmul_onednn、fused_fc_xpu | 原文档规则二、onednn/fused_matmul_kernel.cc、xpu/fc_xpu_kernel.cc |
| 目录组织 | 后端实现放入paddle/phi/kernels/fusion/<backend>/对应子目录 | fusion/ 目录结构 |
| 注册 | 使用PD_REGISTER_KERNEL完成 Kernel 注册并声明支持的数据类型 | gpu/fc_kernel.cu |
七、总结
Fusion Kernel 是飞桨 PHI 内核体系中的“性能特化层”,它的存在价值在于把常见算子组合在特定硬件后端上合并为一次高效调用。原文档 paddle/phi/kernels/fusion/README.md 用三组规则界定了它与普通 Kernel 的本质区别:
- 不暴露 Python API——通过 dy2static/静态图 pass 自动调用,Kernel 复用方向为“融合复用普通、普通不依赖融合”;
- 不要求全后端实现——不写伪 Kernel,按需实现并在命名中加入后端后缀(
fused_matmul_onednn、fused_fc_xpu); - 统一收归
phi/fusion命名空间——实现、注册与目录结构三者一致。
这三条规则在 paddle/phi/kernels/fusion/ 的 CPU、GPU、oneDNN、XPU、CUTLASS、FP8 GEMM 等实现中得到了系统性贯彻。理解并遵守这些约束,是参与飞桨融合算子开发、或基于飞桨源码做二次性能优化的前提。
【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice (『飞桨』核心框架,深度学习&机器学习高性能单机、分布式训练和跨平台部署)项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考