news 2026/6/7 0:24:30

ops-math 仓库全景导读——昇腾 NPU 数学算子库的定位与能力边界

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ops-math 仓库全景导读——昇腾 NPU 数学算子库的定位与能力边界

前言

昇腾 CANN 已经提供了这么丰富的算子生态,为什么还需要一个专门做数学计算的算子库?答案比我想象的有意思得多。数学算子看起来简单——加法就是加法,三角函数就是三角函数,但真正在昇腾 NPU 上把它们跑出硬件理论峰值的百分之七八十,需要做的优化远比你以为的多。数据布局怎么贴合 Cube 单元的向量宽度,Tiling 怎么切才能让多核负载均衡,不同数据类型之间的转换怎么省掉不必要的中间拷贝——这些问题在 ops-math 里都有对应的解法。

ops-math 是昇腾 CANN 算子库中专门负责数值计算的子仓库,涵盖 conversion(张量形态变换)、math(基础数学运算)、random(随机数生成)三大类别。它并不追求大而全,而是在自己划定的范围内把每个算子的硬件亲和性做到极致。在昇腾 NPU 的达芬奇架构上,同样的矩阵加法如果用纯 Python 循环来实现,延迟可能是使用 ops-math 之后的数倍。这不是算法本身的差距,而是算子有没有针对昇腾的 Cube 单元做数据布局和并行度优化的问题。

本文从 ops-math 的仓库定位、目录结构、核心算子分类、实际调用方式、多架构适配机制、以及与其他算子仓库的关系六个角度展开,帮你在最短时间里搞清楚这个仓库到底能做什么、怎么做、以及什么时候该用它。

一、ops-math 在 CANN 生态中的位置

昇腾 CANN 的算子库体系大致可以分为四层。最上层是面向用户的 AOL 算子库,包含 NN/BLAS/DVPP/HCCL 等面向业务场景的算子集合,开发者日常通过 PyTorch 或 MindSpore 调用的算子最终都会落到这一层。第二层是算子开发框架和工具链,包括 asc-devkit(算子开发套件)、pyasc(Python 风格算子编程语言)和 asc-tools(调试调优工具)等。第三层就是各个具体的算子实现仓库,ops-math 就属于这一层。它和 ops-nn、ops-cv、ops-transformer 等仓库共享一套底层构建基础设施(opbase),但在算子功能上互不重叠。第四层是 opbase,作为所有算子仓库的公共基础,提供统一的 Tiling 框架、构建系统脚本和测试基础设施。

从依赖关系来看,ops-math 是 opbase 的直接消费者之一,它的所有算子都建立在 opbase 提供的通用算子开发框架之上。这意味着如果你理解了 ops-math 的算子开发模式,再去看 ops-nn 或者 ops-cv 的源码,上手速度会快很多。它们用的 tiling 机制、kernel 编写规范、INI 配置文件格式几乎完全一致。反过来说,如果你在 ops-math 里发现了某个 Tiling 策略的优化技巧,大概率也能平移到其他算子仓库。

ops-math 于 2025 年 9 月首次开源上线,此后持续迭代。2026 年 1 月新增了 QuickStart 文档,让新手可以零基础入门算子项目部署、算子开发和贡献流程。2025 年 12 月的版本开始支持 Ascend 950PR 和 Ascend 950DT 芯片,并新增了 concat、lerp、drop_out_v3 等算子。从版本节奏来看,这个仓库目前处于快速成长期,几乎每周都有新的 PR 合并进来,涵盖新算子实现、现有算子适配新芯片、代码重构和文档优化等类型。

从代码质量角度来看,ops-math 团队对代码规范的要求在持续提升。2025 年底的系列重构 PR 完成了所有算子到 opbase 公共头文件的迁移,消除了代码重复。最近合并的 PR 则引入了规范化的错误码上报机制(EZ0008-EZ0034 系列),替代了之前散落在各处的 OP_LOGE 宏调用。这种对工程质量的持续投入,让 ops-math 在 CANN 的算子仓库中算是一个代码风格比较统一、可读性比较好的项目。

二、目录结构与开发范式

clone 下来之后,ops-math 的源码结构很清晰。根目录下分 conversion、math、random 三大算子目录,外加 common(公共头文件与工具函数,目前正在逐步精简)、cmake(构建系统,含 func.cmake、opbuild.cmake、ut.cmake 等)、docs(文档,包括 QuickStart 和进阶教程)、scripts(辅助脚本,如 opdesc_parser.py、insert_kernel_src.py 等)、test(测试用例)等辅助目录。

每个算子目录下面又按算子名划分子目录,比如 math/add、math/matmul、math/lerp、random/drop_out_v3、conversion/concat 等。每个算子目录内的文件布局遵循一套约定:op_kernel 目录放算子实现源码,op_kernel 下的 CMakeLists.txt 负责注册编译单元,而算子描述信息则通过 INI 配置文件声明。这套约定不是随便定的,而是 opbase 仓统一规范的结果,所有算子仓库都必须遵循。

以 add 算子为例。它的 Ascend C 实现文件通常在 op_kernel 目录下,文件名遵循算子名加后缀的模式。CMakeLists.txt 中用add_kernel_sources()函数把源码文件注册到构建系统。这里有一个值得注意的细节:最新的代码已经支持KERNEL_SRC机制,允许算子通过 CMake 参数指定自定义的 kernel 入口文件名,而不是强制使用算子名作为入口。这意味着同一个算子可以有多套不同的 kernel 实现,通过切换 KERNEL_SRC 来选用。

// math/add/op_kernel/CMakeLists.txt 关键片段add_kernel_sources(${KERNEL_NAME}KERNEL_SRC arch35/add.cpp add_apt.cpp)// WHY: KERNEL_SRC 指定了 arch35/add.cpp 作为自定义入口,// 当需要在不同架构(如 arch35 对应 Ascend 910,arch55 对应 Ascend 950)上// 使用不同实现时,可以通过 KERNEL_SRC 灵活切换,而不需要修改算子逻辑。// WHY: add_apt.cpp 仍然保留在编译列表中,作为通用的算子适配层,// 处理跨架构的公共逻辑,避免每个架构重复编写相同代码。

公共头文件的组织也经历过一次重要的重构。早期的 ops-math 有自己的 tiling_base.h、tiling_util.h、tiling_templates_registry.h 等公共头文件,但随着 opbase 仓的功能越来越完善,这些公共头文件的功能逐渐被 opbase 替代。2025 年底的系列 PR(#2951 处理 conversion 算子、#2969 处理 math 上半部分、#2980 处理 math 下半部分、#3000 完成收尾)完成了所有算子到 opbase 公共头文件的迁移,并删除了已经变成空壳转发的旧文件。这一步的重要性在于:以后 ops-math 只需要维护算子本身的业务逻辑,公共基础设施的升级会在 opbase 仓统一完成。开发者在升级 CANN 版本时也不需要再担心算子仓库的公共代码和 opbase 不一致的问题。

这套开发范式的另一个好处是新人上手成本低。opbase 提供了一套完整的 QuickStart 文档,从源码编译到算子调用到算子开发都有详细的步骤说明。ops-math 的 add 算子是 QuickStart 文档中使用的示例算子,它的实现展示了 Ascend C 算子开发的完整流程:定义算子描述文件、编写 tiling 函数、实现 kernel 函数、配置 CMake 构建规则、运行单元测试。掌握了这个流程之后,开发一个新的数学算子基本上就是照猫画虎的事情。

三、核心算子分类与能力边界

conversion 类算子

conversion 类算子处理张量形态变换,是最基础的算子类别之一。常见的成员包括 cast(类型转换,比如从 FLOAT16 转到 FLOAT32)、concat(张量拼接)、transpose(转置)等。这些算子在深度学习模型中无处不在——模型加载时需要做数据类型转换,多分支特征融合时需要做张量拼接,注意力机制中的序列重排需要做转置。ops-math 的 conversion 算子针对昇腾 NPU 的内存布局做了优化,避免了数据在 Host 和 Device 之间不必要的搬运。

cast 算子可能是你用得最多的 conversion 算子。在混合精度训练场景下,模型的权重通常以 FLOAT16 存储以节省显存,而梯度更新时需要转成 FLOAT32 来保证数值精度。cast 算子在昇腾 NPU 上可以做到全流水线执行——数据从 HBM 读出、类型转换、写回 HBM,整个过程不需要 CPU 介入。如果用 NumPy 的 astype 方法在 CPU 上做同样的事情,还需要先把数据从 NPU 搬到 CPU 内存、转换、再搬回去,延迟会高出一个数量级。

# 使用 ops-math 的 concat 算子进行多分支特征融合importaclfromaclimportops_math# 假设有三个分支的特征图,shape 分别为 [N, C1, H, W]、[N, C2, H, W]、[N, C3, H, W]# 在实际模型中,C1、C2、C3 可能分别是 64、128、256branch1=acl.Tensor([N,C1,H,W],dtype=acl.dtype.FLOAT32)branch2=acl.Tensor([N,C2,H,W],dtype=acl.dtype.FLOAT32)branch3=acl.Tensor([N,C3,H,W],dtype=acl.dtype.FLOAT32)# 在通道维度上拼接,输出 shape 为 [N, C1+C2+C3, H, W]merged=ops_math.concat([branch1,branch2,branch3],axis=1)# WHY: 在 NPU 上使用 ops_math.concat 而不是先拷贝回 CPU 再用 NumPy 拼接,# 省掉了 Host↔Device 之间的数据搬运,延迟通常可以降低一个数量级。# WHY: concat 算子内部已经针对昇腾内存对齐做了优化,# 拼接后的张量可以直接喂给后续算子,不需要额外的 layout 转换步骤。
math 类算子

math 类算子是 ops-math 的核心,也是数量最多的类别。它涵盖了加减乘除(add、sub、mul、div)、三角函数(sin、cos、tan、tanh、acos、asin、atan、atanh、acosh)、指数对数(exp、log、log1p)、比较运算(max、min、maximum、minimum)、以及更高级的运算(matmul、add_lora、accumulate_nv2、stft)等。

add 算子虽然简单,但它是理解 Ascend C 算子开发全流程最好的入口。ops-math 的 add 算子实现包含完整的 tiling 逻辑(将大张量拆分成硬件友好的小块)、kernel 实现(调用 Cube 单元的向量加法指令)、以及单元测试。而且 add 算子最近完成了对 Ascend 950 的 Ascend C 实现,包含了异构调用示例——也就是在同一个算子内调用其他算子的能力。这种能力在实现复杂的复合运算时很有用。

2025 年 12 月的更新中新增了 lerp 算子(线性插值)和 atanh 算子的 Ascend 950 实现。lerp 在深度学习中的应用场景很广——数据增强时的 MixUp 融合、某些注意力机制中的插值计算、以及模型蒸馏中的软标签混合都会用到。lerp 的公式很简单:output = start + weight * (end - start),但在 NPU 上直接调用融合的 lerp 算子,比分别调用减法、乘法和加法算子要快,因为融合算子省掉了中间结果的显存读写。atanh(反双曲正切)则在某些归一化操作和特殊激活函数中有用武之地。

# 使用 ops-math 的 lerp 算子做 MixUp 数据增强importaclfromaclimportops_math# 两张图像的张量表示img_a=acl.Tensor([H,W,3],dtype=acl.dtype.UINT8).to(acl.dtype.FLOAT32)img_b=acl.Tensor([H,W,3],dtype=acl.dtype.FLOAT32)# MixUp 线性混合,lambda=0.6lam=0.6mixed=ops_math.lerp(img_a,img_b,lam)# WHY: lerp 算子直接在 NPU 上完成逐元素线性插值,# 相比分三次调用(sub→mul→add),融合算子只需要一次,# 因为中间结果不需要写回显存再读出来,省掉了两次显存读写。# WHY: 如果混合比例是动态的(比如每张图不同),lerp 支持按张量传入 weight,# NPU 内部会自动做 broadcast,不需要你在外面手动扩展维度。
random 类算子

random 类算子提供随机数生成能力,包括均匀分布、正态分布以及专门的 drop_out_v3 算子。深度学习训练中的 Dropout 正则化、数据增强中的随机裁剪和噪声注入都属于这个类别的应用场景。

drop_out_v3 是 random 类中比较有意思的一个算子。和标准的 Dropout 相比,drop_out_v3 在性能上有明显的优化:它把掩码生成和元素置零合并为一个融合操作,在 NPU 上执行时只需要一次 kernel 调用。标准实现需要先调用随机数算子生成掩码、再做逐元素乘法,两次 kernel 调用之间还需要保存中间掩码张量。drop_out_v3 把这两步融合到一起,省掉了中间张量的显存分配和读写。对于大模型训练来说,这种融合优化在每一层都省一点,累积起来的收益相当可观。

ops-math 的 random 算子目前支持 Ascend 950 芯片的 Ascend C 实现。2025 年 12 月新增了 population_count(popcount)算子的适配,用于统计二进制数中 1 的个数。虽然 popcount 看起来跟随机数生成关系不大,但在某些哈希算法和压缩算法中会用到,因此被归类在 random 目录下。

四、快速上手:从调用到编译

ops-math 提供了两条主要的算子使用路径。第一条是通过 aclnn(AscendCL Neural Network)接口直接调用,这是最简单的方式,适合只想用算子不想深入实现的开发者。aclnn 是一套高层的算子调用接口,屏蔽了底层的 device 管理、内存分配、流同步等细节,你只需要构造输入张量和输出张量,然后调用对应的 aclnn 函数即可。这种方式的好处是代码量少、上手快,坏处是灵活性有限——你只能使用算子已经暴露出来的参数和配置,无法自定义算子行为。

第二条路径是自己编译和部署算子源码。如果你需要修改算子行为、添加新的算子变体,或者想了解算子到底在硬件上怎么跑的,就需要走这条路。流程大致是:git clone 源码、安装 CANN 开发包、配置 CMake 构建参数、运行 opbuild 脚本编译算子、生成可部署的包。CANN 还提供了 Simulator 工具,让你在没有物理 NPU 的 x86 机器上也能编译和调试算子,极大降低了开发者的入门门槛。

# 使用 CANN Simulator 在 x86 主机上编译并运行 add 算子# 前提:已安装 CANN 开发包,环境变量 CANN_ROOT 已设置# 1. clone 与 CANN 版本配套的分支源码gitclone-b${tag_version}https://atomgit.com/cann/ops-math.git# 2. 进入算子目录cdops-math/math/add# 3. 配置构建mkdirbuild&&cdbuild cmake..\-DCMAKE_BUILD_TYPE=Release\-DCANN_ROOT=${CANN_ROOT}\-DSOC_VERSION=ascend910# 4. 编译make-j8# 5. 运行单元测试./add_ut# WHY: CANN Simulator 让你在没有物理 NPU 的 x86 机器上也能编译和调试算子,# 极大降低了开发者的入门门槛。硬件验证可以等后续再放到真实设备上跑。# WHY: SOC_VERSION 参数必须和你的硬件匹配,# 选错会导致编译通过但运行时出现指令集不兼容的错误。# 比如你在 Ascend 910 上用了 ascend950 的配置,Cube 单元的指令集不同就会报错。

注意一点:clone 源码时一定要选择和你的 CANN 版本配套的分支标签,不要直接用 Main 分支。README 中明确说明了这一点。Main 分支包含的是最新的开发代码,可能还没有经过完整的版本兼容性测试,使用它有版本不匹配的风险。正确的做法是到 release-management 仓库查看你安装的 CANN 版本对应的 Gitcode 标签名,然后用git clone -b ${tag_version}拉取。

五、效率对比与性能考量

性能对比是一个绕不开的话题。在同一个硬件上,ops-math 的算子性能和使用 CPU 上对应的 NumPy 操作相比有显著差异。以矩阵加法为例,两个 4096x4096 的浮点矩阵相加,NumPy 在 CPU 上跑通常需要数百毫秒级别,而 ops-math 的 add 算子在同一任务上只需要几十毫秒。这个差距的来源不是算法——矩阵加法本身的复杂度是 O(n²),两边一样——而是数据搬运和并行执行的效率。CPU 上的 NumPy 操作受限于内存带宽和线程并行度的瓶颈,而昇腾 NPU 的 Cube 单元可以以硬件流水线的方式同时完成数据读取、计算和写回,三个阶段完全重叠。

对比维度CPU NumPy (4096x4096 矩阵加法)ops-math add 算子 (昇腾 NPU)差异说明
执行延迟数百毫秒级数十毫秒级通常有 5-10 倍的提升幅度
数据搬运CPU 内存读写,受限于系统内存带宽NPU 片内 HBM 直连,数据不离开 Device零 Host↔Device 搬运开销
并行度受限于 CPU 核心数(通常 8-64 核)Cube 单元数千个计算单元并行执行硬件级大规模并行
显存管理手动管理 CPU 内存,容易忘记释放由 Runtime 自动管理 NPU 显存池开发者可以更关注算法本身
批处理能力逐个矩阵操作,缺乏批量调度多流并行,支持多个矩阵加法并发执行吞吐量进一步提升

再来看 lerp 算子的效率对比。如果在 NPU 上用三步操作来实现线性插值——先做减法得到 (end - start),再做乘法得到 weight * diff,最后做加法得到 start + result——每一步操作都需要把中间结果写回显存再读出来给下一步用。而 lerp 融合算子把这三步合并成一个 kernel,数据只在寄存器里流转,不需要写回显存。对于大张量来说,省掉两次显存读写带来的性能提升非常可观。

实现方式Kernel 调用次数中间张量显存读写适合场景
三步分开调用 (sub + mul + add)3 次2 次中间结果写回+读出调试阶段,需要观察中间结果
lerp 融合算子1 次0 次中间结果生产环境,追求性能
手写 Ascend C 融合1 次0 次中间结果有特殊需求,标准融合不满足时

drop_out_v3 的融合优化逻辑也是类似的。标准的 Dropout 实现需要两步:先用随机数算子生成掩码张量,再做逐元素乘法。两步之间掩码张量需要占显存、需要写入和读出。drop_out_v3 把掩码生成和乘法融合在一起,掩码只存在于寄存器中,算完就丢弃,不占用额外的显存。在大模型训练场景下,每一层都省一个掩码张量的显存,几十层下来就能省出可观的空间,可以用来增大 batch size 或者模型维度。

六、KERNEL_SRC 机制与多架构适配

2025 年底的 KERNEL_SRC 机制是 ops-math 的一个重要进化。在此之前,每个算子的 kernel 入口文件名必须和算子名一致,这在大多数情况下没问题,但当同一个算子需要在不同架构上使用不同的实现时,就变得不够灵活了。

举个例子:add 算子可能在 Ascend 910 上使用一套 Tiling 策略,在 Ascend 950 上因为 Cube 单元的计算能力不同需要使用另一套 Tiling。在 KERNEL_SRC 机制出现之前,常见的做法是写一个通用的实现,然后在运行时根据 SOC_VERSION 做分支判断——这样做的问题是两个架构的实现代码混在一个文件里,可读性和可维护性都很差。KERNEL_SRC 机制允许你把不同架构的实现放在不同的文件中(比如 arch35/add.cpp 和 arch55/add.cpp),然后在 CMakeLists.txt 中通过 KERNEL_SRC 参数指定使用哪个文件。

这套机制还伴随着一个辅助脚本 insert_kernel_src.py 的引入。这个脚本负责在算子编译流程中读取 INI 配置文件,在对应的算子 section 中插入或更新 kernelSrc.value 字段。这样算子描述信息和 kernel 源文件的映射关系就在构建时自动完成了,开发者不需要手动维护 INI 文件中的这个字段。

# math/add/op_kernel/CMakeLists.txt 中的 KERNEL_SRC 用法 set(KERNEL_NAME add) add_kernel_sources(${KERNEL_NAME} KERNEL_SRC arch35/add.cpp add_apt.cpp ) # WHY: KERNEL_SRC 让同一算子的多架构实现在物理上分离, # 每次只编译目标架构需要的文件,编译产物更小,排查问题时也更容易定位。 # WHY: add_apt.cpp 作为适配层负责跨架构的公共逻辑, # 包括算子描述注册、输入输出的格式转换、以及与 Runtime 的交互封装。 # 如果没有这层适配,arch35 和 arch55 的实现就需要各自重复这些代码。

这套机制不是 ops-math 独创的。2025 年 10 月 ops-nn 仓库也同步了相同的 CMake 改造(PR#3936),说明它正在成为 CANN 算子仓库的通用实践。对于想同时为多款昇腾芯片开发算子的开发者来说,理解 KERNEL_SRC 机制是一个基本功。它的设计思路也很清晰:把架构差异封装在文件级别,而不是代码级别——这比在代码里写 if-else 分支要干净得多。

七、如何选择合适的算子仓库

ops-math、ops-nn、ops-cv、ops-transformer 这几个仓库有时候会让新手困惑:我到底该从哪个仓库找我要的算子?这里有一个简单的判断逻辑。如果你的需求是基础数学运算(加减乘除、三角函数、指数对数、张量拼接、类型转换),ops-math 就是你的首选。如果你的需求是神经网络中的标准算子(矩阵乘法、激活函数、归一化、池化),看 ops-nn。如果你的需求是图像处理相关的算子(resize、crop、颜色空间转换),看 ops-cv。如果你的需求是 Transformer 模型特有的算子(FlashAttention、MoE 路由、rotary embedding),看 ops-transformer。


ops-math 的仓库地址是 https://atomgit.com/cann/ops-math

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

联想拯救者BIOS高级设置终极解锁指南:免费简单教程

联想拯救者BIOS高级设置终极解锁指南:免费简单教程 【免费下载链接】LEGION_Y7000Series_Insyde_Advanced_Settings_Tools 支持一键修改 Insyde BIOS 隐藏选项的小工具,例如关闭CFG LOCK、修改DVMT等等 项目地址: https://gitcode.com/gh_mirrors/le/L…

作者头像 李华
网站建设 2026/6/7 0:14:21

Windows系统卡顿终极解决方案:Mem Reduct内存优化完全指南

Windows系统卡顿终极解决方案:Mem Reduct内存优化完全指南 【免费下载链接】memreduct Lightweight real-time memory management application to monitor and clean system memory on your computer. 项目地址: https://gitcode.com/gh_mirrors/me/memreduct …

作者头像 李华