CANN ops-math手把手入门:基于昇腾NPU的conversion类和math类算子从源码编译到调用的完整实操指南
2026/6/16 0:45:58 网站建设 项目流程

前言

CANN(Compute Architecture for Neural Networks)是华为昇腾AI处理器配套的异构计算架构,为神经网络推理和训练提供底层算子库与运行时支持。ops-math作为CANN算子库中的基础数值计算组件,覆盖conversion类(张量形态变换)、math类(基础数学运算)、random类(随机数生成)三大算子类别,是上层AI框架与昇腾NPU硬件之间的关键桥梁。在昇腾NPU上开展算子级开发时,理解ops-math的目录结构、编译流程与调用方式,是决定开发效率的核心环节。本文以真实可复现的操作步骤,逐一拆解从环境准备、源码获取、单算子编译、自定义算子包安装,到算子调用与调试的完整链路,帮助开发者在昇腾NPU环境中快速建立算子开发的工作流。

ops-math仓库结构与算子分类解析

ops-math仓库采用按算子类别划分的目录结构,根目录下核心源码目录包含conversion、math、random三个类别,每个类别下放置对应算子工程。除算子实现外,仓库还提供examples目录,内含add_example等端到端算子开发示例,适合作为入门实践的起点。

进入克隆后的源码目录,根目录下的build.sh是核心编译入口脚本,支持单算子编译与全量编译两种模式。单算子编译通过–ops参数指定目标算子名称,仅构建指定算子的内核与Host侧代码,编译耗时短,适合开发迭代阶段使用。全量编译省略–ops参数,构建整个ops-math包,适合版本发布场景。

每个算子工程目录下包含op_host、op_kernel、op_api、op_graph、examples、tests等子目录。op_host存放Host侧实现,包括算子信息库定义、InferShape、Tiling实现;op_kernel存放Device侧Kernel实现,即实际在昇腾NPU AI Core上执行的代码;op_api存放aclnn接口封装,供上层应用通过C接口调用算子;examples存放调用示例源码,是验证算子功能是否正常的最快路径。

理解这套目录结构后,才能准确定位需要修改的文件。例如要修改一个算子的内核逻辑,应当进入对应算子目录下的op_kernel子目录,找到实现文件进行修改;要修改算子Host侧的Tiling策略,则在op_host目录下找到对应的tiling源文件。

环境准备与源码获取

在昇腾NPU上进行ops-math算子开发,前提是完成驱动与CANN软件包的安装。环境准备完成后,需要获取与CANN版本配套的ops-math源码。CANN软件版本与ops-math的Git分支标签存在配套关系,使用不匹配的分支会导致编译或运行时错误。

获取源码的命令格式如下,将${tag_version}替换为与目标CANN版本配套的分支标签:

gitclone-b${tag_version}https://gitcode.com/cann/ops-math.git&&cdops-math

以CANN 9.0.0版本为例,对应的克隆命令为:

gitclone-b9.0.0 https://gitcode.com/cann/ops-math.gitcdops-math

这段代码的作用是获取指定版本的ops-math源码并进入项目根目录。git clone命令的-b参数指定分支或标签名称,确保下载的源码与当前环境安装的CANN版本匹配。配套的版本对应关系可在cann/release-management仓库中查阅。若环境中已存在配套分支源码(例如CANNLab云开发环境默认提供),可跳过克隆步骤直接使用。

算子内核依赖CANN提供的底层API(如Ascend C API),不同版本的CANN其API接口、内核编程模型、二进制接口可能存在差异。通过分支标签锁定配套版本,避免头文件不匹配、符号找不到等编译错误,也避免运行时出现未定义行为。

单算子编译:以add_example为实践对象

ops-math提供build.sh脚本统一处理编译流程。单算子编译适合开发阶段频繁验证,仅构建指定算子的Host侧与Device侧二进制,生成的自定义算子包体积较小,安装速度快。

编译命令格式如下,${soc_version}需根据昇腾NPU具体型号填写:

bashbuild.sh--pkg--soc=${soc_version}--ops=add_example-j16

不同昇腾NPU产品对应的soc_version取值:

  • Atlas A2训练系列/Atlas A2推理系列产品:ascend910b
  • Atlas A3训练系列/Atlas A3推理系列产品:ascend910_93
  • 950系列 products:ascend950

以Atlas A2环境为例,完整编译命令为:

bashbuild.sh--pkg--soc=ascend910b--ops=add_example-j16

执行后,脚本依次完成以下工作:读取算子工程下的CMakeLists.txt,调用cmake生成构建规则,调用make执行并行编译(一j16指定16线程并行),将编译产物打包为自解压安装包,存放于项目根目录的build_out目录下,文件名格式为cann-ops-math-custom_linux-${arch}.run。

这段代码的核心是直接调用build.sh并传入四个关键参数。–pkg指示脚本执行编译并打包;–soc指定目标芯片架构,编译出的内核二进制必须与实际NPU架构匹配;–ops指定仅编译add_example算子,省略此参数会触发全量编译;-j16指定并行编译线程数,加速构建过程。

昇腾NPU的不同产品系列(如910b与950)在AI Core指令集、向量计算单元规格、L2缓存大小上存在差异,内核二进制需要针对具体架构编译。单算子编译模式通过–ops参数过滤构建目标,避免全量编译带来的长时间等待,让开发者的修改-编译-验证循环保持在秒级而非分钟级。

自定义算子包安装与环境变量配置

编译成功后,build_out目录下生成的自解压安装包需要执行安装,将算子二进制与接口头文件部署到CANN的算子vendor路径下。

安装命令如下:

./build_out/cann-ops-math-*linux*.run

安装路径为ASCENDHOMEPATH/opp/vendors/custommath/,其中{ASCEND_HOME_PATH}/opp/vendors/custom_math/,其中ASCENDHOMEPATH/opp/vendors/custommath/,其中{ASCEND_HOME_PATH}为CANN软件的安装根目录。算子包安装完成后,op_api目录下的动态库需要加入运行时库搜索路径,否则应用程序运行时会出现无法加载动态库的错误。

配置环境变量的命令:

exportLD_LIBRARY_PATH=${ASCEND_HOME_PATH}/opp/vendors/custom_math/op_api/lib:${LD_LIBRARY_PATH}

这段环境变量配置将自定义算子包的op_api动态库路径加入LD_LIBRARY_PATH。Linux系统下的动态链接器在启动程序时,从LD_LIBRARY_PATH指定的目录中搜索所需的共享库。若遗漏此步骤,执行调用算子的程序时会报"error while loading shared libraries"错误。

CANN的算子加载机制通过vendor路径隔离不同来源的算子包,避免与CANN自带算子发生冲突。自定义算子安装在opp/vendors路径下,运行时需要显式将该路径加入库搜索路径。此设计与Linux的动态库加载机制保持一致,也便于多版本算子包的隔离管理。

运行算子调用示例验证功能正确性

ops-math为每个算子提供调用示例源码,位于算子目录的examples子目录下。以add_example为例,其示例源码为examples/test_aclnn_add_example.cpp,通过aclnn接口调用算子,完成输入张量的构造、算子执行与结果打印。

build.sh脚本提供–run_example参数,自动完成示例程序的编译与执行:

bashbuild.sh--run_exampleadd_example eager cust--vendor_name=custom

执行后终端输出类似以下内容,表明算子已正确部署并执行:

add_example first input[0] is: 1.000000, second input[0] is: 1.000000, result[0] is: 2.000000 add_example first input[1] is: 1.000000, second input[1] is: 1.000000, result[1] is: 2.000000

这段命令中,–run_example指定要运行的算子示例,eager表示以动态图模式调用(对应aclnn接口),cust表示自定义算子包模式,–vendor_name=custom指定vendor名称为custom,与安装路径中的custom_math对应。

提供标准调用示例降低了算子验证的门槛。开发者无需从零编写调用代码,直接运行示例即可验证编译与安装是否成功。示例代码中包含完整的设备管理、内存分配、张量构造、算子Launch、结果拷贝到主机内存的全流程,是学习CANN算子调用规范的最佳参照。

修改Kernel实现:从Add到Mul的实操

掌握编译与调用流程后,可以尝试修改算子内核逻辑。以add_example为例,将其从逐元素加法改为逐元素乘法,体验完整的修改-编译-安装-验证闭环。

内核实现文件位于examples/add_example/op_kernel/add_example.h,找到Compute函数,将AscendC::Add替换为AscendC::Mul:

__aicore__inlinevoidAddExample<T>::Compute(int64_tcurrentNum){AscendC::LocalTensor<T>xLocal=inputQueueX.DeQue<T>();AscendC::LocalTensor<T>yLocal=inputQueueY.DeQue<T>();AscendC::LocalTensor<T>zLocal=outputQueueZ.AllocTensor<T>();// 将Add替换为Mul// AscendC::Add(zLocal, xLocal, yLocal, currentNum);AscendC::Mul(zLocal,xLocal,yLocal,currentNum);outputQueueZ.EnQue<T>(zLocal);inputQueueX.FreeTensor(xLocal);inputQueueY.FreeTensor(yLocal);}

这段内核代码运行在昇腾NPU的AI Core上。AscendC::Add和AscendC::Mul均为Ascend C API提供的向量计算接口,对两个输入LocalTensor执行逐元素加法或乘法,结果写入输出LocalTensor。修改后保存文件,回到项目根目录重新执行编译、安装、验证步骤。

重新编译:

bashbuild.sh--pkg--soc=ascend910b--ops=add_example-j16

重新安装:

./build_out/cann-ops-math-*linux*.run

重新验证:

bashbuild.sh--run_exampleadd_example eager cust--vendor_name=custom

验证输出的result值变为两个输入的乘积(输入全为1时输出仍为1,可修改示例中的输入数据以观察乘法效果)。

Ascend C API提供类C++的编程接口,开发者在Kernel中直接调用Add、Mul等向量运算接口,由编译器负责映射到AI Core的向量计算指令。这种分层设计让开发者聚焦于算子计算逻辑,无需手写汇编。Queue机制(inputQueueX.DeQue、outputQueueZ.AllocTensor等)是Ascend C的流水线内存管理抽象,通过生产者-消费者队列协调AI Core的流水阶段,提高计算与数据搬运的并行度。

算子调试:打印与性能采集

算子开发过程中,功能错误与性能瓶颈是两类核心问题。ops-math支持在Kernel中插入打印语句辅助调试,也支持通过msprof工具采集性能数据。

在Kernel代码中添加PRINTF打印标量数据:

int64_tremainderLength=tilingData->totalNum-tilingData->blockFactor*AscendC::GetBlockIdx();blockLength_=(remainderLength>tilingData->blockFactor)?tilingData->blockFactor:remainderLength;ubLength_=tilingData->ubFactor;AscendC::PRINTF("Tiling blockLength is %lld\n",blockLength_);

添加DumpTensor打印Tensor内容:

AscendC::LocalTensor<float>zLocal=outputQueueZ.DeQue<float>();DumpTensor(zLocal,0,128);

PRINTF接口类似于C语言中的printf,但运行在AI Core上,支持打印整数、浮点数等标量类型。DumpTensor接口将指定Tensor指定范围内的数据打印出来,用于检查算子输出是否符合预期。

性能采集使用msprof工具,先生成可执行文件,再执行性能采集:

bashbuild.sh--run_exampleadd_example eager cust--vendor_name=customcdbuild msprof--application="./test_aclnn_add_example"

msprof是昇腾平台提供的性能分析命令行工具,采集AI Core利用率、内存带宽、指令流水线停顿等底层性能指标,输出报告存放于build目录下,可结合MindStudio Insights进行可视化分析。

AI Core作为独立的计算加速单元,其上的程序调试不能依赖主机侧的gdb等工具。PRINTF与DumpTensor是Ascend C提供的专用调试接口,通过在Kernel中插入打印点,将AI Core上的运行时信息传回主机侧输出。msprof通过在应用程序运行时注入性能采集逻辑,记录AI Core硬件计数器的状态,帮助开发者定位性能瓶颈(如DMA搬运等待、向量计算单元利用率不足等)。

修改测试输入验证多场景正确性

算子的功能正确性需要在多种输入形状与数值组合下验证。修改示例源码中的输入张量定义,可以构造不同的测试场景。

编辑examples/add_example/examples/test_aclnn_add_example.cpp,修改输入张量的shape与初始化数据:

intmain(){// 修改输入shape为{8, 8, 8, 8}std::vector<int64_t>selfXShape={8,8,8,8};std::vector<float>selfXHostData(4096);// 填充有区分度的测试数据for(inti=0;i<4096;++i){selfXHostData[i]=static_cast<float>(i%10);}// 同理修改selfY、selfZ的输入// ...}

修改完成后,由于仅变更了示例测试代码(未修改算子Kernel或Host侧实现),无需重新编译算子包,直接重新运行示例即可:

bashbuild.sh--run_exampleadd_example eager cust--vendor_name=custom

观察输出结果中result值与输入数据的对应关系是否符合算子逻辑。

算子需要对任意合法输入形状产生正确输出。通过修改测试输入,覆盖不同shape、不同数值分布(全1、递增序列、随机数等)的测试场景,提前发现InferShape错误、Tiling边界处理不当、数值稳定性等问题。示例测试代码与算子实现分离编译,修改测试代码后无需重新构建算子包,缩短验证周期。

conversion类与math类算子的调用方式对比

conversion类算子负责张量形态变换(如数据类型转换、形状重塑、维度重排等),math类算子负责逐元素或归约类数学运算(如指数、对数、三角函数、矩阵乘法等)。两类算子在调用接口上遵循相同的aclnn规范,但在Tiling策略与内核实现上有各自特点。

conversion类算子的内核通常以数据搬运与格式转换为主,AI Core上的计算量较小,性能瓶颈多在HBM(高带宽内存)带宽上。math类算子的内核以向量或矩阵计算为主,性能瓶颈可能在计算单元利用率或HBM带宽,具体取决于算子类型与数据规模。

在调用代码层面,两类算子均通过aclnn接口完成。典型调用流程为:aclnnXxxGetWorkspaceSize获取工作空间大小,根据返回值分配设备内存,aclnnXxx执行算子。开发者无需关心算子内部是conversion类还是math类,统一通过aclnn接口调用。

使用前后效率对比

在昇腾NPU上开展算子开发时,是否掌握ops-math的完整编译与调用流程,对开发效率有直接影响。以下从多个维度对比使用ops-math标准流程前后的效率差异。

维度使用前(无标准流程)使用后(遵循ops-math流程)差异来源
环境搭建时间手动查找CANN版本依赖,逐一解决头文件缺失、库版本不匹配问题,耗时数小时至数天参照仓库README与环境部署文档,使用CANNLab或Docker环境,环境准备在30分钟内完成CANNLab与Docker环境预装配套CANN包与源码,消除版本匹配问题
算子编译耗时(修改-验证循环)全量编译整个算子库,单次编译耗时10分钟以上,频繁修改时开发节奏被打断使用单算子编译模式(–ops参数),仅构建目标算子,单次编译耗时在1分钟以内单算子编译通过过滤构建目标,避免无关算子参与编译,并行编译参数进一步缩短构建时间
算子调用代码编写从CANN文档中查找aclnn接口定义,手动编写设备管理、内存分配、算子Launch代码,容易引入资源管理错误直接运行仓库提供的examples测试程序,或在示例基础上修改,调用代码即参考实现ops-math为每个算子提供标准调用示例,覆盖完整的调用生命周期,可直接复用或改编
算子内核调试效率无内核打印手段,只能通过最终输出结果反推错误位置,调试依赖反复编译验证在Kernel中插入PRINTF与DumpTensor调用,实时观察AI Core上的变量值与Tensor内容,定位问题有具体数据支撑Ascend C调试接口将AI Core上的运行时信息传回主机侧输出,使内核调试具备可观察性
性能优化起点无性能采集手段,无法获知AI Core利用率、带宽占用等底层指标,优化缺乏方向使用msprof工具采集性能数据,获取AI Core利用率、内存读写次数、指令停顿次数等指标,优化有针对性msprof提供的硬件计数器数据揭示性能瓶颈所在(计算密集vs内存带宽受限),指导优化方向

结尾

ops-math是CANN算子生态中的基础数值计算库,掌握其源码编译与调用流程是昇腾NPU算子开发的基本功。本文以add_example为实践对象,覆盖了环境准备、源码获取、单算子编译、自定义算子包安装、环境变量配置、示例运行、内核修改、调试打印、性能采集、测试输入修改的完整操作链路。conversion类与math类算子虽在内核实现上各有特点,但调用接口统一于aclnn规范,开发者掌握一套调用流程即可覆盖多类算子的使用场景。仓库提供的examples目录是学习算子开发的最佳起点,建议在实际上机操作中对照本文步骤逐一验证,建立对ops-math项目结构的肌肉记忆。

https://atomgit.com/cann/ops-math

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

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

立即咨询