ATB activation 算子源码导航:路由文件、Runner 决策与 Kernel 实现的完整阅读路径
【免费下载链接】ascend-transformer-boost本项目是CANN提供的是一款高效、可靠的Transformer加速库,基于华为Ascend AI处理器,提供Transformer定制化场景的高性能融合算子。项目地址: https://gitcode.com/cann/ascend-transformer-boost
本文以 CANN ascend-transformer-boost(ATB)仓库内的 activation 路由文件 为核心骨架,结合 activation 知识条目 与 src/ops/ops_infer/activation/ 下的真实实现,系统梳理 activation 复合算子的 10 个源码文件、四种 Runner 决策逻辑、ACLNN 封装模式、原生 Ops 调用链、参数定义与 Kernel 目录结构。读者读完可以掌握:activation 算子由路由定位到源码的标准路径、不同平台下 Runner 如何被选择、ACLNN 与原生 Ops 两条执行链路的差异,以及各激活函数 Kernel 的落点。
1. 算子坐标:activation 在 ATB 知识库中的定位
在 ATB Agent 知识库主索引(.agent/knowledge/README.md)中,activation 被归类为infer(推理)类别、M 级复杂度,共10 个文件,支持ACLNN路径。路由文件头部的元信息给出了第一手定义:
分类: infer |复杂度: M |文件数: 10Runner 类型: OpsRunner, ACLNNRunner, Operation |ACLNN: yes预估阅读时间: 5-10 分钟
知识条目(index.md)进一步明确了它的本质:
- 类型:
composite(复合算子),包含 GELU、SiLU/SwiGLU、ReLU、Tanh、Sigmoid 等多种激活函数; - Runner 决策:
ActivationAclnnRunner/GeluAclnnRunner/SwigluForwardAclnnRunner/ActivationOpsRunner四种; - Pipeline: 逐元素单阶段,部分支持量化和 dtype 转换;
- 相关算子:
swiglu_quant(激活+量化)、fast_gelu(GELU 变体)。
路由文件的核心价值在于它是一张"地图":它告诉读者 activation 算子的源码分布在哪些文件、每个文件承担什么角色、按什么顺序阅读最高效、以及 Kernel 与参数定义在哪里。
2. 文件清单:10 个文件的角色分工
路由文件第 1 节完整列出了 activation 涉及的 10 个文件(目录见 src/ops/ops_infer/activation/),按角色分为三类:
| # | 文件 | 角色 |
|---|---|---|
| 1 | activation_aclnn_runner.cpp | ACLNN Runner |
| 2 | activation_aclnn_runner.h | ACLNN Runner |
| 3 | activation_operation.cpp | Operation 定义 |
| 4 | activation_operation.h | Operation 定义 |
| 5 | activation_ops_runner.cpp | Ops Runner |
| 6 | activation_ops_runner.h | Ops Runner |
| 7 | gelu_aclnn_runner.cpp | ACLNN Runner |
| 8 | gelu_aclnn_runner.h | ACLNN Runner |
| 9 | swiglu_forward_aclnn_runner.cpp | ACLNN Runner |
| 10 | swiglu_forward_aclnn_runner.h | ACLNN Runner |
这 10 个文件构成三层架构:
- Operation 层(2 个文件):对外暴露的算子定义,负责参数校验、InferShape、Runner 创建;
- ACLNN Runner 层(6 个文件):封装 CANN 标准 ACLNN API,是 ASCEND_950 等平台的主执行路径;
- Ops Runner 层(2 个文件):走 ATB 原生 Ops API 的执行路径,用于其他平台。
activation 是知识库中少有的同时拥有 3 个 ACLNN Runner 的算子(activation / gelu / swiglu_forward),说明不同激活函数在底层采用了不同的算子封装,而非一个 Runner 通吃所有激活类型。
3. 推荐阅读顺序:从接口到实现
路由文件第 2 节给出的 10 步阅读顺序,实际反映了 activation 算子的代码依赖方向——先看对外接口,再看决策逻辑,最后深入到 Workspace 计算与平台适配:
| 顺序 | 文件 | 重点关注 |
|---|---|---|
| 1 | activation_operation.h | 了解输入输出数量、InferShape 签名 |
| 2 | activation_operation.cpp | CreateRunner() 决策逻辑 |
| 3 | activation_aclnn_runner.h | ACLNN API 封装接口 |
| 4 | gelu_aclnn_runner.h | ACLNN API 封装接口 |
| 5 | swiglu_forward_aclnn_runner.h | ACLNN API 封装接口 |
| 6 | activation_aclnn_runner.cpp | Workspace 计算 + ACLNN API 调用 |
| 7 | gelu_aclnn_runner.cpp | Workspace 计算 + ACLNN API 调用 |
| 8 | swiglu_forward_aclnn_runner.cpp | Workspace 计算 + ACLNN API 调用 |
| 9 | activation_ops_runner.h | 原生 Ops 执行接口 |
| 10 | activation_ops_runner.cpp | 原生 Ops 调用链 + 平台适配 |
这条路径设计合理:activation_operation.h是入口,声明了GetInputNum()、GetOutputNum()、InferShapeCheckImpl、InferShapeImpl、CreateRunner、SetupCheckImpl等核心虚函数签名(见 activation_operation.h),先建立接口心智模型,再在CreateRunner()中看到"平台 + 激活类型"的二维决策,随后分别进入 ACLNN 与 Ops 两条执行路径的细节。
4. 源码路径定位
路由文件第 3 节给出了三个关键路径锚点,这也是 ATB infer 算子的标准定位模式(见知识库 README 第 3.1 节的标准目录结构约定):
- Op 目录:
src/ops/ops_infer/activation/ - Kernel 目录:
src/kernels/kernels/activation - 参数头文件:
include/atb/infer_op_params.h
Kernel 目录(src/kernels/kernels/activation/)按激活函数分子目录组织,每个子目录包含 kernel 实现,必要的子目录还分离了 tiling 逻辑:
| 子目录 | 内容 |
|---|---|
relu/ | relu_kernel.cpp |
gelu/、gelu_forward/ | GELU kernel(后者含 kernel/tiling 分离结构) |
fast_gelu/、faster_gelu_forward/ | GELU 快速近似变体 |
swish/、sigmoid/、log/ | 逐元素激活 kernel |
swiglu_forward/、swiglu_backward/ | SwiGLU 前向/反向(含 tiling) |
tiling/ | activation_tiling.cpp/h 公共 tiling 逻辑 |
从目录结构可以推断,activation 覆盖了 Transformer 推理场景中最常用的激活函数集合;其中 SwiGLU 路径(前向 + 反向)拥有独立的 tiling 与 kernel 实现,复杂度明显高于纯逐元素的 ReLU/Sigmoid 等。
5. 参数定义:ActivationParam 与 ActivationType
参数头文件 include/atb/infer_op_params.h 定义了 activation 所需的两个核心类型。
5.1 ActivationType 枚举
enum ActivationType : int { ACTIVATION_UNDEFINED = 0, // 未定义 ACTIVATION_RELU, // RELU激活类型 ACTIVATION_GELU, // GELU激活类型 ACTIVATION_FAST_GELU, // FAST_GELU激活类型 ACTIVATION_SWISH, // SWISH激活类型 ACTIVATION_LOG, // LOG激活类型 ACTIVATION_SWIGLU_FORWARD, // SWIGLU_FORWARD激活类型 ACTIVATION_SWIGLU_BACKWARD, // SWIGLU_BACKWARD激活类型 ACTIVATION_SIGMOID, // SIGMOID激活类型 ACTIVATION_FASTER_GELU_FORWARD, // FASTER_GELU_FORWARD激活类型 ACTIVATION_MAX, // 枚举最大值, 非激活类型 };枚举注释同时给出了关键约束:
ACTIVATION_FAST_GELU:快速运算的 GELU,对 Tensor 内每个 element 做近似计算,计算速度更快,同时保持较高的准确性;ACTIVATION_SWIGLU_FORWARD:SwiGLU 正向激活函数,Atlas 推理系列产品中只支持 32 位对齐的数据;ACTIVATION_FASTER_GELU_FORWARD:简化后的 FastGelu,计算速度更快;ACTIVATION_SWIGLU_BACKWARD:SwiGLU 正向激活函数的反向,求梯度时使用,只支持 Atlas 800I A2 推理产品。
5.2 ActivationParam 结构体
struct ActivationParam { enum GeLUMode : int { TANH_MODE = 0, // 默认值,使用tanh估算 NONE_MODE, // 原GeLU计算公式 }; ActivationType activationType = ACTIVATION_UNDEFINED; // 激活函数类型 float scale = 1.0f; // SWISH激活函数的参数 int32_t dim = -1; // SWIGLU激活函数的参数 GeLUMode geluMode = TANH_MODE; // GeLU模式选择参数 uint8_t rsv[8] = {0}; // 预留参数 };各字段语义与默认值:
| 字段 | 默认值 | 作用 |
|---|---|---|
activationType | ACTIVATION_UNDEFINED | 必须显式设置,决定走哪条执行路径 |
scale | 1.0f | Swish 激活的缩放参数 |
dim | -1 | SwiGLU 的切分维度,-1表示最后一维 |
geluMode | TANH_MODE | GELU 计算模式:tanh 近似或原始公式 |
rsv[8] | {0} | 预留,保持 ABI 兼容 |
在 activation_operation.cpp 的CreateOperation模板特化中,参数被强校验:activationType必须落在(ACTIVATION_UNDEFINED, ACTIVATION_MAX)开区间内,否则返回ERROR_INVALID_PARAM;dim != -1时仅允许ACTIVATION_SWIGLU_FORWARD/BACKWARD使用(SwiGLU 支持切分维度),其他激活类型一旦携带dim直接报错。
6. Runner 决策:CreateRunner() 的二维分派
CreateRunner()(见 activation_operation.cpp)的核心思想是按平台 × 激活类型二维分派:
std::shared_ptr<Runner> ActivationOperation::CreateRunner(Context &context) const { if (Mki::PlatformInfo::Instance().GetPlatformType() == Mki::PlatformType::ASCEND_950) { if (param_.activationType == atb::infer::ActivationType::ACTIVATION_SWIGLU_FORWARD) { return std::make_shared<SwigluForwardAclnnRunner>(param_); } else if (param_.activationType == atb::infer::ActivationType::ACTIVATION_GELU) { return std::make_shared<GeluAclnnRunner>(param_); } else { return std::make_shared<ActivationAclnnRunner>(param_); } } return std::make_shared<ActivationOpsRunner>(param_); }决策结果可归纳为:
| 平台 | 激活类型 | Runner |
|---|---|---|
| ASCEND_950 | SWIGLU_FORWARD | SwigluForwardAclnnRunner |
| ASCEND_950 | GELU | GeluAclnnRunner |
| ASCEND_950 | 其他(SWISH/SIGMOID 等) | ActivationAclnnRunner |
| 非 950 平台 | 全部 | ActivationOpsRunner(原生 Ops) |
与之配套,CreateOperation在 ASCEND_950 平台下会预加载对应的 ACLNN 动态符号:GELU 加载GeluAclnnRunner::LoadMethod(),SWISH/SIGMOID 加载ActivationAclnnRunner::LoadAclnnFunctions(),SWIGLU_FORWARD 加载SwigluForwardAclnnRunner::LoadMethod(),加载失败返回ERROR_CANN_ERROR。此外,构造函数还会按平台选择不同的 Operation IR:GetOperationIrForActivation950()与GetOperationIrForActivation()(后者通过AtbOperationIrCfg单例按激活类型取 IR,如ActivationOperationRELU、ActivationOperationSWIGLUFORWARD、ActivationOperationSWISH950等),体现 950 平台在 IR 层面的差异化适配。
7. ACLNN Runner 深入:Workspace 计算与 API 调用
以 activation_aclnn_runner.cpp 为例,其设计模式是函数指针 + KernelAdapters 适配器:
aclnnStatus (*ActivationAclnnRunner::fastGeluGetWorkspaceSizeFunc_)(const aclTensor *, aclTensor *, uint64_t *, aclOpExecutor **) = nullptr; aclnnStatus (*ActivationAclnnRunner::fastGeluExecuteFunc_)(void *, uint64_t, aclOpExecutor *, aclrtStream) = nullptr; // gelu / log / relu / sigmoid / swish 的函数指针同理MakeAdaptersByType()按activationType返回{算子名, GetWorkspaceSize 回调, Execute 回调}三元组。GELU 的回调会透传geluMode(NONE_MODE映射为 0,否则为 1):
case infer::ActivationType::ACTIVATION_GELU: return {"gelu", mode = (param.geluMode == infer::ActivationParam::GeLUMode::NONE_MODE) ? 0 : 1 { int64_t modeTmp = mode; return ActivationAclnnRunner::geluGetWorkspaceSizeFunc_(In(vp), &modeTmp, Out(vp), wsSize, executor); }, [](void *ws, uint64_t wsSize, aclOpExecutor *executor, aclrtStream stream) { return ActivationAclnnRunner::geluExecuteFunc_(ws, wsSize, executor, stream); }};Swish 的封装则额外携带scale参数(aclScalar),对应ActivationParam::scale字段。
整个 ACLNN 执行流程遵循 CANN 标准两步式调用:先xxxGetWorkspaceSize计算 workspace 大小并创建aclOpExecutor,再xxxExecute在指定aclrtStream上执行。ATB 通过 src/atb/utils/aclnn_util.h 与 src/atb/kernel_cache/aclnn_executor_cache.cpp 对该流程做统一封装;函数指针通过LoadMethod()/LoadAclnnFunctions()运行时动态加载,规避编译期对具体 ACLNN 头文件的强依赖。gelu_aclnn_runner.cpp与swiglu_forward_aclnn_runner.cpp遵循同样的封装模式,分别对应aclnnGelu与aclnnSwigluForward系列 API。
8. Ops Runner 调用链:原生 Ops 路径与平台适配
activation_ops_runner.cpp 的实现非常精简——继承OpsRunner,在构造函数中构建一个单节点的 Kernel Graph:
ActivationOpsRunner::ActivationOpsRunner(const infer::ActivationParam ¶m) : OpsRunner("ActivationOpsRunner"), param_(param) { kernelGraph_.nodes.resize(1); auto &activationNode = kernelGraph_.nodes.at(0); activationNode.opDesc = RunnerUtil::GetActivationNodeOpDesc(param_); // 普通激活:1 输入 x、1 输出;SWIGLU_BACKWARD:2 输入 (y_grad, x)、1 输出 }关键点:
- 输入输出张量数量与 Operation 层保持一致:普通激活 1 输入 1 输出,
ACTIVATION_SWIGLU_BACKWARD为 2 输入(y_grad、x)1 输出; RunnerUtil::GetActivationNodeOpDesc(param_)(见 src/atb/utils/runner_util.cpp)负责将 ATB 的ActivationParam翻译为底层MkiOpDesc;- 文件末尾通过
REG_RUNNER_TYPE(ActivationOpsRunner)注册 Runner 类型,通过REG_OP_PARAM(AsdOps::OpParam::Activation)绑定底层 AsdOps 参数类型。
这条路径在非 ASCEND_950 平台(含 Atlas 推理系列,如 310P)上作为默认执行方式。与之对应,CheckSwigluForwardInTensor中针对 310P 做了约束:hiddenSize(最后一维)必须是 32 的倍数(HIDDEN_SIZE_DIM_BASE = 32),与参数头文件中"SWIGLU_FORWARD 只支持 32 位对齐数据"的注释相互印证。
9. InferShape 与参数校验:SwiGLU 的特殊处理
InferShapeImpl体现了复合算子的形状推导差异:
| 激活类型 | 输出形状规则 |
|---|---|
SWIGLU_FORWARD | 输出复制输入,但splitDim(dim < 0时转为dim + dimNum)上的维度减半(/ SPLIT_NUM) |
SWIGLU_BACKWARD | 输出形状取inTensor[1](即 x) |
| 其他 | 输出形状与输入一致(逐元素) |
校验逻辑CheckSwigluBackwardInTensor还要求:两个输入dimNum相同,splitDim处输入 1 的维度是输入 0 的 2 倍(SPLIT_NUM * dims[i]),其余维度一致。SetupCheckImpl在运行时对真实 Tensor 做同样的形状一致性检查,保证传入的 Tensor 满足 SwiGLU 的切分语义。
10. 知识条目与快速导航
路由文件第 5~6 节指向知识库中的详细条目与主索引(注意:原文中的相对链接需换算为以仓库根目录为起点的路径):
- 详细知识条目: ops/activation/activation/index.md,状态为
complete,YAML 元信息记录了算子属性(op: {name: "activation", category: "activation", tier: "M", type: "composite"})与源码锚点(src/ops/ops_infer/activation/、src/kernels/kernels/activation/、include/atb/infer_op_params.h); - 主索引: .agent/knowledge/README.md,覆盖 82 个 ATB 算子,可按功能分类、复杂度分层、ACLNN 支持三种维度检索;
- 分类: infer: 在 .agent/knowledge/README.md 的 activation 分类下,activation 与 elewise、softmax、rope、swiglu_quant 等算子并列,其中仅有 activation 与 swiglu_quant 两个激活类算子同时支持 ACLNN 路径。
对 Agent 或开发者而言,定位 activation 的最小路径是:主索引搜索activation→ 读 routing/activation.md 获取文件清单 → 按需读知识条目与源码。这条链路同样适用于知识库中其他 81 个算子。
11. 实战定位速查
结合路由文件与源码,将 activation 算子最常用的定位目标汇总如下:
| 任务 | 目标文件 | 要点 |
|---|---|---|
| 了解算子对外接口 | activation_operation.h | 输入输出数量、InferShape/CreateRunner 签名 |
| 理解 Runner 如何选择 | activation_operation.cpp | 平台 × 激活类型二维分派、IR 选择、参数强校验 |
| 查看参数与枚举定义 | include/atb/infer_op_params.h | ActivationType 全枚举、ActivationParam 全字段与默认值 |
| ACLNN 执行路径 | activation_aclnn_runner.cpp | 函数指针加载、Workspace 计算、GELU mode / Swish scale 透传 |
| 原生 Ops 执行路径 | activation_ops_runner.cpp | 单节点 Kernel Graph、OpDesc 翻译、Runner 注册 |
| 底层 Kernel | src/kernels/kernels/activation/ | 各激活函数的 kernel 与 tiling 实现 |
12. 小结
activation 路由文件篇幅不长,但精准刻画了一个 M 级复合算子的完整知识边界:10 个源码文件、4 种 Runner、3 个 ACLNN 封装、1 个复合激活函数族。顺着"路由文件 → 知识条目 → 源码"的路径可以确认:ATB 在 ASCEND_950 上优先走 ACLNN 标准 API(按激活类型细分为三个 Runner),在其他推理平台上回退到原生 Ops 路径,两者共享同一套 ActivationParam 参数定义与形状推导语义。这种"平台优先 + 类型分派"的 Runner 架构,也是理解 ATB 中其他支持 ACLNN 的 infer 算子(如 softmax、rope、rms_norm)的通用钥匙。
【免费下载链接】ascend-transformer-boost本项目是CANN提供的是一款高效、可靠的Transformer加速库,基于华为Ascend AI处理器,提供Transformer定制化场景的高性能融合算子。项目地址: https://gitcode.com/cann/ascend-transformer-boost
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考