为了帮助开发者快速的完成算子的Kernel Launch,方便开发者调试调优,提供简易的算子工程,您可以基于该算子工程中的样例代码和工程框架进行算子开发。算子工程提供的功能如下:
wget https://cmake.org/files/v3.16/cmake-3.16.0.tar.gz --no-check-certificate tar -zxvf cmake-3.16.0.tar.gz cd cmake-3.16.0 ./bootstrap --prefix=/usr sudo make sudo make install
算子工程样例所在路径为:CANN软件安装后文件存储路径中的“tools/ascendc_kernel_sample”。
样例目录结构如下所示:
ascendc_kernel_sample ├── CMakeLists.txt // CMake编译配置文件 ├── main.cpp // NPU域算子调用应用程序 ├── add_custom.cpp // 矢量算子kernel实现 ├── matmul_custom.cpp // 矩阵算子kernel实现 ├── matmul_leakyrelu_custom.cpp // 矢量+矩阵融合算子kernel实现 └── run.sh // 编译运行算子的脚本
基于该算子工程,开发者进行算子开发的步骤如下:
请参考矢量编程和工程目录中的矩阵算子、融合算子的kernel实现完成Ascend C算子实现文件的编写。
下面代码以固定shape的add_custom算子为例,介绍算子核函数调用的应用程序main.cpp如何编写。您在实现自己的应用程序时,需要关注由于算子核函数不同带来的修改,包括算子核函数名,入参出参的不同等,合理安排相应的内存分配、内存拷贝和文件读写等,相关API的调用方式直接复用即可。
#include <stdio.h> #include <string.h> #include <securec.h> #include "acl/acl.h" #include "aclrtlaunch_add_custom.h"
int32_t main(void) { constexpr int32_t loopValue = 16; constexpr uint32_t blockDim = 8; constexpr uint32_t rowDim = 1024; constexpr uint32_t colDim = 2048; size_t inputByteSize = rowDim * colDim * sizeof(aclFloat16); size_t outputByteSize = rowDim * colDim * sizeof(aclFloat16); aclFloat16 duplicateValue = aclFloatToFloat16(4.0f); // AscendCL初始化 CHECK_ACL(aclInit(nullptr)); // 运行管理资源申请 aclrtContext context; int32_t deviceId = 0; CHECK_ACL(aclrtSetDevice(deviceId)); CHECK_ACL(aclrtCreateContext(&context, deviceId)); aclrtStream stream = nullptr; CHECK_ACL(aclrtCreateStream(&stream)); // 分配Host内存 uint8_t *xHost, *yHost, *zHost; uint8_t *xDevice, *yDevice, *zDevice; CHECK_ACL(aclrtMallocHost((void**)(&xHost), inputByteSize)); CHECK_ACL(aclrtMallocHost((void**)(&yHost), inputByteSize)); CHECK_ACL(aclrtMallocHost((void**)(&zHost), outputByteSize)); // 分配Device内存 CHECK_ACL(aclrtMalloc((void**)&xDevice, inputByteSize, ACL_MEM_MALLOC_HUGE_FIRST)); CHECK_ACL(aclrtMalloc((void**)&yDevice, inputByteSize, ACL_MEM_MALLOC_HUGE_FIRST)); CHECK_ACL(aclrtMalloc((void**)&zDevice, outputByteSize, ACL_MEM_MALLOC_HUGE_FIRST)); // Host内存初始化 for (int i = 0; i < rowDim * colDim; ++i) { xHost[i] = duplicateValue; yHost[i] = duplicateValue; } // 将数据从Host上拷贝到Device上 CHECK_ACL(aclrtMemcpy(xDevice, inputByteSize, xHost, inputByteSize, ACL_MEMCPY_HOST_TO_DEVICE)); CHECK_ACL(aclrtMemcpy(yDevice, inputByteSize, yHost, inputByteSize, ACL_MEMCPY_HOST_TO_DEVICE)); // 用内核调用符ACLRT_LAUNCH_KERNEL调用核函数完成指定的运算 CHECK_ACL(ACLRT_LAUNCH_KERNEL(add_custom)(blockDim, stream, xDevice, yDevice, zDevice)); CHECK_ACL(aclrtSynchronizeStream(stream)); // 将Device上的运算结果拷贝回Host CHECK_ACL(aclrtMemcpy(zHost, outputByteSize, zDevice, outputByteSize, ACL_MEMCPY_DEVICE_TO_HOST)); // 打印数据结果,作为示例只打印前16个数 printf("output of add_custom:\n"); for (int i = 0; i < loopValue; i++) { printf("%f ", aclFloat16ToFloat(zHost[i])); } // 释放申请的资源 CHECK_ACL(aclrtFree(xDevice)); CHECK_ACL(aclrtFree(yDevice)); CHECK_ACL(aclrtFree(zDevice)); CHECK_ACL(aclrtFreeHost(xHost)); CHECK_ACL(aclrtFreeHost(yHost)); CHECK_ACL(aclrtFreeHost(zHost)); // AscendCL去初始化 CHECK_ACL(aclrtDestroyStream(stream)); CHECK_ACL(aclrtDestroyContext(context)); CHECK_ACL(aclrtResetDevice(deviceId)); CHECK_ACL(aclFinalize()); return 0; }
CMake编译配置文件的样例如下,开发者可基于该样例修改环境变量和Cmake命令参数的配置,配置参数的具体说明请参考表1和表2。
cmake_minimum_required(VERSION 3.16.0) project(Ascend_C) # user-defined configuration // 配置AI处理器型号,请配置为实际的AI处理器型号 set(SOC_VERSION "ascendxxx" CACHE STRING "system on chip type") // 配置CANN软件包安装后的实际路径 set(ASCEND_CANN_PACKAGE_PATH "/usr/local/Ascend/ascend-toolkit/latest/" CACHE PATH "ASCEND CANN package installation directory") // 配置编译模式,"Release"或者"Debug" set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type Release/Debug (default Debug)" FORCE) // 配置CMAKE执行install时,安装的路径前缀 set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_LIST_DIR}/out" CACHE STRING "path for install()" FORCE) if(EXISTS ${ASCEND_CANN_PACKAGE_PATH}/compiler/tikcpp/ascendc_kernel_cmake) set(ASCENDC_CMAKE_DIR ${ASCEND_CANN_PACKAGE_PATH}/compiler/tikcpp/ascendc_kernel_cmake) else() message(FATAL_ERROR "ascendc_kernel_cmake does not exist, please check whether the compiler package is installed.") endif() // 包含内部的编译文件 include(${ASCENDC_CMAKE_DIR}/ascendc.cmake) // 按需添加kernel实现文件 ascendc_library(kernels STATIC add_custom.cpp ) // 配置编译宏,按需使能PRINTF/DumpTensor功能 ascendc_compile_definitions(kernels PRIVATE -DASCENDC_DUMP) // 请替换为算子调用源文件 add_executable(main main.cpp) target_link_libraries(main PRIVATE kernels )
环境变量 |
配置说明 |
---|---|
SOC_VERSION |
AI处理器的型号。 在安装昇腾AI处理器的服务器执行npu-smi info命令进行查询,查询到的“Name”前增加Ascend信息,例如“Name”对应取值为xxxyy,实际配置的SOC_VERSION值为Ascendxxxyy。 |
ASCEND_CANN_PACKAGE_PATH |
CANN软件包安装后的实际路径。 |
CMAKE_BUILD_TYPE |
编译模式选项,可配置为:
|
CMAKE_INSTALL_PREFIX |
用于指定CMAKE执行install时,安装的路径前缀,执行install后编译产物(ascendc_library中指定的target以及对应的头文件)会安装在该路径下。默认路径为当前目录的out目录下。 |
环境变量 |
配置说明 |
---|---|
add_executable |
使用指定的源文件将可执行文件添加到项目中。和Cmake通用的命令参数使用方法一致。 |
ascendc_library |
使用指定的核函数源文件向项目(project)添加库。语法格式如下: ascendc_library(<target_name> [STATIC | SHARED] [<source>...]) 其中<target_name>表示库文件的名字,该库文件会根据命令里列出的源文件来建立。STATIC、SHARED的做用是指定生成的库文件的类型。STATIC库是目标文件的归档文件,在连接其它目标的时候使用。SHARED库会被动态连接(动态连接库),在运行时会被加载。<source>表示核函数源文件。 |
ascendc_compile_definitions |
添加编译宏。可以添加Ascend C提供的编译宏和开发者自定义的编译宏。语法格式如下: ascendc_compile_definitions(<target_name> [PRIVATE] [<xxx>...]) Ascend C提供的编译宏介绍如下: -DASCENDC_DUMP :使用PRINTF或DumpTensor功能时需要添加该编译宏。 |
简化的编译流程图如下图所示:将算子核函数源文件编译生成kernel侧的库文件(*.so或*.a库文件);工程框架自动生成核函数调用接口声明头文件;编译main.cpp(算子调用应用程序)时依赖上述头文件,将编译应用程序生成的目标文件和kernel侧的库文件进行链接,生成最终的可执行文件。
编译安装结束后在CMAKE_INSTALL_PREFIX目录下生成的编译产物示例如下;最终的可执行文件会生成在cmake命令的执行目录下。
out ├── lib │ ├── libkernels1.a │ ├── libkernels2.so ├── include │ ├── kernels1 │ ├── aclrtlaunch_matmul_custom.h │ ├── aclrtlaunch_add_custom.h │ ├── kernels2 │ ├── aclrtlaunch_xxx.h │ ├── ...
您可以基于样例工程中提供的一键式编译运行脚本进行快速编译,在NPU侧执行Ascend C算子。同时可以按需完成性能数据的采集和分析。run.sh的代码如下:
set -e rm -rf build out mkdir build cmake -B build cmake --build build -j cmake --install build #执行可执行文件,请替换为实际的应用程序二进制名称 ./build/main #使能profiling功能 msprof --ai-core=on --ascendcl=on --model-execution=on --runtime-api=on --task-time=on --application="./build/main"
bash run.sh
运行脚本后可以看到结果输出示例如下:
output of add_custom: 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000