如何在c++侧编译运行一个aclnn(AOL)算子?

1 AOL算子库

CANN(Compute Architecture for Neural Networks)提供了算子加速库(Ascend Operator Library,简称AOL)。该库提供了一系列丰富且深度优化过的高性能算子API,更亲和昇腾AI处理器,调用流程如图1所示。开发者可直接调用算子库API使能模型创新与应用,以进一步提升开发效率和获取极致模型性能。
在这里插入图片描述

单算子API执行的算子接口一般定义为“两段式接口”,以NN算子接口定义为例:

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

其中aclnnXxxGetWorkspaceSize为第一段接口,主要用于计算本次API调用计算过程中需要多少的workspace内存。获取到本次API计算需要的workspace大小后,按照workspaceSize大小申请AI处理器内存,然后调用第二段接口aclnnXxx。
说明:

  • workspace是指除输入/输出外,API在AI处理器上完成计算所需要的临时内存。
  • 第二段接口aclnnXxx(…)不能重复调用,如下调用方式会出现异常:
    aclnnXxxGetWorkspaceSize(…)
    aclnnXxx(…)
    aclnnXxx(…)

2 具体示例

2.1 文件准备

可以从官网获得一个算子的使用示例,如下算子是aclnnAdd:

aclnnAdd&aclnnInplaceAdd-单算子接口-NN类算子接口-单算子API执行-单算子执行-AscendCL API(C&C++)-应用开发接口-CANN商用版8.0.RC2.2开发文档-昇腾社区

#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_add.h"#define CHECK_RET(cond, return_expr) \do {                               \if (!(cond)) {                   \return_expr;                   \}                                \} while (0)#define LOG_PRINT(message, ...)     \do {                              \printf(message, ##__VA_ARGS__); \} while (0)int64_t GetShapeSize(const std::vector<int64_t>& shape) {int64_t shapeSize = 1;for (auto i : shape) {shapeSize *= i;}return shapeSize;
}int Init(int32_t deviceId, aclrtStream* stream) {// 固定写法,AscendCL初始化auto ret = aclInit(nullptr);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);ret = aclrtSetDevice(deviceId);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);ret = aclrtCreateStream(stream);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);return 0;
}template <typename T>
int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr,aclDataType dataType, aclTensor** tensor) {auto size = GetShapeSize(shape) * sizeof(T);// 调用aclrtMalloc申请device侧内存auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);// 调用aclrtMemcpy将host侧数据拷贝到device侧内存上ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);// 计算连续tensor的stridesstd::vector<int64_t> strides(shape.size(), 1);for (int64_t i = shape.size() - 2; i >= 0; i--) {strides[i] = shape[i + 1] * strides[i + 1];}// 调用aclCreateTensor接口创建aclTensor*tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,shape.data(), shape.size(), *deviceAddr);return 0;
}int main() {// 1. (固定写法)device/stream初始化,参考AscendCL对外接口列表// 根据自己的实际device填写deviceIdint32_t deviceId = 0;aclrtStream stream;auto ret = Init(deviceId, &stream);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);// 2. 构造输入与输出,需要根据API的接口自定义构造std::vector<int64_t> selfShape = {4, 2};std::vector<int64_t> otherShape = {4, 2};std::vector<int64_t> outShape = {4, 2};void* selfDeviceAddr = nullptr;void* otherDeviceAddr = nullptr;void* outDeviceAddr = nullptr;aclTensor* self = nullptr;aclTensor* other = nullptr;aclScalar* alpha = nullptr;aclTensor* out = nullptr;std::vector<float> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};std::vector<float> otherHostData = {1, 1, 1, 2, 2, 2, 3, 3};std::vector<float> outHostData(8, 0);float alphaValue = 1.2f;// 创建self aclTensorret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);CHECK_RET(ret == ACL_SUCCESS, return ret);// 创建other aclTensorret = CreateAclTensor(otherHostData, otherShape, &otherDeviceAddr, aclDataType::ACL_FLOAT, &other);CHECK_RET(ret == ACL_SUCCESS, return ret);// 创建alpha aclScalaralpha = aclCreateScalar(&alphaValue, aclDataType::ACL_FLOAT);CHECK_RET(alpha != nullptr, return ret);// 创建out aclTensorret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);CHECK_RET(ret == ACL_SUCCESS, return ret);uint64_t workspaceSize = 0;aclOpExecutor* executor;// aclnnAdd接口调用示例  // 3. 调用CANN算子库API// 调用aclnnAdd第一段接口ret = aclnnAddGetWorkspaceSize(self, other, alpha, out, &workspaceSize, &executor);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAddGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);// 根据第一段接口计算出的workspaceSize申请device内存void* workspaceAddr = nullptr;if (workspaceSize > 0) {ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);}// 调用aclnnAdd第二段接口ret = aclnnAdd(workspaceAddr, workspaceSize, executor, stream);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAdd failed. ERROR: %d\n", ret); return ret);// 4. (固定写法)同步等待任务执行结束ret = aclrtSynchronizeStream(stream);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);// 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改auto size = GetShapeSize(outShape);std::vector<float> resultData(size, 0);ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr,size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);for (int64_t i = 0; i < size; i++) {LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]);}// aclnnInplaceAdd接口调用示例  // 3. 调用CANN算子库APILOG_PRINT("\ntest aclnnInplaceAdd\n");// 调用aclnnInplaceAdd第一段接口ret = aclnnInplaceAddGetWorkspaceSize(self, other, alpha, &workspaceSize, &executor);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceAddGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);// 根据第一段接口计算出的workspaceSize申请device内存if (workspaceSize > 0) {ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);}// 调用aclnnInplaceAdd第二段接口ret = aclnnInplaceAdd(workspaceAddr, workspaceSize, executor, stream);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceAdd failed. ERROR: %d\n", ret); return ret);// 4. (固定写法)同步等待任务执行结束ret = aclrtSynchronizeStream(stream);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);// 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr,size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);for (int64_t i = 0; i < size; i++) {LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]);}  // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改aclDestroyTensor(self);aclDestroyTensor(other);aclDestroyScalar(alpha);aclDestroyTensor(out);// 7. 释放Device资源,需要根据具体API的接口定义修改aclrtFree(selfDeviceAddr);aclrtFree(otherDeviceAddr);aclrtFree(outDeviceAddr);if (workspaceSize > 0) {aclrtFree(workspaceAddr);}aclrtDestroyStream(stream);aclrtResetDevice(deviceId);aclFinalize();return 0;
}

如果将该文件命名为test_add.cpp,那么接下来写它的CMakeLists文件,可从如下模板中修改。

重要内容修改:

  • add_executable中的文件名称,比如当前要改成test_add.cpp
# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved.# CMake lowest version requirement
cmake_minimum_required(VERSION 3.14)# 设置工程名
project(ACLNN_EXAMPLE)# Compile options
add_compile_options(-std=c++11)# 设置编译选项
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY  "./bin")    
set(CMAKE_CXX_FLAGS_DEBUG "-fPIC -O0 -g -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-fPIC -O2 -Wall")# 设置可执行文件名(如opapi_test),并指定待运行算子文件*.cpp所在目录
add_executable(opapi_testtest_add.cpp) # 设置ASCEND_PATH(CANN软件包目录,请根据实际路径修改)和INCLUDE_BASE_DIR(头文件目录)
if(NOT "$ENV{ASCEND_CUSTOM_PATH}" STREQUAL "")      set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH})
else()set(ASCEND_PATH "/usr/local/Ascend/ascend-toolkit/latest")
endif()
set(INCLUDE_BASE_DIR "${ASCEND_PATH}/include")
include_directories(${INCLUDE_BASE_DIR}${INCLUDE_BASE_DIR}/aclnn
)# 设置链接的库文件路径
target_link_libraries(opapi_test PRIVATE${ASCEND_PATH}/lib64/libascendcl.so${ASCEND_PATH}/lib64/libnnopbase.so${ASCEND_PATH}/lib64/libopapi.so)# 可执行文件在CMakeLists文件所在目录的bin目录下
install(TARGETS opapi_test DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

2.2 编译运行

1、进入CMakeLists.txt所在目录,执行如下命令,新建build目录存放生成的编译文件。执行:

source ${install_path}/set_env.sh。#install_path为CANN的安装目录,一般为/usr/local/Ascend/ascend-toolkit/latest

2、进入build目录,执行cmake命令编译,再执行make命令生成可执行文件。

cd build
cmake ../ -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE
make

编译成功后,会在build目录的bin文件夹下生成opapi_test可执行文件。

3、进入bin目录,运行可执行文件opapi_test。

cd bin
./opapi_test

以Add算子的运行结果为例,运行后的结果示例如下:

result[0] is: 1.200000
result[1] is: 2.200000
result[2] is: 3.200000
result[3] is: 5.400000
result[4] is: 6.400000
result[5] is: 7.400000
result[6] is: 9.600000
result[7] is: 10.600000

可参考官网:

编译与运行样例-NN类算子接口-单算子API执行-单算子执行-AscendCL API(C&C++)-应用开发接口-API参考-CANN商用版8.0.RC2.2开发文档-昇腾社区 (hiascend.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/467633.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA git提交时如何忽略某个文件或文件夹

步骤如下 英文界面操作顺序 打开file——>settings——>Editor——>File Types 中文插件操作顺序 打开 文件——>设置——>编辑器——> 文件类型 安装下面的操作顺序添加想要屏蔽文件类型后缀即可&#xff1a;

《常用深度学习神经网络及其原理与应用场景》

一、总体介绍 一、引言 随着科技的不断发展&#xff0c;深度学习已经成为人工智能领域中最具影响力的技术之一。深度学习神经网络通过模拟人类大脑的神经元结构和工作方式&#xff0c;能够自动学习数据中的特征和模式&#xff0c;从而实现各种复杂的任务&#xff0c;如图像识…

科技革命前沿:救援机器人!

救援机器人主要制作材料 传统刚性材料&#xff1a;传统救援机器人多采用金属等刚性材料制作&#xff0c;以确保其结构强度和稳定性。这些材料在承受较大负载和复杂环境时表现出色&#xff0c;但可能缺乏一定的灵活性。 软体材料&#xff1a;近年来&#xff0c;软体机器人技术…

Ubuntu中以root身份运行Qt创建的项目

Ubuntu中以root身份运行Qt创建的项目 Chapter1 Ubuntu中以root身份运行Qt创建的项目解决方法&#xff1a; Chapter1 Ubuntu中以root身份运行Qt创建的项目 原文链接&#xff1a;https://blog.csdn.net/lhbaba/article/details/124733323 使用Qt开发项目时遇到了一个问题&#…

leetcode25:k个一组链表反转

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

ctfshow-web入门-反序列化(web265-web270)

目录 1、web265 2、web266 3、web267 4、web268 5、web269 6、web270 1、web265 很简单的一个判断&#xff0c;满足 $this->token$this->password; 即可 由于 $ctfshow->tokenmd5(mt_rand()) 会将 token 随机为一个 md5 值&#xff0c;我们使用 & 绕一下&am…

qt QLocale详解

1、概述 QLocale是Qt框架中的一个类&#xff0c;用于处理与本地化相关的操作。它能够方便地实现日期、时间、数字和货币的格式化和解析&#xff0c;支持不同的语言、区域设置和字符集。QLocale提供了一种跨平台的方式来获取当前系统的语言设置&#xff0c;并返回该语言的本地化…

年龄大了,听力一定会下降吗?

随着年龄的增长&#xff0c;听力下降&#xff08;也称为老年性听力损失或感音神经性聋&#xff09;确实是一个常见的现象&#xff0c;但并不是每个人都会经历明显的听力下降。以下是一些影响因素和相关信息&#xff1a; 1. 自然老化过程 •随着年龄的增长&#xff0c;内耳的毛…

Linux SSH私钥认证结合cpolar内网穿透安全高效远程登录指南

文章目录 前言1. Linux 生成SSH秘钥对2. 修改SSH服务配置文件3. 客户端秘钥文件设置4. 本地SSH私钥连接测试5. Linux安装Cpolar工具6. 配置SSHTCP公网地址7. 远程SSH私钥连接测试8. 固定SSH公网地址9. 固定SSH地址测试 前言 开发人员在工作中经常需要远程访问服务器和数据中心…

国产化浪潮下,高科技企业如何选择合适的国产ftp软件方案?

高科技企业在数字化转型和创新发展中&#xff0c;数据资产扮演着越来越重要的角色。在研发过程中产生的实验数据、设计文档、测试结果等&#xff0c;专利、商标、版权之类的创新成果等&#xff0c;随着信息量急剧增加和安全威胁的复杂化&#xff0c;传统的FTP软件已经不能满足这…

高校宿舍信息管理系统小程序

作者主页&#xff1a;编程千纸鹤 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参…

DNS域名详细解析详解

文章目录 DNS域名详细解析详解一、引言二、DNS域名解析过程1、DNS解析概述1.1、DNS解析的基本步骤 2、代码示例 三、DNS查询类型1、递归查询2、迭代查询 四、总结 DNS域名详细解析详解 一、引言 在互联网的世界里&#xff0c;域名和IP地址是两个不可或缺的概念。IP地址是计算…

Selenium自动化测试 —— 模拟鼠标键盘的操作事件

软件测试资料领取&#xff1a;[内部资源] 想拿年薪40W的软件测试人员&#xff0c;这份资料必须领取~ 软件测试面试刷题工具&#xff1a;软件测试面试刷题【800道面试题答案免费刷】 鼠标操作事件 在实际的web产品测试中&#xff0c;对于鼠标的操作&#xff0c;不单单只有clic…

全网视频下载神器一键下载全网视频!

前言 想从网上下载视频和音乐到手机吗&#xff1f;那真的很简单&#xff01;这个应用支持各种格式&#xff0c;而且完全不用花钱。当你在下载器内打开一个网站视频&#xff0c;下载器会自动“看到”它&#xff0c;你只需要点一下&#xff0c;下载就开始了。下载过程中&#xf…

系统架构师2023版:习题

架构设计基础 计算机基础 目前处理器市场中存在 CPU 和 DSP 两种类型的处理器&#xff0c;分别用于不同的场景&#xff0c;这两种处理器具有不同的体系结构&#xff0c;DSP采用()。 A.冯诺依曼结构 B.哈佛结构 C.FPGA 结构 D.与 GPU 相同的结构 解析:…

C++:lambda表达式

lambda表达式是一个可调用对象。 lambda表达式定义&#xff1a; 看作一个匿名函数。定义lambda&#xff0c;[ ]开始&#xff0c;跟&#xff08;&#xff09;&#xff0c;括号内传递参数 &#xff0c;{ }内接函数体。用一个auto 类型的变量接收。把该变量名当作该匿名函数的函数…

javascript实现sha512和sha384算法(支持微信小程序),可分多次计算

概述&#xff1a; 本人前端需要实现sha512和sha384计算的功能&#xff0c;最好是能做到分多次计算。 本文所写的代码在现有sha512和sha384的C代码&#xff0c;反复测试对比计算过程参数&#xff0c;成功改造成sha512和sha384的javascript代码&#xff0c;并成功验证好分多次计算…

C++类和对象 (下)

文章目录 前言一. 再探构造函数初始化列表特性总结练习 二. 类型转换2.1 隐式类型转换2.2 临时对象具有常性2.3 explicit关键字2.4 多参数类型转化 三. static成员概念特性练习 四. 友元概念特性 五. 内部类概念特性 六. 匿名对象概念特性 七. 对象拷贝时的编译器优化END 前言 …

【数据集】【YOLO】【目标检测】航拍船只识别数据集 3550 张,YOLO航拍水面船只识别算法实战训练教程!

一、数据集介绍 【数据集】航拍船只识别数据集 3550 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。 数据集中包含1种分类&#xff1a;{0: ship}&#xff0c;代表水面船只。 数据集来自国内外图片网站、无人机航拍视频截图以及卫星云图&#xff1b; 可用于无人…

【LeetCode】【算法】48. 旋转图像

LeetCode 48. 旋转图像 题目描述 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 思路 思路&#xff1a;再次拜见K神&#xf…