OrangePi AIpro 香橙派 昇腾 Ascend C 算子开发 与 调用 通过aclnn调用的方式调用 - AddCustom算子 - 单算子API执行(aclnn)
多种算子调用方式
* | 开发时间 | 使用场景 | 调用方式 | 运行硬件 |
---|---|---|---|---|
基于Kernel直调工程(快速) | 少 | 单算子调用,快速验证算法逻辑 | ICPU_RUN_KF 内核调用符号 ACLRT_LAUNCH_KERNEL | CPU NPU NPU |
基于自定义算子工程(标准) | 多 | 单算子网络/整网部署调用 | aclInnxxx aclopExecuteV2 pytorch adapter | NPU NPU NPU |
AscendCL单算子调用概述
完成自定义算子的开发部署(标准流程)后,可以通过单算子调用的方式来验证单算子的功能。单算子调用有API执行和模型执行两种方式:
单算子API执行(aclnn) (本文描述的):
基于C语言的API执行算子,直接调用单算子API接口。多用于大模型训练算子,既整网算子模型较为固定的场景。
单算子模型执行:
基于图模式执行算子,先编译算子(例如,使用ATC工具将AscendIR定义的单算子描述文件编译成算子om模型文件),再调用AscendCL接口加载算子模型,最后调用AscendCL接口执行算子。多用于搜广推,整网模型变化较大的场景。
前面的内容是实现AddCustom算子,这里是通过aclnn调用的方式调用AddCustom算子,也是基于C语言的API执行算子,直接调用单算子API接口。
所有有两部分
一部分是 之前的实现
/home/HwHiAiUser/samples/operator/AddCustomSample/FrameworkLaunch/AddCustom
可以从json串开始生成代码骨架,然后开始填写内容。具体请看这里
前置知识 1
前置知识 2
前置知识 3
编译
因为单算子API执行方式,会自动在编译工程的build_out/autogen目录下生成.cpp和.h,编写单算子的调用代码时,要包含自动生成的单算子API执行接口头文件。
#include "aclnn_add_custom.h"
/home/HwHiAiUser/samples/operator/AddCustomSample/FrameworkLaunch/AddCustom/build_out/
编译完之后开始安装
编译成功后打包为run文件
然后安装run,安装完整就可以用了
安装完就可以调用了
另一部分算子调用
/home/HwHiAiUser/samples/operator/AddCustomSample/FrameworkLaunch/AclNNInvocation/
├──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配置文件
运行结果
自定义算子编译部署后,会自动生成单算子API,可以直接在应用程序中调用。算子API的形式一般定义为“两段式接口”,形如:
aclnnStatus aclnnXxxGetWorkspaceSize(const aclTensor *src, ..., aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor);
aclnnStatus aclnnXxx(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);
Xxx替换实际使用的函数
// 获取算子使用的workspace空间大小
aclnnStatus aclnnAddCustomGetWorkspaceSize(
const aclTensor *x,
const aclTensor *y,
const alcTensor *out,
uint64_t workspaceSize,
aclOpExecutor **executor);
// 执行算子
aclnnStatus aclnnAddCustom(void *workspace,
int64_t workspaceSize,
aclOpExecutor **executor,
aclrtStream stream);
单算子API(aclnn)调用–关键代码
重要的就两个函数
bool OpRunner::RunOp()
{
for (size_t i = 0; i < numInputs_; ++i) {
auto size = GetInputSize(i);
aclrtMemcpyKind kind = ACL_MEMCPY_HOST_TO_DEVICE;
if (g_isDevice) {
kind = ACL_MEMCPY_DEVICE_TO_DEVICE;
}
if (aclrtMemcpy(devInputs_[i], size, hostInputs_[i], size, kind) != ACL_SUCCESS) {
ERROR_LOG("Copy input[%zu] failed", i);
return false;
}
INFO_LOG("Copy input[%zu] success", i);
}
aclrtStream stream = nullptr;
if (aclrtCreateStream(&stream) != ACL_SUCCESS) {
ERROR_LOG("Create stream failed");
return false;
}
INFO_LOG("Create stream success");
size_t workspaceSize = 0;
aclOpExecutor *handle = nullptr;
auto ret =
aclnnAddCustomGetWorkspaceSize(inputTensor_[0], inputTensor_[1], outputTensor_[0], &workspaceSize, &handle);
if (ret != ACL_SUCCESS) {
(void)aclrtDestroyStream(stream);
ERROR_LOG("Get Operator Workspace failed. error code is %d", static_cast<int32_t>(ret));
return false;
}
INFO_LOG("Execute aclnnAddCustomGetWorkspaceSize success, workspace size %lu", workspaceSize);
if (workspaceSize != 0) {
if (aclrtMalloc(&workspace_, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST) != ACL_SUCCESS) {
ERROR_LOG("Malloc device memory failed");
}
}
ret = aclnnAddCustom(workspace_, workspaceSize, handle, stream);
if (ret != ACL_SUCCESS) {
(void)aclrtDestroyStream(stream);
ERROR_LOG("Execute Operator failed. error code is %d", static_cast<int32_t>(ret));
return false;
}
INFO_LOG("Execute aclnnAddCustom success");
ret = aclrtSynchronizeStreamWithTimeout(stream, 5000);
if (ret != SUCCESS) {
ERROR_LOG("Synchronize stream failed. error code is %d", static_cast<int32_t>(ret));
(void)aclrtDestroyStream(stream);
return false;
}
INFO_LOG("Synchronize stream success");
for (size_t i = 0; i < numOutputs_; ++i) {
auto size = GetOutputSize(i);
aclrtMemcpyKind kind = ACL_MEMCPY_DEVICE_TO_HOST;
if (g_isDevice) {
kind = ACL_MEMCPY_DEVICE_TO_DEVICE;
}
if (aclrtMemcpy(hostOutputs_[i], size, devOutputs_[i], size, kind) != ACL_SUCCESS) {
INFO_LOG("Copy output[%zu] success", i);
(void)aclrtDestroyStream(stream);
return false;
}
INFO_LOG("Copy output[%zu] success", i);
}
(void)aclrtDestroyStream(stream);
return true;
}