aclnn算子开发入门
Ascend aclnn 算子开发入门
[toc]
一、概述
什么是算子?
在 AI 框架中,算子一般指一些最基本的代数运算(如:矩阵加法、矩阵乘法等),多个算子之间也可以根据需要组合成更加复杂的融合算子(如:flash-attention 算子等)。算子的输入和输出都是 Tensor(张量)。
融合算子:将多个独立的“小算子”融合成一个“大算子”,多个小算子的功能和大算子的功能等价,但融合算子在性能或者内存等方面优于独立的小算子。
另外,算子更多地是 AI 框架中的一个概念,在硬件底层算子具体的执行部分,一般叫做 Kernel(核函数)。
下面将首先对算子开发中涉及的一些基本概念进行介绍(可以用 CUDA 作为参考,大部分概念都是相似的),然后会以具体的矩阵加法和乘法算子的代码实现为例进行讲解。
二、基本概念
2.1 Device
- Host:一般指 CPU(负责调度);
- Device:一般指 GPU、NPU(负责计算)。
2.2 Context
Context 主要负责管理线程中各项资源的生命周期。
一般来说,Context 与其它概念之间具有以下关系:
- 一个进程可以创建多个 Context;
- 一个线程只能同时使用一个 Context,该 Context 对应一个唯一的 Device,线程可以通过切换 Context 来切换要使用的 Device;
- 一个 Device 可以拥有多个 Context,但同时只能使用一个 Context。
每一个线程都具有一个默认的 Context,无需手动创建,也无法被删除。我们也可以手动创建更多的 Context,使用后需要及时释放。另外,在线程中,默认使用最后一次创建的 Context。
2.3 Stream
Stream 主要负责维护一些异步操作的执行顺序,这些操作包括:
- Host 到 Device 的数据传输;
- 调用 Kernel;
- 其它由 Host 发起并由 Device 执行的动作。
说明:在 GPU/NPU 上调用的函数,被称为核函数(Kernel function)。核函数使用
__global__关键字进行定义,会被 GPU/NPU 上的多个线程执行。
同一个 Stream 里的操作是严格串行的(顺序执行),而不同 Stream 之间则可以并行执行。来自不同 Stream 的 Kernel 可以共享 GPU/NPU 的内核并发执行。
一般来说,Context 与其它概念之间具有以下关系:
- 一个线程或 Context 中可以创建多个 Stream;
- 不同线程或 Context 间的 Stream 在 Device 上相互隔离。
每一个 Context 都具有一个默认的 Stream,无需手动创建,也无法被删除。我们也可以手动创建更多的 Stream,并将多个操作分配到不同的 Stream 上,这样就可以实现多个操作的并行,Stream 使用后需要及时释放。
2.4 Task
Task 或 Kernel,是 Device 上真正的任务执行体。
一般来说,Task 与其它概念之间具有以下关系:
- 一个 Stream 中可以下发多个 Task;
- 多个 Task 之间可以插入 Event,用于同步不同 Stream 之间的 Task。

参考资料:
三、单算子开发
官方介绍:
AscendCL(Ascend Computing Language)是一套用于在昇腾平台上开发深度神经网络应用的 C 语言 API 库,提供运行资源管理、内存管理、模型加载与执行、算子加载与执行、媒体数据处理等 API,能够实现利用昇腾硬件计算资源、在昇腾 CANN 平台上进行深度学习推理计算、图形图像预处理、单算子加速计算等能力。简单来说,就是统一的 API 框架,实现对所有资源的调用。
面向算子开发场景的编程语言 Ascend C,原生支持 C/C++ 标准规范,最大化匹配用户开发习惯;通过多层接口抽象、自动并行计算、孪生调试等关键技术,极大提高算子开发效率,助力 AI 开发者低成本完成算子开发和模型调优部署。
3.1 单算子调用方式
- 单算子 API 执行:
- 直接调用 CANN 已经提供的算子 API;
- 使用 Ascend C 开发并调用自定义算子。
- 单算子模型执行。
3.2 单算子 API 执行
- NN 算子;
- DVPP 算子;
- 融合算子;
- ……
更详细的算子 API 文档可以参考:算子加速库接口。
两段式接口:单算子 API 执行时,针对每个算子,都需要依次先调用 aclxxXxxGetWorkspaceSize() 接口获取算子执行需要的 workspace 内存大小、再调用 aclxxXxx() 接口执行算子。
参考资料:
四、代码实现
本小节将以 aclnnAdd 和 aclnnMatmul 算子为例,实现具体的代码。
更详细的 API 文档可以参考:
- 加法算子:aclnnAdd;
- 乘法算子:aclnnMatmul。
4.1 环境搭建
4.2 单算子开发流程


4.3 常见参数说明
strides:描述 Tensor 维度上相邻两个元素的间隔,详见非连续的 Tensor;workspace:在 device 侧申请的 workspace 内存地址;workspaceSize:在 device 侧申请的 workspace 大小;executor:算子执行器,实现了算子的计算流程;aclnnStatus:详见aclnn 返回码。
注意:
- 多个输入数据之间,数据类型需要满足互推导关系:当一个 API(如
aclnnAdd()、aclnnMul()等)输入的 Tensor 数据类型不一致时,API 内部会推导出一个数据类型,将输入数据转换成该数据类型进行计算;- 多个输入数据之间,shape 需要满足广播关系:在某些情况下,较小的数组可以“广播至”较大的数组,使两者shape互相兼容;
- 更多算子 API 信息详见:CANN 社区版开发文档,位置:【CANN 社区版 -> 8.0.RC3.alpha003 -> API 参考 -> 算子加速库接口 -> NN 算子接口】。
4.4 矩阵加法算子
目录结构:
1 | sss@xxx:~/xxx/add$ tree |
CMakeLists:
1 | # Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. |
编译构建:
1 | mkdir build |
test_add 代码:
1 |
|
运行程序:
1 | ./opapi_add_test |
运行结果:
1 | sss@xxx:~/xxx/add/build/bin$ ./opapi_test |
4.5 矩阵乘法算子
目录结构:
1 | sss@xxx:~/xxx/mul$ tree |
CMakeLists:
1 | # Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. |
编译构建:
1 | mkdir build |
test_mul 代码:
1 |
|
运行程序:
1 | ./opapi_mul_test |
运行结果:
1 | sss@xxx:~/xxx/mul/build/bin$ ./opapi_test |
参考资料: