news 2026/9/19 3:09:13

CANN AscendTransformerBoost ActivationOperation C++ Demo 实战:从环境搭建到 GELU / SwiGLU 算子调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN AscendTransformerBoost ActivationOperation C++ Demo 实战:从环境搭建到 GELU / SwiGLU 算子调用

CANN AscendTransformerBoost ActivationOperation C++ Demo 实战:从环境搭建到 GELU / SwiGLU 算子调用

【免费下载链接】ascend-transformer-boost本项目是CANN提供的是一款高效、可靠的Transformer加速库,基于华为Ascend AI处理器,提供Transformer定制化场景的高性能融合算子。项目地址: https://gitcode.com/cann/ascend-transformer-boost

本文围绕ascend-transformer-boost仓库中 example/op_demo/activation/README_en.md 对应的 ActivationOperation C++ 调用示例展开,完整讲解如何配置 CANN 与 NNAL(加速库)运行环境、编译并执行 activation_demo.cpp,并逐段剖析该 demo 中 GELU(FasterGelu)与 SwiGLU 正向两类激活算子的调用流程:从设备初始化、Context/Stream 创建,到输入 Tensor 准备、Setup/Execute执行、Workspace 管理及资源释放。读者读完本文后,能够独立编写、编译并运行基于atb::infer::ActivationParam的激活算子推理示例,并理解其背后的参数校验、Shape 推导与 Runner 分发机制。

一、示例概览:示例能做什么

该示例位于 example/op_demo/activation/,是一个纯 C++ 的加速库算子调用样例,演示了ActivationOperation的两种典型用法:

演示函数激活类型参数设置输出 Shape
RunGeluDemoACTIVATION_FASTER_GELU_FORWARD无额外参数[16, 1024, 4096](与输入一致)
RunSwigluDemoACTIVATION_SWIGLU_FORWARDdim = -1(最后一维切分)[16, 1024, 2048](最后一维减半)

两种算子共享同一个PrepareInTensor输入构造逻辑:生成形状为[BATCH_SIZE, SEQ_LEN, HIDDEN_SIZE] = [16, 1024, 4096]、数值范围在[-100, 100)的随机 float 数据,并通过aclrtMemcpyACL_MEMCPY_HOST_TO_DEVICE方式拷贝到 Device 侧。

其中 SwiGLU 的正向计算本质上是把输入张量在指定维度(dim = -1,即最后一维)一分为二得到ab两个切片,再计算sigmoid(a) * a * b,因此输出最后一维为输入的一半——这也解释了为何 Swiglu 示例的输出 Shape 是[16, 1024, 2048]。该公式可以在 tests/apitest/opstest/python/operations/activation/test_activation.py 的TestSwigluForwardOperation.golden_calc中看到精确的对照实现:

x = in_tensors[0] a, b = x.chunk(2, dim=self.SPLIT_DIM) # SPLIT_DIM = -1 y = torch.sigmoid(a) * a * b

二、运行环境准备:source 两个安装路径

示例的运行依赖 CANN 工具链与 NNAL(加速库)两个软件包的环境变量,使用前必须依次执行 source:

# 1. source CANN 安装路径下的 set_env.sh(默认安装路径) source /usr/local/Ascend/ascend-toolkit/set_env.sh # 2. source NNAL(加速库)安装路径下的 set_env.sh source /usr/local/Ascend/nnal/atb/set_env.sh

关于第二步,README 特别给出了一个替代场景:如果使用加速库源码自行编译,则应改为 source 源码编译输出目录下的环境脚本:

# 从加速库源码编译后,使用源码输出路径下的 set_env.sh source ./ascend-transformer-boost/output/atb/set_env.sh

这一点对开发者非常实用:仓库根目录 CMakeLists.txt 构建完成后,产物会输出到output/atb目录,其中包含了加速库的头文件、动态库以及set_env.sh环境脚本。也就是说,开发者既可以选择安装官方发布的 NNAL 包,也可以先本地编译再直接 source 源码产物,两种方式都能让示例链接到atb头文件与库。

三、编译与运行 Demo

环境准备就绪后,在示例目录下执行:

bash build.sh

README 中特别提示了 C++ ABI 的注意事项:示例的编译必须与加速库发布包使用的cxx_abi保持一致,否则链接期或运行期会出现符号不匹配问题。

  • 使用 cxx_abi=0(默认)时,需要设置D_GLIBCXX_USE_CXX11_ABI=0

    g++ -D_GLIBCXX_USE_CXX11_ABI=0 -I ...
  • 使用 cxx_abi=1 时,改为:

    g++ -D_GLIBCXX_USE_CXX11_ABI=1 -I ...

说明:_GLIBCXX_USE_CXX11_ABI是 GCC 新旧标准库 ABI 的切换宏。加速库预编译产物在特定 ABI 下生成,调用方编译宏必须与之匹配,这是所有基于该仓库的 C++ 示例(包括 example/atb_aclnn、example/multiStream 等)都要遵守的通用约束。-I ...部分需替换为实际的加速库头文件包含路径(一般由set_env.sh导出的ASCEND_HOME_PATH等环境变量决定)。

程序运行成功后,会在终端依次打印两行成功标志:

Gelu Activation demo success! Swiglu_forward Activation demo success!

四、代码剖析:激活算子的完整调用链路

activation_demo.cpp 的主函数完整展示了加速库算子的标准使用范式,共分为四步:环境初始化 → 创建算子并准备参数包 → Setup/Execute → 释放资源。

4.1 入口:初始化 ACL、创建 Context 与 Stream

int main(int argc, char **argv) { CHECK_STATUS(aclInit(nullptr)); int32_t deviceId = 0; CHECK_STATUS(aclrtSetDevice(deviceId)); atb::Context *context = nullptr; CHECK_STATUS(atb::CreateContext(&context)); void *stream = nullptr; CHECK_STATUS(aclrtCreateStream(&stream)); context->SetExecuteStream(stream); RunGeluDemo(context, stream); RunSwigluDemo(context, stream); // 资源释放 CHECK_STATUS(aclrtDestroyStream(stream)); CHECK_STATUS(atb::DestroyContext(context)); // context,全局资源,后释放 CHECK_STATUS(aclFinalize()); return 0; }

关键点:

  • aclInit(nullptr)初始化 ACL 运行时,aclrtSetDevice(deviceId)指定使用 0 号卡;
  • atb::CreateContext(&context)创建加速库上下文,aclrtCreateStream创建 Stream,并通过context->SetExecuteStream(stream)将执行流绑定到上下文;
  • 释放顺序是"先 Stream、再 Context、最后aclFinalize",注释中明确说明 Context 属于全局资源,必须后释放。这个"后创建先释放、先创建后释放"的原则与示例中 Operation 对象的释放时机(见 4.4)一起构成了完整的资源生命周期管理。

4.2 创建激活算子:GELU 与 SwiGLU 的参数设置

atb::Status GeluOperation(atb::Operation **opPtr) { atb::infer::ActivationParam opParam; opParam.activationType = atb::infer::ActivationType::ACTIVATION_FASTER_GELU_FORWARD; CHECK_STATUS(atb::CreateOperation(opParam, opPtr)); return atb::ErrorType::NO_ERROR; } atb::Status SwigluOperation(atb::Operation **opPtr) { atb::infer::ActivationParam opParam; opParam.activationType = atb::infer::ActivationType::ACTIVATION_SWIGLU_FORWARD; opParam.dim = -1; CHECK_STATUS(atb::CreateOperation(opParam, opPtr)); return atb::ErrorType::NO_ERROR; }

两个算子的区别仅在于activationTypedim参数。atb::infer::ActivationParam定义在 include/atb/infer_op_params.h,完整字段如下:

字段类型默认值含义
activationTypeActivationTypeACTIVATION_UNDEFINED激活函数类型(必填)
scalefloat1.0fSWISH 激活函数的缩放参数
dimint32_t-1SWIGLU 激活函数的切分维度
geluModeGeLUModeTANH_MODEGeLU 计算模式(TANH_MODE=0用 tanh 近似,NONE_MODE=1用原公式)
rsv[8]uint8_t{0}预留参数

ActivationType枚举定义在同一文件(infer_op_params.h),支持的类型包括:

ACTIVATION_UNDEFINED = 0 未定义 ACTIVATION_RELU 1 ReLU ACTIVATION_GELU 2 GELU ACTIVATION_FAST_GELU 3 快速 GELU(近似计算,速度快且保持较高精度) ACTIVATION_SWISH 4 Swish ACTIVATION_LOG 5 Log ACTIVATION_SWIGLU_FORWARD 6 SwiGLU 正向 ACTIVATION_SWIGLU_BACKWARD 7 SwiGLU 反向(求梯度,仅 Atlas 800I A2 推理产品支持) ACTIVATION_SIGMOID 8 Sigmoid ACTIVATION_FASTER_GELU_FORWARD 9 简化后的 FasterGelu(计算速度更快) ACTIVATION_MAX 10 枚举最大值,非激活类型

README 对几个核心类型的语义有明确说明:ACTIVATION_FAST_GELU对 Tensor 内每个元素做 GELU 近似计算,速度更快且精度较高;ACTIVATION_FASTER_GELU_FORWARD是进一步简化后的 FastGelu;ACTIVATION_SWIGLU_FORWARD在 Atlas 推理系列产品中只支持 32 位对齐的数据(即最后一维须为 32 的倍数)。

4.3 输入 Tensor 准备与 VariantPack 组装

PrepareInTensor展示了"Host 生成数据 → 申请 Device 内存 → 拷贝"的标准流程:

std::vector<float> inTensorData(BATCH_SIZE * SEQ_LEN * HIDDEN_SIZE); std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<float> dis(-100.0f, 100.0f); for (float &val : inTensorData) { val = dis(gen); } atb::Tensor inTensor; CreateTensor(ACL_FLOAT, aclFormat::ACL_FORMAT_ND, {BATCH_SIZE, SEQ_LEN, HIDDEN_SIZE}, inTensor); CHECK_STATUS(aclrtMemcpy(inTensor.deviceData, inTensor.dataSize, inTensorData.data(), sizeof(float) * inTensorData.size(), ACL_MEMCPY_HOST_TO_DEVICE));

这里用到的CreateTensor辅助函数定义在 example/op_demo/demo_util.h,它完成三件事:填充atb::Tensor.desc(dtype/format/shape)、调用atb::Utils::GetTensorSize计算数据字节数、用aclrtMalloc(..., ACL_MEM_MALLOC_HUGE_FIRST)申请 Device 内存。该头文件还提供了CastOp(调用 Elewise 的ELEWISE_CAST做类型转换)、TransdataOp(ND 与 FRACTAL_NZ 格式互转)、CreateTensorFromVector等可复用的工具函数,以及统一错误打印的CHECK_STATUS宏——宏会根据错误码范围(100000~999999 为 ACL 错误码)区分打印 ACL 还是 ATB 的查错文档指引。

Tensor 准备完成后,通过atb::VariantPack把输入输出 Tensor 打包交给算子:

atb::VariantPack geluVariantPack; CHECK_STATUS(PrepareInTensor(geluVariantPack.inTensors)); // 放入输入 tensor atb::Tensor tensorOut; CreateTensor(ACL_FLOAT, aclFormat::ACL_FORMAT_ND, {BATCH_SIZE, SEQ_LEN, HIDDEN_SIZE}, tensorOut); geluVariantPack.outTensors.push_back(tensorOut); // 放入输出 tensor

注意 Swiglu 的输出 Shape 不同:由于dim = -1切分,输出最后一维是HIDDEN_SIZE / 2 = 2048

CreateTensor(ACL_FLOAT, aclFormat::ACL_FORMAT_ND, {BATCH_SIZE, SEQ_LEN, HIDDEN_SIZE / 2}, tensorOut);

4.4 Setup 与 Execute:两阶段执行模型与 Workspace

加速库算子的执行采用经典的"准备 + 执行"两阶段模型,这也是Operation接口的核心语义:

uint64_t geluWorkspaceSize = 0; CHECK_STATUS(geluOp->Setup(geluVariantPack, geluWorkspaceSize, context)); // 阶段一:准备 uint8_t *geluWorkspacePtr = nullptr; if (geluWorkspaceSize > 0) { CHECK_STATUS(aclrtMalloc((void **)(&geluWorkspacePtr), geluWorkspaceSize, ACL_MEM_MALLOC_HUGE_FIRST)); } geluOp->Execute(geluVariantPack, geluWorkspacePtr, geluWorkspaceSize, context); // 阶段二:执行 CHECK_STATUS(aclrtSynchronizeStream(stream)); // 流同步,等待 device 侧任务计算完成
  • Setup负责基于实际传入的 Tensor 描述做校验、Shape 推导、Tiling 等准备工作,并通过输出参数返回算子所需的Workspace 大小
  • 调用方根据该大小申请 Workspace 内存后,调用Execute真正下发计算任务;
  • Execute是异步的,必须用aclrtSynchronizeStream(stream)做流同步,否则后续立刻释放内存会破坏还在 Device 侧排队执行的任务。

执行完成后,示例按"输入/输出 Tensor → Workspace → Operation 对象"的顺序依次释放:

for (atb::Tensor &inTensor : geluVariantPack.inTensors) { CHECK_STATUS(aclrtFree(inTensor.deviceData)); } for (atb::Tensor &outTensor : geluVariantPack.outTensors) { CHECK_STATUS(aclrtFree(outTensor.deviceData)); } if (geluWorkspaceSize > 0) { CHECK_STATUS(aclrtFree(geluWorkspacePtr)); } CHECK_STATUS(atb::DestroyOperation(geluOp)); // operation,对象概念,先释放

五、源码级原理:从 CreateOperation 到 Runner

Demo 中看似简单的atb::CreateOperationSetup/Execute背后,是 src/ops/ops_infer/activation/ 目录下一套完整的算子实现。理解这些源码能帮助开发者更好地掌握参数约束与平台差异。

5.1 CreateOperation 的参数校验

atb::CreateOperation(const infer::ActivationParam &, Operation **)的模板特化实现在 activation_operation.cpp,主要校验逻辑:

  1. activationType必须落在(ACTIVATION_UNDEFINED, ACTIVATION_MAX)开区间内,否则返回ERROR_INVALID_PARAM
  2. dim参数只允许为 -1:对 SWIGLU 类算子会报 "only support splitting dim -1",对其他激活类型则报 "does not support splitting dim"——这解释了为何 demo 中 Swiglu 必须显式设置dim = -1
  3. 平台差异:在ASCEND_950平台上,仅支持 GELU、SWISH、SIGMOID、SWIGLU_FORWARD 四种类型,且分别走GeluAclnnRunnerActivationAclnnRunnerSwigluForwardAclnnRunner三条 aclnn 加载路径;其他平台则统一创建ActivationOperation对象。

5.2 Shape 推导:SwiGLU 输出为何减半

ActivationOperation::InferShapeImpl(activation_operation.cpp)体现了三种类型的 Shape 规则:

  • SWIGLU_FORWARD:输出 Shape 与输入相同,但splitDimdim为负时先加dimNum归一化)上的尺寸除以 2;
  • SWIGLU_BACKWARD:输出 Shape 直接取第二个输入x的 Shape(即恢复为切分前尺寸,用于梯度回传);
  • 其他激活类型(RELU/GELU/FASTER_GELU 等):输出与输入 Shape 完全一致。

同时,SetupCheckImplCheckSwigluForwardInTensor还包含额外的约束校验:

  • Swiglu 反向算子要求两个输入维数一致,且inTensor0(y_grad)在切分维上尺寸须为inTensor1(x)的一半;
  • Swiglu 正向算子在 310P(Atlas 推理系列)上要求输入最后一维(hidden size)为 32 的倍数(源码中HIDDEN_SIZE_DIM_BASE = 32),与 README 中"只支持 32 位对齐数据"的说明相互印证;
  • dim的取值范围为[-dimNums, dimNums - 1],负值表示从最高维开始计数。

5.3 Runner 分发与 Kernel 图构建

ActivationOperation::CreateRunner(activation_operation.cpp)按平台与类型分发 Runner:ASCEND_950平台走各类 aclnn Runner,其他平台走ActivationOpsRunner。而 activation_ops_runner.cpp 展示了ActivationOpsRunner如何把算子描述成一张单节点 Kernel 图:节点数为 1,opDescRunnerUtil::GetActivationNodeOpDesc(param_)生成,并根据类型决定输入数量——普通激活 1 个输入,SWIGLU_BACKWARD则按(y_grad, x)顺序挂接 2 个输入。

从 Kernel 实现侧看,仓库在 src/kernels/kernels/activation/ 下按算子类型分目录维护了对应的 Ascend 算子实现(tiling 与 kernel 代码),例如 demo 使用的 FasterGelu 对应 faster_gelu_forward/ 目录,其中tiling/负责切分策略计算、kernel/负责 Device 侧向量计算。这与 demo 中Setup阶段返回 Workspace 大小、Execute阶段执行 Kernel 的流程一一对应。

六、结果验证:结合 Python 用例对照精度

README 末尾的"Remarks"部分明确指出:示例中生成的数据(随机数)不代表实际业务场景,若需要了解真实的数据生成与精度校验方式,请参考根目录下的 Python 用例:

tests/apitest/opstest/python/operations/activation/

该目录下的 test_activation.py 基于operation_test.OperationTest框架,为每种激活类型提供了golden_calc 参考实现,可作为 C++ demo 输出正确性的对照标准:

测试类activationType输入构造golden 公式
TestActivationOperation4(SWISH)torch.rand(2, 3, 5)bfloat16x / (1 + exp(-x * scale))
TestSwigluForwardOperation6(SWIGLU_FORWARD)(8192, 1, 3904)float16a, b = x.chunk(2, -1); sigmoid(a) * a * b
TestSwigluBackwardOperation7(SWIGLU_BACKWARD)y_grad(8192, 1, 1952)+ x(8192, 1, 3904)float32分别计算b * y_grad * swiglu_grad(a)y_grad * swiglu(a)后按 -1 维拼接
TestFasterGeluForwardNd*9(FASTER_GELU_FORWARD)多种 Shape、fp16/fp32/bf16、数据范围 [-5, 5]get_golden_data(float_in_tensors)

这些用例同时给出了多种可运行的输入 Shape 组合(如[1, 1024][8, 5504][8192, 5504][1123, 4032]等),并且用例会在特定 SoC(如 Ascend910B / Ascend310B)上做条件判断,开发者可以把 C++ demo 的输出与 golden 结果对比,验证算子精度。此外,仓库 tests/high_level_test/ActivationOperation/ 下还有基于 CSV 的高层测试用例集,可作为更多参数组合的参考。

七、小结:Demo 背后的通用范式

通过对 activation_demo.cpp 的完整剖析,可以提炼出加速库 C++ 调用的通用四步范式,它同样适用于仓库 example/op_demo 下的其他算子示例(如 rms_norm、rope、linear 等):

  1. 环境与资源:source CANN/NNAL 的set_env.shaclInitaclrtSetDeviceatb::CreateContextaclrtCreateStreamSetExecuteStream
  2. 算子与数据:填充对应的atb::infer::*Param结构体 →atb::CreateOperation创建算子 → 构造输入输出 Tensor 并装入atb::VariantPack
  3. 两阶段执行Setup获取 Workspace 大小 → 申请 Workspace →Execute下发任务 →aclrtSynchronizeStream同步;
  4. 资源释放:先释放 Tensor 内存与 Workspace,再DestroyOperation,最后按"Stream → Context → aclFinalize"的顺序收尾。

理解这套范式后,读者可以在此基础上进一步探索 src/ops/ops_infer/activation/ 的算子实现细节(参数校验、Shape 推导、Runner 分发),或通过 tests/apitest/opstest/python/operations/activation/test_activation.py 的 golden 实现来校验自研调用的正确性。

【免费下载链接】ascend-transformer-boost本项目是CANN提供的是一款高效、可靠的Transformer加速库,基于华为Ascend AI处理器,提供Transformer定制化场景的高性能融合算子。项目地址: https://gitcode.com/cann/ascend-transformer-boost

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 3:08:53

未发布研究模型插指令,TaoToken Key 跑摘要任务看消耗

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 3:08:25

车辆GPS定位系统如何实现视频实时监控?从选型到部署避坑指南

干这行久了你会发现一个现象&#xff1a;车队装GPS定位系统&#xff0c;装的时候觉得"能看车在哪儿"就够了&#xff0c;真正用起来才意识到&#xff0c;轨迹只是基础&#xff0c;视频画面才是刚需。尤其处理事故定责、疲劳驾驶投诉、货损纠纷的时候&#xff0c;光有一…

作者头像 李华
网站建设 2026/9/19 3:06:02

魔兽世界 WoTLK 私服搭建完整指南:AzerothCore 从源码到开服

魔兽世界 WoTLK 私服搭建完整指南:AzerothCore 从源码到开服 【免费下载链接】azerothcore-wotlk Complete Open Source and Modular solution for MMO 项目地址: https://gitcode.com/GitHub_Trending/az/azerothcore-wotlk AzerothCore-WoTLK 是一款完整的魔兽世界《燃…

作者头像 李华