ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CANN Runtime 内建任务实战指南:Reduce 归约与随机数生成的异步下发(aclrtReduceAsync / aclrtRandomNumAsync)

CANN Runtime 内建任务实战指南:Reduce 归约与随机数生成的异步下发(aclrtReduceAsync / aclrtRandomNumAsync) CANN Runtime 内建任务实战指南Reduce 归约与随机数生成的异步下发aclrtReduceAsync / aclrtRandomNumAsync【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtimeCANN Runtime 将归约Reduce、随机数生成等高频计算能力封装为内建任务built-in task应用可以直接通过aclrtReduceAsync、aclrtRandomNumAsync等 API 在 Stream 上异步下发无需自行编写并编译算子内核即可在 Device 侧完成数据归约与多分布随机数生成。本文基于本仓库example/2_advanced_features/built_in_task目录下的两个可运行样例完整讲解这两类内建任务的使用流程、核心 API 结构体与参数语义、随机数算法与内存要求并结合include/external/acl/acl_rt.h等源码级定义验证其底层接口契约。读完本文你可以直接复现 Reduce 求和样例与五类随机数生成样例并掌握在自有程序中正确组装aclrtRandomNumTaskInfo下发随机数任务的完整方法。一、内建任务是什么在 CANN 的编程模型中绝大多数 Device 侧计算以**算子内核Kernel**形式下发应用侧编译或加载内核二进制再通过 Stream 提交执行。而内建任务是 Runtime 预先内置在 Device 侧的一类特殊任务无需用户提供内核代码只需通过几个专用 API 描述任务参数即可完成一类标准化的数据操作。example/2_advanced_features/built_in_task目录正是围绕这一能力组织样例的包含两个子样例样例目录演示能力核心 API0_reduce_taskReduce 任务的下发与结果读取数组求和aclrtReduceAsync1_random_num_task随机数任务的下发与多分布生成aclrtRandomNumAsync两者共用同一套初始化 → 设置 Device → 创建 Stream → 申请/拷贝内存 → 异步下发 → 同步 → 拷回结果 → 释放资源的标准 Runtime 流程差异只在于任务本身的参数组装方式。二、产品支持情况两个内建任务样例在产品上的支持情况略有差异下表可直接作为选型参考产品Reduce 任务0_reduce_task随机数任务1_random_num_taskAscend 950PR / Ascend 950DT√×Atlas A3 训练系列产品 / Atlas A3 推理系列产品√√Atlas A2 训练系列产品 / Atlas A2 推理系列产品√√可以看到随机数任务目前在 Ascend 950PR / Ascend 950DT 上暂不支持在 Atlas A2 / A3 系列上两类任务均可运行。开发前请先核对目标硬件型号。三、Reduce 归约任务3.1 功能概述归约Reduce是并行计算中的常见操作对数组元素执行求和、求最大值、求最小值等聚合运算。在深度学习中归约被广泛用于 loss 计算、梯度聚合、attention 内部的归一化统计等场景。通过aclrtReduceAsync应用可以在不编写内核的情况下把这段计算直接交给 Device 侧完成。3.2 核心 APIaclrtReduceAsync该 API 的原型在 include/external/acl/acl_rt.h 中声明ACL_FUNC_VISIBILITY aclError aclrtReduceAsync( void* dst, const void* src, uint64_t count, aclrtReduceKind kind, aclDataType type, aclrtStream stream, void* reserve);各参数说明参数说明dst输出地址指向 Device 内存中存放归约结果的位置src输入地址指向 Device 内存中的源数据count数据大小单位为字节注意不是元素个数样例中传入count * sizeof(float)kind归约类型取值为aclrtReduceKind枚举type输入/输出的数据类型取值为aclDataType如ACL_FLOATstream任务下发所在的 Streamreserve预留参数当前传NULL返回ACL_SUCCESS表示调用成功其他值表示失败该调用本身是异步的任务真正执行完成需要配合aclrtSynchronizeStream等待。3.3 归约类型aclrtReduceKindaclrtReduceKind枚举定义在 include/external/acl/acl_rt.htypedef enum { ACL_RT_MEMCPY_SDMA_AUTOMATIC_SUM 10, // 自动求和前缀和 ACL_RT_MEMCPY_SDMA_AUTOMATIC_MAX 11, // 求最大值 ACL_RT_MEMCPY_SDMA_AUTOMATIC_MIN 12, // 求最小值 ACL_RT_MEMCPY_SDMA_AUTOMATIC_EQUAL 13, // 相等比较 } aclrtReduceKind;从源码结构看归约能力与 Runtime 的 SDMA数据搬运引擎自动模式绑定因此这组枚举沿用了ACL_RT_MEMCPY_SDMA_AUTOMATIC_*的命名风格。Reduce 样例使用的ACL_RT_MEMCPY_SDMA_AUTOMATIC_SUM执行的是前缀和扫描求和输出数组的第 i 个元素等于输入数组前 i1 个元素之和而不是简单的单值归约——这正是样例输出中结果呈现递增累加的原因。3.4 完整代码流程解析样例主体见 0_reduce_task/main.cpp完整流程如下#include acl/acl.h #include utils.h int main() { aclError ret; // 1. 初始化 ACL CHECK_ERROR(aclInit(NULL)); // 2. 指定用于运算的 Device CHECK_ERROR(aclrtSetDevice(0)); // 3. 创建 Stream aclrtStream stream; CHECK_ERROR(aclrtCreateStream(stream)); // 4. 准备 Host 数据4 个 float 元素 const int count 4; float hostInput[4] {1.0, 2.0, 3.0, 4.0}; float hostOutput[4] {0, 0, 0, 0}; size_t size count * sizeof(float); // 5. 申请 Device 内存 void* devInput NULL; void* devOutput NULL; CHECK_ERROR(aclrtMalloc(devInput, size, ACL_MEM_MALLOC_HUGE_FIRST)); CHECK_ERROR(aclrtMalloc(devOutput, size, ACL_MEM_MALLOC_HUGE_FIRST)); // 6. 拷贝数据到 Device CHECK_ERROR(aclrtMemcpy(devInput, size, hostInput, size, ACL_MEMCPY_HOST_TO_DEVICE)); CHECK_ERROR(aclrtMemcpy(devOutput, size, hostInput, size, ACL_MEMCPY_HOST_TO_DEVICE)); // 7. 异步下发 Reduce 任务前缀和 ret aclrtReduceAsync( devOutput, devInput, size, ACL_RT_MEMCPY_SDMA_AUTOMATIC_SUM, // 归约类型 ACL_FLOAT, // 数据类型 stream, NULL); CHECK_ERROR(ret); // 8. 同步等待 Stream 上的任务执行完成 CHECK_ERROR(aclrtSynchronizeStream(stream)); // 9. 将结果拷回 Host CHECK_ERROR(aclrtMemcpy(hostOutput, size, devOutput, size, ACL_MEMCPY_DEVICE_TO_HOST)); // 10. 打印结果 for (int i 0; i count; i) { printf(Reduce SUM result[%d] %f\n, i, hostOutput[i]); } // 11. 释放资源 aclrtFree(devInput); aclrtFree(devOutput); aclrtDestroyStream(stream); aclrtResetDeviceForce(0); aclFinalize(); return 0; }代码中的CHECK_ERROR宏来自 example/utils.h它检查aclError返回值非ACL_SUCCESS时打印错误信息并退出是样例统一的错误处理方式。几个值得注意的工程细节aclrtResetDeviceForce用于强制复位当前运算的 Device 并回收其上的资源与普通释放相比更彻底适合样例这种一次性运行的场景。dst与src指向同一块内存或内容相同是允许的样例先把devOutput也填成输入值再把归约结果写回devOutput读者可以观察这种原地覆盖式用法。count的单位是字节若元素是 float4 字节4 个元素即 16 字节。3.5 示例输出运行0_reduce_task后预期输出如下对应{1, 2, 3, 4}的前缀和序列Reduce SUM result[0] 2.000000 Reduce SUM result[1] 4.000000 Reduce SUM result[2] 6.000000 Reduce SUM result[3] 8.000000 [INFO] Sample run successfully.注意result[0]的值为 2.0 而不是 1.0这是因为样例在dst中预先放置了与输入相同的内容前缀和从输入首元素开始累加因此result[0] 1.0原值 1.0输入首元素 2.0其余元素依此类推。若希望得到{1, 3, 6, 10}的纯前缀和结果可将devOutput初始化为全 0参考随机数样例中aclrtMemset清零的做法。四、随机数生成任务4.1 功能概述aclrtRandomNumAsync是 Runtime 提供的另一类内建任务在 Device 侧直接生成指定分布、指定类型的随机数并支持生成 Dropout 所需的 bitmask。它支持均匀分布、正态分布、截断正态分布与 Dropout bitmask 四种函数类型且无需用户编写内核代码非常适合初始化阶段的大规模随机数填充如权重初始化、mask 生成。4.2 核心 APIaclrtRandomNumAsync该 API 原型声明于 include/external/acl/acl_rt.hACL_FUNC_VISIBILITY aclError aclrtRandomNumAsync( const aclrtRandomNumTaskInfo* taskInfo, const aclrtStream stream, void* reserve);参数说明taskInfo随机数任务信息见下文aclrtRandomNumTaskInfo结构体stream任务下发所在的 Streamreserve预留字段当前传NULL与aclrtReduceAsync的位置参数风格不同随机数任务的所有信息通过一个结构体聚合传递因此参数组装是关键。4.3 任务信息结构体aclrtRandomNumTaskInfoaclrtRandomNumTaskInfo定义在 include/external/acl/acl_rt.htypedef struct { aclDataType dataType; aclrtRandomNumFuncParaInfo randomNumFuncParaInfo; void *randomParaAddr; void *randomResultAddr; void *randomCounterAddr; aclrtRandomParaInfo randomSeed; aclrtRandomParaInfo randomNum; uint8_t rsv[10]; } aclrtRandomNumTaskInfo;字段语义如下字段说明dataType随机数的数据类型ACL_FLOAT、ACL_FLOAT16、ACL_BF16及整数类型等randomNumFuncParaInfo函数类型标识与分布参数见 4.4randomParaAddr分布参数所在设备地址样例中参数直接内嵌在结构体中此处传NULLrandomResultAddr输出缓冲区Device 地址存放生成的随机数randomCounterAddr随机数状态计数器Counter的 Device 地址固定 16 字节randomSeed随机种子aclrtRandomParaInfo封装见 4.5randomNum生成的随机数个数aclrtRandomParaInfo封装rsv[10]预留字节置 0 即可4.4 函数类型与分布参数aclrtRandomNumFuncParaInfo函数类型枚举与参数联合体定义在 include/external/acl/acl_rt.htypedef enum { ACL_RT_RANDOM_NUM_FUNC_TYPE_DROPOUT_BITMASK 0, // dropout bitmask ACL_RT_RANDOM_NUM_FUNC_TYPE_UNIFORM_DIS, // uniform distribution ACL_RT_RANDOM_NUM_FUNC_TYPE_NORMAL_DIS, // normal distribution ACL_RT_RANDOM_NUM_FUNC_TYPE_TRUNCATED_NORMAL_DIS, // truncated normal distribution } aclrtRandomNumFuncType; typedef struct { aclrtRandomNumFuncType funcType; union { aclrtDropoutBitmaskInfo dropoutBitmaskInfo; aclrtUniformDisInfo uniformDisInfo; aclrtNormalDisInfo normalDisInfo; } paramInfo; } aclrtRandomNumFuncParaInfo;其中分布参数结构体acl_rt.h// dropout bitmask typedef struct { aclrtRandomParaInfo dropoutRation; } aclrtDropoutBitmaskInfo; // uniform distribution typedef struct { aclrtRandomParaInfo min; aclrtRandomParaInfo max; } aclrtUniformDisInfo; // normal distribution typedef struct { aclrtRandomParaInfo mean; aclrtRandomParaInfo stddev; } aclrtNormalDisInfo;4.5 参数封装aclrtRandomParaInfo分布参数、种子、数量统一用aclrtRandomParaInfo封装定义在 include/external/acl/acl_rt.htypedef struct { uint8_t isAddr; uint8_t valueOrAddr[8]; uint8_t size; uint8_t rsv[6]; } aclrtRandomParaInfo;isAddr0 表示valueOrAddr中直接存放立即值非 0 表示valueOrAddr中存放设备内存地址参数在 Device 内存中valueOrAddr[8]8 字节缓冲区既可作为立即值的存储区也可作为地址使用size参数字节数rsv[6]预留。样例中所有参数均采用立即值方式isAddr 0例如 1_random_num_task/main.cpp 中组装均匀分布参数*((float*)taskInfo.randomNumFuncParaInfo.paramInfo.uniformDisInfo.min.valueOrAddr) min; taskInfo.randomNumFuncParaInfo.paramInfo.uniformDisInfo.min.size sizeof(float); taskInfo.randomNumFuncParaInfo.paramInfo.uniformDisInfo.min.isAddr 0;4.6 支持的分布类型与数据类型1均匀分布Uniform Distribution函数类型标识ACL_RT_RANDOM_NUM_FUNC_TYPE_UNIFORM_DIS支持数据类型浮点ACL_FLOAT、ACL_FLOAT16、ACL_BF16整数ACL_INT32、ACL_INT64、ACL_UINT32、ACL_UINT64参数min最小值、max最大值2正态分布Normal Distribution函数类型标识ACL_RT_RANDOM_NUM_FUNC_TYPE_NORMAL_DIS支持数据类型ACL_FLOAT、ACL_FLOAT16、ACL_BF16参数mean均值、stddev标准差3截断正态分布Truncated Normal Distribution函数类型标识ACL_RT_RANDOM_NUM_FUNC_TYPE_TRUNCATED_NORMAL_DIS支持数据类型ACL_FLOAT、ACL_FLOAT16、ACL_BF16参数mean均值、stddev标准差4Dropout Bitmask 生成用于生成随机失活Dropout的 bit mask按比例生成函数类型标识ACL_RT_RANDOM_NUM_FUNC_TYPE_DROPOUT_BITMASK参数ratiodropout 比例支持数据类型ACL_FLOAT、ACL_FLOAT16、ACL_BF164.7 随机数算法算法特性Philox4_32_10支持所有分布类型Counter 为 128bit需要 16Byte 存储Philox4_32_10是一种基于计数器counter-based的伪随机数生成算法给定 seed 与 counter 即可确定性地产出随机序列。由于是 counter-based 算法同一个 seed 下改变 counter 可以并行生成互不重叠的随机序列这对大规模并行初始化非常重要同时它也要求用户为每个任务维护独立的 counter 状态见 4.8。4.8 内存要求根据文档与样例实现随机数任务需要三类 Device 内存Counter 内存固定 16 字节任意字节对齐地址即可。样例中先申请再清零constexpr size_t kCounterSize 16U; void* counterAddr nullptr; CHECK_ERROR(aclrtMalloc(counterAddr, kCounterSize, ACL_MEM_MALLOC_HUGE_FIRST)); CHECK_ERROR(aclrtMemset(counterAddr, kCounterSize, 0, kCounterSize));输出内存根据数据类型和随机数数量动态分配。样例申请num * sizeof(uint64_t)作为足够大的输出缓冲以容纳不同位宽的结果uint64_t num 128; size_t size num * sizeof(uint64_t); CHECK_ERROR(aclrtMalloc(devOutput, size, ACL_MEM_MALLOC_HUGE_FIRST));参数内存均值、标准差、范围等参数既可使用立即值内嵌在结构体的valueOrAddr中也可使用设备内存isAddr 1valueOrAddr存地址。样例统一使用立即值方式。4.9 完整代码流程解析随机数样例的完整骨架见 1_random_num_task/main.cpp其任务下发模式如下以正态分布 float 为例aclError NormalFloatAsync( float mean, float stddev, bool isTruncated, uint64_t seed, uint64_t num, void* counterDevAddr, void* devOutput, aclrtStream stream) { aclrtRandomNumTaskInfo taskInfo {}; taskInfo.dataType ACL_FLOAT; taskInfo.randomNumFuncParaInfo.funcType isTruncated ? ACL_RT_RANDOM_NUM_FUNC_TYPE_TRUNCATED_NORMAL_DIS : ACL_RT_RANDOM_NUM_FUNC_TYPE_NORMAL_DIS; taskInfo.randomParaAddr NULL; taskInfo.randomCounterAddr counterDevAddr; taskInfo.randomResultAddr devOutput; *((float*)taskInfo.randomNumFuncParaInfo.paramInfo.normalDisInfo.mean.valueOrAddr) mean; taskInfo.randomNumFuncParaInfo.paramInfo.normalDisInfo.mean.size sizeof(float); taskInfo.randomNumFuncParaInfo.paramInfo.normalDisInfo.mean.isAddr 0; *((float*)taskInfo.randomNumFuncParaInfo.paramInfo.normalDisInfo.stddev.valueOrAddr) stddev; taskInfo.randomNumFuncParaInfo.paramInfo.normalDisInfo.stddev.size sizeof(float); taskInfo.randomNumFuncParaInfo.paramInfo.normalDisInfo.stddev.isAddr 0; *((uint64_t*)taskInfo.randomSeed.valueOrAddr) seed; taskInfo.randomSeed.size sizeof(uint64_t); taskInfo.randomSeed.isAddr 0; *((uint64_t*)taskInfo.randomNum.valueOrAddr) num; taskInfo.randomNum.size sizeof(uint64_t); taskInfo.randomNum.isAddr 0; return aclrtRandomNumAsync(taskInfo, stream, NULL); }主程序依次生成五组随机数并打印前 5 个结果每组之间均执行同步 Stream → 拷回 Host → 打印INFO_LOG(Generate normal distribution random numbers, data type: float); float mean 3.0; float stddev 2.0; CHECK_ERROR(NormalFloatAsync(mean, stddev, false, seed, num, counterAddr, devOutput, stream)); CHECK_ERROR(aclrtSynchronizeStream(stream)); CHECK_ERROR(aclrtMemcpy(hostOutput.get(), size, devOutput, size, ACL_MEMCPY_DEVICE_TO_HOST));半精度类型FP16 / BF16在样例中直接以uint16_t表示并采用十六进制字面量构造数值例如0x3F80即 BF16 的 1.0、0x4000即 2.0打印时使用%#xBF16 meanBF16 0x3F80; // bf16(1.0) BF16 stddevBF16 0x4000; // bf16(2.0) CHECK_ERROR(NormalBF16Async(meanBF16, stddevBF16, true, seed, num, counterAddr, devOutput, stream));Dropout bitmask 生成与分布生成共用同一套接口区别仅在于funcType改为ACL_RT_RANDOM_NUM_FUNC_TYPE_DROPOUT_BITMASK并填充dropoutBitmaskInfo.dropoutRationfloat ration 0.4; CHECK_ERROR(DropoutBitmask(ration, seed, num, counterAddr, devOutput, stream));4.10 示例输出运行1_random_num_task后预期输出结构如下随机数值随种子不同而变化[INFO] Generate normal distribution random numbers, data type: float Random result[0] ... ... [INFO] Generate truncated normal distribution random numbers, data type: BF16 Random result[0] ... ... [INFO] Generate uniform distribution random numbers, data type: FP16 Random result[0] ... ... [INFO] Generate uniform distribution random numbers, data type: INT32 Random result[0] ... ... [INFO] Generate dropout bitmask, output data type: UINT8 Random result[0] ... [INFO] Sample run successfully.五、编译与运行5.1 前置环境已安装 CANN 软件包默认安装根目录为/usr/local/Ascend目标 Device 符合上文的产品支持情况样例代码位于仓库 example/2_advanced_features/built_in_task 目录下编译依赖头文件与acl_rt库CANN 安装包自带。5.2 编译运行步骤在任意一个样例目录下执行# ${install_root} 替换为 CANN 安装根目录默认安装在 /usr/local/Ascend source ${install_root}/cann/set_env.sh export ASCEND_INSTALL_PATH${install_root}/cann # 编译并运行 bash run.sh环境安装详情与运行详情也可参见 example/README.md。5.3 run.sh 脚本解析两个样例的 run.sh 完全一致核心流程为set -e # 获取 CANN 安装路径 _ASCEND_INSTALL_PATH$ASCEND_INSTALL_PATH source $_ASCEND_INSTALL_PATH/bin/setenv.bash # 在脚本所在目录创建 build 目录并进入 BUILD_DIR${SCRIPT_DIR}/build mkdir -p ${BUILD_DIR} cd ${BUILD_DIR} # 执行 cmake 配置 cmake .. -DASCEND_CANN_PACKAGE_PATH${_ASCEND_INSTALL_PATH} # 编译 make -j$(nproc) # 运行生成的可执行文件 ./build/main注意脚本依赖ASCEND_INSTALL_PATH环境变量这正是 5.2 中先export ASCEND_INSTALL_PATH${install_root}/cann的原因。5.4 CMake 配置要点样例的 CMakeLists.txt 结构简洁可供自有工程参考cmake_minimum_required(VERSION 3.16.0) project(Reduce_Sample) # 随机数样例为 Random_Num_Sample include_directories(${ASCEND_CANN_PACKAGE_PATH}/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../..) # 引入 example/utils.h link_directories(${ASCEND_CANN_PACKAGE_PATH}/lib64) add_executable(main main.cpp) target_compile_options(main PRIVATE -O2 -stdc17 -D_GLIBCXX_USE_CXX11_ABI0 -Wall -Werror ) target_link_libraries(main PRIVATE acl_rt )要点解读头文件搜索路径指向 CANN 安装包的include目录运行时库搜索路径指向lib64链接库为acl_rt-D_GLIBCXX_USE_CXX11_ABI0与 CANN 发行库的 ABI 保持一致避免链接期符号不匹配-Wall -Werror将警告升级为错误保证样例代码的严格性额外 include 到example顶层目录是为了使用 example/utils.h 中定义的CHECK_ERROR与INFO_LOG宏。六、接口契约的源码级印证上文涉及的核心类型与函数均可在公开头文件中找到权威定义可作为编写自有程序时的接口契约依据aclrtReduceAsync原型include/external/acl/acl_rt.haclrtReduceKind枚举SUM/MAX/MIN/EQUALinclude/external/acl/acl_rt.haclrtRandomNumAsync原型include/external/acl/acl_rt.haclrtRandomNumTaskInfo结构体include/external/acl/acl_rt.haclrtRandomNumFuncType枚举与分布参数联合体include/external/acl/acl_rt.haclrtRandomParaInfo参数封装include/external/acl/acl_rt.haclrtReduceAsync的 Host 侧实现位于 src/acl/aclrt_impl/memory.cpp可据此进一步追踪其任务封装与下发的底层调用链。七、注意事项与已知问题字节单位aclrtReduceAsync的count参数是字节数而非元素个数组装参数时务必乘以sizeof(type)Counter 唯一性随机数任务要求 Counter 为固定 16 字节、任意字节对齐的 Device 内存多次下发需保证 Counter 状态连续正确样例在首次使用前通过aclrtMemset清零从源码结构看不同任务/不同线程若复用同一 Counter需自行保证串行化或管理好状态半精度字面量FP16/BF16 参数需要以二进制位模式十六进制构造切勿直接赋浮点常量产品差异随机数任务在 Ascend 950PR/Ascend 950DT 上不支持Reduce 任务在三个产品系列上均支持异步语义两个 API 均为异步下发读取结果前必须调用aclrtSynchronizeStream等待任务完成否则可能读到未就绪的数据已知 issue截至当前仓库版本两个样例均标注暂无已知 issue。总体而言Reduce 与随机数生成是 Runtime 内建任务中最具代表性的两类能力前者展示了一个枚举一次调用即完成标准聚合计算后者展示了结构体聚合描述任务的复杂参数传递模式。掌握这两类任务的组装方式后迁移到其他内建任务时只需替换对应的 API 与参数结构流程骨架完全一致。【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表