CANN ascend-transformer-boost RmsNormOperation C++ Demo 实战指南:从环境配置到 Qwen / DeepSeek 模型场景
2026/9/19 21:42:00 网站建设 项目流程

CANN ascend-transformer-boost RmsNormOperation C++ Demo 实战指南:从环境配置到 Qwen / DeepSeek 模型场景

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

本篇技术指南以 CANN ascend-transformer-boost 仓库中 rms_norm 示例目录 为骨架,完整讲解 RmsNormOperation(RMS 归一化算子)的 C++ 调用方式:包括 CANN 与 NNAL 双环境变量配置、build.sh编译运行流程、RmsNormParam参数结构逐字段解析,以及面向 Llama、Qwen、DeepSeek 等主流模型场景的 5 个可直接运行的 demo 规格。读完本文,你将能够独立配置环境、编译并运行 rms_norm 系列示例,并具备依据模型隐藏层维度自行修改 demo 的能力。

RMSNorm 与 RmsNormOperation 简介

RMSNorm(Root Mean Square Layer Normalization)是 Llama、Qwen、DeepSeek 等主流 Transformer 模型中广泛使用的归一化层,相比 LayerNorm 省去了均值中心化步骤,只基于均方根对输入进行缩放,计算开销更低。在 CANN ascend-transformer-boost 加速库中,该能力由RmsNormOperation提供,其参数结构体atb::infer::RmsNormParam定义在 include/atb/infer_op_params.h。

从定义可以看出,RmsNormOperation 是一个能力相当完整的融合算子:

  • 三种归一化模式layerType):RMS_NORM_NORM(标准 NORM)、RMS_NORM_PRENORM(前置归一化)、RMS_NORM_POSTNORM(后置归一化);
  • 精度模式precisionMode):HIGH_PRECISION_MODE(中间计算使用 float)与HIGH_PERFORMANCE_MODE(中间计算使用 float16);
  • 模型公式选择modelType):LLAMA_MODEL(Llama 系 RMSNorm 公式,默认)与GEMMA_MODEL(Gemma 系公式);
  • 量化支持quantType):当前支持QUANT_UNQUANTQUANT_INT8
  • 前置/后置归一化还支持偏置叠加(hasBias)与各自的 epsilon 配置。

环境准备:source 双环境变量

在编译运行 demo 之前,需要先加载 CANN 与 NNAL(Ascend Transformer Boost 加速库)两个包的运行环境,README 给出了两条 source 命令:

  1. 加载 CANN 工具包环境:

    source /usr/local/Ascend/ascend-toolkit/set_env.sh
  2. 加载 NNAL 加速库环境:

    source /usr/local/Ascend/nnal/atb/set_env.sh

特别说明:如果是从加速库源码自行编译构建(而非安装预编译包),则应 source 源码编译产物的环境脚本,例如:

source ./ascend-transformer-boost/output/atb/set_env.sh

环境脚本还会顺带设置ATB_HOME_PATHASCEND_HOME_PATH两个关键环境变量,它们正是后面编译脚本中定位头文件与库文件路径的依据(见下文 build.sh 分析)。

编译与运行:build.sh 全流程解读

环境就绪后,在示例目录下执行一条命令即可完成编译并运行:

bash build.sh

以 example/op_demo/rms_norm/build.sh 为例,脚本内部做了两件事:

第一步:探测 cxx_abi 并编译。脚本通过 Python 探测当前 torch 是否以 C++11 ABI 编译(有 torch 时按其返回值,无 torch 时默认输出 1),随后调用 g++ 完成编译:

g++ -D_GLIBCXX_USE_CXX11_ABI=${cxx_abi} \ -I "${ATB_HOME_PATH}/include" -I "${ASCEND_HOME_PATH}/include" \ -L "${ATB_HOME_PATH}/lib" -L "${ASCEND_HOME_PATH}/lib64" \ rms_norm_demo.cpp ../demo_util.h -l atb -l ascendcl -o rms_norm_demo

第二步:运行生成的二进制./rms_norm_demo

编译脚本默认编译并运行rms_norm_demo.cpp。如需编译其他 demo(例如rms_norm_qwen_demo_0.cpp),需要将编译命令中的源文件替换为对应的.cpp文件名。

cxx_abi 与 D_GLIBCXX_USE_CXX11_ABI 的匹配

_GLIBCXX_USE_CXX11_ABI宏决定 C++ 标准库字符串等类型采用 ABI 0(旧版std::string)还是 ABI 1(C++11std::string),必须与加速库链接时使用的 ABI 保持一致,否则链接或运行时会报符号不匹配错误。README 给出了两种显式用法:

  • 使用cxx_abi=0(默认)时:

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

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

build.sh中的自动探测逻辑会在没有 torch 的环境里回退为 1,因此当出现 ABI 相关链接错误时,可通过显式指定宏并调整编译选项排查。

核心代码走读:rms_norm_demo.cpp 调用全流程

example/op_demo/rms_norm/rms_norm_demo.cpp 是标准 NORM 场景的默认示例,其调用流程完整展示了 ATB 算子 C++ 编程的"五步法",对编写其他 ATB 算子调用代码具有通用参考价值:

  1. 初始化 ACL 与设备aclInit(nullptr)初始化 ACL 运行环境,aclrtSetDevice(DEVICE_ID)指定使用 0 号设备,atb::CreateContext(&context)创建 ATB 上下文,aclrtCreateStream创建 stream,最后通过context->SetExecuteStream(stream)将执行流绑定到上下文。

  2. 创建算子:填充atb::infer::RmsNormParam后调用atb::CreateOperation(param, &rmsnormOp)生成算子实例:

    atb::infer::RmsNormParam param; param.layerType = atb::infer::RmsNormParam::RmsNormType::RMS_NORM_NORM; param.normParam.quantType = atb::infer::QuantType::QUANT_UNQUANT; atb::CreateOperation(param, rmsNormOp);
  3. 准备输入输出 Tensor:通过公共工具头文件 example/op_demo/demo_util.h 中的CreateTensorFromVector在设备侧分配内存并把 host 数据搬入,随后组装atb::VariantPack,将输入{x, gamma}与输出{tensorOut}分别放入inTensors/outTensors

  4. Setup 获取 workspace 大小并执行:先调用rmsnormOp->Setup(variantPack, workspaceSize, context)完成输入输出校验并计算工作空间大小,再用aclrtMalloc分配 workspace,随后调用rmsnormOp->Execute(variantPack, workspacePtr, workspaceSize, context)真正下发计算,最后aclrtSynchronizeStream(stream)等待设备侧任务完成。

  5. 资源释放:依次释放输入输出 Tensor 的 device 内存与 workspace、atb::DestroyOperation销毁算子、销毁 stream 与 context、aclFinalize()收尾。注意代码注释强调的顺序:operation 对象先释放,context 全局资源后释放

demo_util.h还封装了CHECK_STATUS错误处理宏(区分 ACL 错误码与 ATB 错误码并打印排查指引)、CreateTensor(依据 shape 计算数据大小并分配设备内存)、CastOp(调用 Elewise 的ELEWISE_CAST完成数据类型转换)与TransdataOp(调用ND_TO_FRACTAL_NZ完成 ND 到 NZ 格式转换)等通用工具,其他算子 demo 均复用了这套工具,可作为统一参考。

RmsNormParam 参数详解:字段、取值范围与默认值

以下字段来自 include/atb/infer_op_params.h 的源码定义,是理解与调整 demo 参数的基础:

参数类型默认值说明
layerTypeRmsNormTypeRMS_NORM_UNDEFINED归一化类型:RMS_NORM_NORM(标准 NORM)、RMS_NORM_PRENORM(PRENORM)、RMS_NORM_POSTNORM(POSTNORM)
normParam.quantTypeQuantTypeQUANT_UNQUANTNORM 场景量化类型,当前支持QUANT_UNQUANT(不量化)与QUANT_INT8(int8 量化)
normParam.epsilonfloat1e-5归一化时加在分母上防止除零的小量
normParam.rstdboolfalse置 true 时使用训练侧 rmsnormforward 算子,仅 Atlas 800I A2 推理产品支持;不可与precisionModemodelType同时设置,量化场景不支持
normParam.precisionModePrecisionModeHIGH_PRECISION_MODE中间计算精度:默认 float;HIGH_PERFORMANCE_MODE使用 float16(输入仅支持 float16);不可与rstdmodelType同时设置,量化场景配置该参数将返回ERROR_INVALID_PARAM
normParam.modelTypeModelTypeLLAMA_MODEL计算公式:Llama 系 RMSNorm 公式(默认)或 Gemma 系公式;不可与rstdprecisionMode同时启用,量化场景不支持
preNormParam/postNormParamPreNormParam/PostNormParamPRENORM / POSTNORM 场景参数,含quantTypeepsilon(默认1e-5)、hasBias(是否叠加偏置,默认 false)

QuantType枚举定义在同文件第 45~57 行:QUANT_UNDEFINED = QUANT_UNQUANT = 0(不量化)、QUANT_INT4 = 1(暂不支持)、QUANT_INT8 = 2(int8 量化)、QUANT_INT16 / QUANT_FLOAT8 / QUANT_FLOAT16(均暂不支持)。

需要特别留意算子实现侧的校验规则(见 src/ops/ops_infer/rms_norm/rms_norm_operation.cpp):layerTypequantType的组合存在约束,例如RMS_NORM_PRENORM/RMS_NORM_POSTNORMQUANT_INT8组合非法时会返回带明确提示的报错;另外所有输入输出 Tensor 的最后一维大小必须相等,且Atlas 推理系列产品不支持 bf16 类型数据

五个 Demo 场景:参数与输入输出规格全表

示例目录共提供 5 个 demo,分别对应不同模型与隐藏层维度的典型场景。除默认的rms_norm_demo.cpp外,其余 demo 编译时需在 build.sh 中把源文件替换为对应.cpp文件名,并且仅适用于Atlas A2/A3 训练系列产品、Atlas 800I A2 推理产品、Atlas A3 推理系列产品

rms_norm_demo.cpp(通用基础场景)

默认编译脚本可直接编译运行。参数设置:

参数
layerTypeatb::infer::RmsNormParam::RmsNormType::RMS_NORM_NORM
normParam.quantTypeatb::infer::QuantType::QUANT_UNQUANT
epsilon1e-5

输入:

Tensor数据类型数据格式Shape
xfloat16nd[4, 1024, 5120]
gammafloat16nd[5120]

输出:

Tensor数据类型数据格式Shape
outputfloat16nd[4, 1024, 5120]

该场景对应代码中DIM_0 = 4DIM_1 = 1024DIM_2 = 5120的常量定义,输入数据全部以 2.0 填充,gamma为一维 [5120] 的逐通道缩放参数,输出与输入x形状一致。

rms_norm_qwen_demo_0.cpp(Qwen 批量场景)

编译时替换源文件为rms_norm_qwen_demo_0.cpp。参数设置:

参数
layerTypeatb::infer::RmsNormParam::RmsNormType::RMS_NORM_NORM
normParam.quantTypeatb::infer::QuantType::QUANT_UNQUANT
epsilon1e-6

输入:

Tensor数据类型数据格式Shape
xbf16nd[1024, 5120]
gammabf16nd[5120]

输出:

Tensor数据类型数据格式Shape
outputbf16nd[1024, 5120]

rms_norm_qwen_demo_1.cpp(Qwen 单 token 场景)

编译时替换源文件为rms_norm_qwen_demo_1.cpp。参数设置:

参数
layerTypeatb::infer::RmsNormParam::RmsNormType::RMS_NORM_NORM
normParam.quantTypeatb::infer::QuantType::QUANT_UNQUANT
epsilon1e-6

输入:

Tensor数据类型数据格式Shape
xbf16nd[1, 5120]
gammabf16nd[5120]

输出:

Tensor数据类型数据格式Shape
outputbf16nd[1, 5120]

rms_norm_qwen_demo_2.cpp(Qwen 小批量场景)

编译时替换源文件为rms_norm_qwen_demo_2.cpp。参数设置:

参数
layerTypeatb::infer::RmsNormParam::RmsNormType::RMS_NORM_NORM
normParam.quantTypeatb::infer::QuantType::QUANT_UNQUANT
epsilon1e-6

输入:

Tensor数据类型数据格式Shape
xbf16nd[5, 5120]
gammabf16nd[5120]

输出:

Tensor数据类型数据格式Shape
outputbf16nd[5, 5120]

rms_norm_deepseek_demo_0.cpp(DeepSeek 大 batch 场景)

编译时替换源文件为rms_norm_deepseek_demo_0.cpp。参数设置:

参数
layerTypeatb::infer::RmsNormParam::RmsNormType::RMS_NORM_NORM
normParam.quantTypeatb::infer::QuantType::QUANT_UNQUANT
epsilon1e-6

输入:

Tensor数据类型数据格式Shape
xfloat16nd[512, 7168]
gammafloat16nd[7168]

输出:

Tensor数据类型数据格式Shape
outputfloat16nd[512, 7168]

rms_norm_deepseek_demo_1.cpp(DeepSeek 小 batch 场景)

编译时替换源文件为rms_norm_deepseek_demo_1.cpp。参数设置:

参数
layerTypeatb::infer::RmsNormParam::RmsNormType::RMS_NORM_NORM
normParam.quantTypeatb::infer::QuantType::QUANT_UNQUANT
epsilon1e-6

输入:

Tensor数据类型数据格式Shape
xfloat16nd[32, 7168]
gammafloat16nd[7168]

输出:

Tensor数据类型数据格式Shape
outputfloat16nd[32, 7168]

场景对比与改造要点

从上述规格可以总结出规律:隐藏层维度(最后一维)与模型强相关——Qwen 系列为 5120,DeepSeek 系列为 7168,二者均与对应模型的 hidden_size 对齐;gamma的形状始终等于隐藏层维度;输出形状与输入x完全一致。因此,当需要适配其他模型时,只需同步修改x的 shape、gamma的 shape(及DIM_*常量)即可,无需改动算子创建与执行逻辑。同时注意 Qwen 系列 demo 使用 bf16,DeepSeek 与基础 demo 使用 float16,须与产品对 bf16 的支持情况(Atlas 推理系列不支持 bf16)核对后再选用。

数据生成与结果验证说明

README 明确提醒:示例中生成的数据(如以常量 2.0 填充的输入)不代表实际场景,仅用于验证调用流程与算子可运行性。若需要贴近真实分布的数据生成与精度对比参考,请查看根目录下的 Python 用例目录:

tests/apitest/opstest/python/operations/rms_norm/

该目录位于 tests/apitest/opstest/python/operations/rms_norm/,属于仓库 opstest 高精度测试体系,其中包含 rms_norm 算子的 Python 侧数据构造与预期输出生成逻辑,可作为自造数据、核对数值精度的权威参考。

常见问题与排障建议

  • 链接报std::string相关符号错误:多为_GLIBCXX_USE_CXX11_ABI与加速库不一致,按 README 显式指定-D_GLIBCXX_USE_CXX11_ABI=0/1并匹配 build.sh 探测值。
  • ATB_HOME_PATH/ASCEND_HOME_PATH为空导致找不到头文件:确认已正确 source NNAL 与 CANN 的 set_env.sh;源码编译场景务必 sourceoutput/atb/set_env.sh
  • 运行报参数校验错误:检查layerTypequantType组合是否合法、量化场景是否误配precisionMode/modelType/rstd、所有 Tensor 最后一维是否一致,以及设备型号对 bf16 的支持限制。
  • 执行结果不符合预期:demo 输入为随机填充,不要将其当作真实推理结果;如需验证正确性,改用tests/apitest/opstest/python/operations/rms_norm/下的 Python 用例数据,或参考 example/op_demo/demo_util.h 中的CastOp/TransdataOp工具完成数据类型与 ND/NZ 格式的转换后再对拍。

通过本文,你已经掌握 RmsNormOperation 在 ascend-transformer-boost 中的完整 C++ 调用链路:从双环境变量配置、build.sh 编译运行,到RmsNormParam参数体系与 5 个模型场景规格,再到源码级校验规则与数据验证手段。这套方法同样适用于仓库中其他算子 demo(如 rms_norm_backward、layer_norm、rope 等),可作为 ATB 算子 C++ 开发的上手范式。

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

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询