本文分享自华为云社区《一文教你如何调用Ascend C算子》,作者: 昇腾CANN。

Ascend C是CANN针对算子开发场景推出的编程语言,原生支持C和C++标准规范,兼具开发效率和运行性能。基于Ascend C编写的算子程序,通过编译器编译和运行时调度,运行在昇腾AI处理器上。使用Ascend C,开发者可以基于昇腾AI硬件高效实现自定义的创新算法。

本文重点介绍基于Ascend C算子编程语言完成自定义算子的开发和部署后,如何调用自定义算子验证算子功能。

三种常见的算子调用方式

目前,Ascend C算子有三种常见的调用方式:

  • Kernel直调:完成算子核函数开发和Tiling实现后,可基于内核调用符方式进行完成算子的调用,用来快速验证算法逻辑。
  • 单算子调用:相比于Kernel直调,单算子调用是一种较为标准的调用方式。开发者在完成所有算子交付件开发、编译部署之后,一般通过单算子调用方式验证单算子功能以满足交付条件,包括两种调用方式:
  • 单算子API执行:基于C语言的API执行算子,直接调用单算子API接口,无需提供单算子描述文件进行离线模型的转换。
  • 单算子模型执行:基于图IR执行算子,先编译算子(例如,使用ATC工具将Ascend IR定义的单算子描述文件编译成算子om模型文件),再调用AscendCL接口加载算子模型,最后调用AscendCL接口执行算子。
  • 在PyTorch、ONNX、TensorFlow等三方框架中调用算子:需要完成框架适配开发,即可从第三方框架实现算子调用。

当然,除了可以调用自定义算子进行功能验证外,开发者也可以通过单算子调用方式直接调用昇腾算子库中预制的算子,使用昇腾算力。

通过单算子API执行方式调用算子

通过单算子API执行方式调用算子,是算子交付阶段最重要的一种调用方式,也是Ascend C算子开发人员必须掌握的算子调用手段,下面做重点讲解。开发者若想了解其他方式,可以移至文末查阅“Ascend C一站式学习资源”[1]。

Ascend C算子开发并编译部署完成后,会在算子包安装目录下的op_api目录下会自动生成单算子API,以默认安装场景为例,单算子调用的头文件.h和动态库libcust_opapi.so所在的目录结构为:

├── opp    //算子库目录
│ ├── vendors //自定义算子所在目录
│ ├── config.ini
│ └── vendor_name1 // 存储对应厂商部署的自定义算子,此名字为编译自定义算子安装包时配置的vendor_name,若未配置,默认值为customize
│ ├── op_api
│ │ ├── include
│ │ │ └── aclnn_xx.h
│ │ └── lib
│ │ └── libcust_opapi.so
...

aclnn_xx.h中的算子API形式一般定义为“两段式接口”,形如:

aclnnStatus aclnnXxxGetWorkspaceSize(const aclTensor *src, ..., aclTensor *out, uint64_t workspaceSize, aclOpExecutor **executor);
aclnnStatus aclnnXxx(void* workspace, int64 workspaceSize, aclOpExecutor* executor, aclrtStream stream);

单算子API可以直接在应用程序中调用,大致过程为:

  1. 使用第一段接口aclnnXxxGetWorkspaceSize计算本次API调用计算过程中需要多少的workspace内存
  2. 获取到本次API计算需要的workspace大小后,按照workspaceSize大小申请Device侧内存
  3. 调用第二段接口aclnnXxx,调用对应的单算子二进制执行计算

完整调用流程如下:

下面提供单算子调用的关键代码示例,供开发者参考:

// 1.AscendCL初始化
aclRet = aclInit("../scripts/acl.json"); // 2.运行管理资源申请
int deviceId = 0;
aclRet = aclrtSetDevice(deviceid);
// 获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)
aclrtRunMode runMode;
bool g_isDevice = false;
aclError aclRet = aclrtGetRunMode(&runMode);
g_isDevice = (runMode == ACL_DEVICE); // 3.申请内存存放算子的输入输出
// ...... // 4.传输数据
if (aclrtMemcpy(devInputs_[i], size, hostInputs_[i], size, kind) != ACL_SUCCESS) {
return false;
} // 5.计算workspace大小并申请内存
size_t workspaceSize = 0;
aclOpExecutor *handle = nullptr;
auto ret = aclnnAddCustomGetWorkspaceSize(inputTensor_[0], inputTensor_[1], outputTensor_[0],
&workspaceSize, &handle);
// ...
void *workspace = nullptr;
if (workspaceSize != 0) {
if (aclrtMalloc(&workspace, workspaceSize, ACL_MEM_MALLOC_NORMAL_ONLY) != ACL_SUCCESS) {
ERROR_LOG("Malloc device memory failed");
}
} // 6.执行算子
if (aclnnAddCustom(workspace, workspaceSize, handle, stream) != ACL_SUCCESS) {
(void)aclrtDestroyStream(stream);
ERROR_LOG("Execute Operator failed. error code is %d", static_cast<int32_t>(ret));
return false;
} // 7.同步等待
aclrtSynchronizeStream(stream); // 8.处理执行算子后的输出数据,例如在屏幕上显示、写入文件等,由用户根据实际情况自行实现
// ...... // 9.释放运行管理资源
aclRet = aclrtResetDevice(deviceid);
// .... // 10.AscendCL去初始化
aclRet = aclFinalize();

运行一个完整的算子调用程序

昇腾的gitee仓中提供了完整的样例工程LINK,工程目录结构如下:

├──input                                                 // 存放脚本生成的输入数据目录
├──output // 存放算子运行输出数据和真值数据的目录
├── inc // 头文件目录
│ ├── common.h // 声明公共方法类,用于读取二进制文件
│ ├── operator_desc.h // 算子描述声明文件,包含算子输入/输出,算子类型以及输入描述与输出描述
│ ├── op_runner.h // 算子运行相关信息声明文件,包含算子输入/输出个数,输入/输出大小等
├── src
│ ├── CMakeLists.txt // 编译规则文件
│ ├── common.cpp // 公共函数,读取二进制文件函数的实现文件
│ ├── main.cpp // 单算子调用应用的入口
│ ├── operator_desc.cpp // 构造算子的输入与输出描述
│ ├── op_runner.cpp // 单算子调用主体流程实现文件
├── scripts
│ ├── verify_result.py // 真值对比文件
│ ├── gen_data.py // 输入数据和真值数据生成脚本文件
│ ├── acl.json // acl配置文件

步骤1 增加头文件引用。

安装部署完成后,会在算子包安装目录下的op_api目录生成单算子调用的头文件aclnn_xx.h和动态库libcust_opapi.so,编写单算子的调用代码时,要包含自动生成的单算子API执行接口头文件:

#include "aclnn_add_custom.h"

步骤2 修改CMakeLists文件。

编译算子调用程序时,需要在头文件的搜索路径include_directories中增加算子包安装目录下的op_api/include目录,便于找到该头文件;同时需要链接cust_opapi动态库。

  • 设置CUST_PKG_PATH变量为算子包安装目录下的op_api目录,以下样例仅为参考,请根据算子包部署的实际目录位置进行设置。
if (NOT DEFINED ENV{DDK_PATH})
set(INC_PATH "/usr/local/Ascend/ascend-toolkit/latest")
message(STATUS "set default INC_PATH: ${INC_PATH}")
else ()
message(STATUS "env INC_PATH: ${INC_PATH}")
endif()
set(CUST_PKG_PATH "${INC_PATH}/opp/vendors/customize/op_api")
  • 在头文件的搜索路径include_directories中增加算子包安装目录下的op_api/include目录。
include_directories(
${INC_PATH}/runtime/include
${INC_PATH}/atc/include
../inc
${CUST_PKG_PATH}/include
)
  • 链接cust_opapi链接库。
target_link_libraries(execute_add_op
ascendcl
cust_opapi
acl_op_compiler
nnopbase
stdc++
)

步骤3 生成测试数据。

在样例工程目录下,执行如下命令:

python3 scripts/gen_data.py

会在工程目录下input目录中生成两个shape为(8,2048),数据类型为float16的数据文件input_0.bin与input_1.bin,用于进行AddCustom算子的验证。代码样例如下:

import numpy as np
a = np.random.randint(100, size=(8, 2048,)).astype(np.float16)
b = np.random.randint(100, size=(8, 2048,)).astype(np.float16)
a.tofile('input_0.bin')
b.tofile('input_1.bin')

步骤4 程序编译与运行。

1. 开发环境上,设置环境变量,配置AscendCL单算子验证程序编译依赖的头文件与库文件路径,如下为设置环境变量的示例。${INSTALL_DIR}表示CANN软件安装目录,例如,$HOME/Ascend/ascend-toolkit/latest。{arch-os}为运行环境的架构和操作系统,arch表示操作系统架构,os表示操作系统,例如x86_64-linux。

export DDK_PATH=${INSTALL_DIR}
export NPU_HOST_LIB=${INSTALL_DIR}/{arch-os}/lib64

2. 编译样例工程,生成单算子验证可执行文件。

a. 切换到样例工程根目录,然后在样例工程根目录下执行如下命令创建目录用于存放编译文件,例如,创建的目录为“build”。

mkdir -p build

b. 进入build目录,执行cmake编译命令,生成编译文件,命令示例如下所示:

cd build
cmake ../src

c. 执行如下命令,生成可执行文件。

make

会在工程目录的output目录下生成可执行文件execute_add_op。

3. 执行单算子。

a. 以运行用户(例如HwHiAiUser)拷贝开发环境中样例工程output目录下的execute_add_op到运行环境任一目录。

说明: 若您的开发环境即为运行环境,此拷贝操作可跳过。

b. 在运行环境中,执行execute_add_op:

chmod +x execute_add_op
./execute_add_op

会有如下屏显信息:

[INFO]  Set device[0] success
[INFO] Get RunMode[1] success
[INFO] Init resource success
[INFO] Set input success
[INFO] Copy input[0] success
[INFO] Copy input[1] success
[INFO] Create stream success
[INFO] Execute aclnnAddCustomGetWorkspaceSize success, workspace size 0
[INFO] Execute aclnnAddCustom success
[INFO] Synchronize stream success
[INFO] Copy output[0] success
[INFO] Write output success
[INFO] Run op success
[INFO] Reset Device success
[INFO] Destory resource success

如果有Run op success,表明执行成功,会在output目录下生成输出文件output_z.bin。

4. 比较真值文件。

切换到样例工程根目录,然后执行如下命令:

python3 scripts/verify_result.py output/output_z.bin output/golden.bin

会有如下屏显信息:

test pass

可见,AddCustom算子验证结果正确。

更多学习资源

[1]Ascend C一站式学习资源:https://www.hiascend.com/ascend-c

点击关注,第一时间了解华为云新鲜技术~

一文教你如何调用Ascend C算子的更多相关文章

  1. Ascend Pytorch算子功能验证

    Ascend Pytorch算子功能验证 编写测试用例 以add算子为例,测试脚本文件命名为:add_testcase.py.以下示例仅为一个简单的用例实现,具体算子的实现,需要根据算子定义进行完整的 ...

  2. Ascend Pytorch算子适配层开发

    Ascend Pytorch算子适配层开发 适配方法 找到和PyTorch算子功能对应的NPU TBE算子,根据算子功能计算出输出Tensor的size,再根据TBE算子原型构造对应的input/ou ...

  3. 手把手教你使用LabVIEW人工智能视觉工具包快速实现传统Opencv算子的调用(含源码)

    前言 今天我们一起来使用LabVIEW AI视觉工具包快速实现图像的滤波与增强:图像灰度处理:阈值处理与设定:二值化处理:边缘提取与特征提取等基本操作.工具包的安装与下载方法可见之前的博客. 一.图像 ...

  4. 【OpenCV新手教程之十二】OpenCV边缘检測:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/25560901 作者:毛星云(浅墨) ...

  5. [OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    http://blog.csdn.net/poem_qianmo/article/details/25560901 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...

  6. 学习 opencv---(11)OpenC 边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器

    本篇文章中,我们将一起学习OpenCV中边缘检测的各种算子和滤波器——Canny算子,Sobel算子,Laplace算子以及Scharr滤波器.文章中包含了五个浅墨为大家准备的详细注释的博文配套源代码 ...

  7. 边缘检测:Canny算子,Sobel算子,Laplace算子

    1.canny算子 Canny边缘检测算子是John F.Canny于 1986 年开发出来的一个多级边缘检测算法.更为重要的是 Canny 创立了边缘检测计算理论(Computational the ...

  8. 大数据学习day24-------spark07-----1. sortBy是Transformation算子,为什么会触发Action 2. SparkSQL 3. DataFrame的创建 4. DSL风格API语法 5 两种风格(SQL、DSL)计算workcount案例

    1. sortBy是Transformation算子,为什么会触发Action sortBy需要对数据进行全局排序,其需要用到RangePartitioner,而在创建RangePartitioner ...

  9. 大数据学习day23-----spark06--------1. Spark执行流程(知识补充:RDD的依赖关系)2. Repartition和coalesce算子的区别 3.触发多次actions时,速度不一样 4. RDD的深入理解(错误例子,RDD数据是如何获取的)5 购物的相关计算

    1. Spark执行流程 知识补充:RDD的依赖关系 RDD的依赖关系分为两类:窄依赖(Narrow Dependency)和宽依赖(Shuffle Dependency) (1)窄依赖 窄依赖指的是 ...

  10. opencv6.3-imgproc图像处理模块之边缘检测

    接opencv6.2-improc图像处理模块之图像尺寸上的操作 本文大部分都是来自于转http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutori ...

随机推荐

  1. Bill的挑战

    看数据范围就知道应该要状压,也不难看出应该压缩位数的状态.所以设f[i][j]为前i位,相互匹配的字符串的状态. 那么,就会有 f[i+1][j&a[i][ch]]=(f[i+1][j& ...

  2. 单点登录(SSO)实现详解!!!

    单点登录是什么?你是怎么理解的?单点登录是如何实现的 普通登录 提到单点登录,首先可以想到传统登录,通过登录页面根据用户名查询用户信息,判断密码是否正确,正确则将用户信息写到session,访问的时候 ...

  3. 力扣177(MySQL)-第N高的薪水(中等)

    题目: 表: Employee 编写一个SQL查询来报告 Employee 表中第 n 高的工资.如果没有第 n 个最高工资,查询应该报告为 null . 查询结果格式如下所示 示例1: 示例2: 解 ...

  4. 每天5分钟复习OpenStack(十三)存储缓存技术Bcache

    Ceph作为一个分布式存储,在项目中常见的形态有两者,一种是采用 SSD 或NVME 磁盘做Ceph的日志盘,使用SATA磁盘来做数据盘.这样的好处是比较经济实惠.另一种则是全部采用 SSD 或NVM ...

  5. dotnet C# 通过 Vortice 使用 Direct2D 的 ID2D1CommandList 入门

    本文将告诉大家如何通过 Vortice 使用 D2D 的 CommandList 功能 本文属于 DirectX 系列博客,更多 DirectX 和 D2D 以及 Vortice 库的博客,请参阅我的 ...

  6. 优秀的 Modbus 从站(从机、服务端)仿真器、串口调试工具

    目录 优秀的 Modbus 从站(从机.服务端)仿真器.串口调试工具 主要功能 软件截图 优秀的 Modbus 从站(从机.服务端)仿真器.串口调试工具 官网下载地址:http://www.redis ...

  7. 用Multisim验证简易测谎仪

    用Multisim验证简易测谎仪 测谎仪电路如下图所示: 节点1,2之间用10M欧的电位计代表人体表电阻,原理是撒谎出汗的话,体表电阻就小.Q1,Q2构成互补音频振荡器,振荡频率由R2.C1和R12共 ...

  8. 【人脸识别】OpenCV获取自己的图像

    思路:先获取10000张自己的图像,然后通过CNN神经网络进行学习. 第一步:先获取自己的脸的数据.如何做? 代码如下: import cv2 import os import sys import ...

  9. 开源电子邮件营销平台 listmonk 使用教程

    做产品肯定要做电子邮件营销,特别是面向海外的产品,电子邮件营销已成为企业与客户沟通.建立品牌忠诚度和推动销售的重要工具,可以直接接触到目标受众,提供个性化内容,并以相对较低的成本获得可观的投资回报.你 ...

  10. golang url解析

    package main import "fmt" import "net/url" import "strings" func main( ...