CANN Runtime 随机数内置任务实战:使用 aclrtRandomNumAsync 生成随机数分布

CANN Runtime 随机数内置任务实战:使用 aclrtRandomNumAsync 生成随机数分布 CANN Runtime 随机数内置任务实战使用 aclrtRandomNumAsync 生成随机数分布【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime本篇技术指南以 CANN Runtime 样例example/2_advanced_features/built_in_task/1_random_num_task为主体讲解如何通过 Runtime 内置系统任务接口aclrtRandomNumAsync在 Device 侧异步生成均匀分布、正态分布、截断正态分布随机数以及 Dropout 位掩码。读完本文你将掌握aclrtRandomNumTaskInfo任务结构的完整填写方式立即值与 Device 地址两种参数传递机制、Philox4_32_10 算法对 16 字节 counter 内存的要求以及如何把该任务与 Stream、Device 内存管理组合成可运行的随机数生成流程。样例概述与产品支持情况该样例演示 CANN Runtime 的aclrtRandomNumAsyncAPI 如何生成随机数支持多种随机数分布类型与多种数据类型用于满足训练推理场景中随机数生成的需求如初始化权重、Dropout、随机采样等。样例位于 1_random_num_task 目录属于 example 目录中高级特性样例的一部分示例总览 中将 Reduce 和随机数生成归为内置系统任务。产品是否支持Ascend 950PR/Ascend 950DT不支持Atlas A3 训练系列产品/Atlas A3 推理系列产品支持Atlas A2 训练系列产品/Atlas A2 推理系列产品支持该产品的支持情况与 API 参考文档 中aclrtRandomNumAsync一节的产品支持列表一致Atlas 310 系列、IPV350 等均不支持使用本接口前应先确认硬件平台。构建与运行环境准备样例的构建运行依赖 CANN 安装包官方说明README_en.md给出的环境准备方式为# 将 ${install_root} 替换为 CANN 安装根目录默认安装路径为 /usr/local/Ascend source ${install_root}/cann/set_env.sh export ASCEND_INSTALL_PATH${install_root}/cann然后执行bash run.sh即可一键构建并运行。构建脚本 run.sh 逐段解读实际的 run.sh 脚本完整流程为通过source $_ASCEND_INSTALL_PATH/bin/setenv.bash加载 CANN 环境变量脚本内部以ASCEND_INSTALL_PATH环境变量定位 CANN 包路径因此上面必须先 export 该变量在样例目录下创建build/构建目录并清理旧产物执行cmake .. -DASCEND_CANN_PACKAGE_PATH${_ASCEND_INSTALL_PATH}配置工程make -j$(nproc)并行编译直接执行产物./build/main。脚本以set -e开启失败即停任意一步出错会立即退出并暴露错误。CMake 配置要点CMakeLists.txt 的关键配置最低 CMake 版本 3.16.0工程名Random_Num_Sample头文件搜索路径包含${ASCEND_CANN_PACKAGE_PATH}/include与样例总目录../../..用于定位公共头文件 utils.h库路径为${ASCEND_CANN_PACKAGE_PATH}/lib64最终只链接一个库acl_rt编译选项-O2 -stdc17 -D_GLIBCXX_USE_CXX11_ABI0 -Wall -Werror即要求零警告编译。这说明随机数任务属于 Runtime 核心接口应用侧只需链接acl_rt即可调用无需引入算子或模型相关库。核心 APIaclrtRandomNumAsync函数原型与参数接口声明见 acl_rt.h 头文件aclError aclrtRandomNumAsync( const aclrtRandomNumTaskInfo* taskInfo, // 随机数任务信息 aclrtStream stream, // 执行任务的 Stream void* reserve // 预留参数当前固定传 NULL );如 API 参考文档 所述该接口下发并执行随机数生成任务是异步接口任务在指定 Stream 上按序执行返回 0 表示成功其他值表示失败。样例中该接口与以下接口配合使用初始化aclInit初始化 AscendCLaclFinalize反初始化设备管理aclrtSetDevice指定计算 DeviceaclrtResetDeviceForce强制重置 Device 并回收资源流管理aclrtCreateStream创建 StreamaclrtSynchronizeStream阻塞等待 Stream 上任务执行完成内存管理aclrtMalloc分配 Device 输出缓冲区与随机数 counter 缓冲区aclrtFree释放 Device 内存数据搬运aclrtMemcpy将生成的随机数拷回 Host 侧检查。任务信息结构 aclrtRandomNumTaskInfo任务的全部参数收敛在一个结构体中其真实定义见 acl_rt.h 结构体定义typedef struct { aclDataType dataType; // 随机数输出数据类型 aclrtRandomNumFuncParaInfo randomNumFuncParaInfo; // 分布类型及分布参数 void* randomParaAddr; // 随机数附加参数地址 void* randomResultAddr; // 随机数结果 Device 内存地址 void* randomCounterAddr; // 随机数计数器 Device 内存地址 aclrtRandomParaInfo randomSeed; // 种子 aclrtRandomParaInfo randomNum; // 随机数个数 uint8_t rsv[8]; // 保留字段 } aclrtRandomNumTaskInfo; typedef struct { aclrtRandomNumFuncType funcType; // 分布类型 union { aclrtDropoutBitmaskInfo dropoutBitmaskInfo; // { dropoutRation } aclrtUniformDisInfo uniformDisInfo; // { min, max } aclrtNormalDisInfo normalDisInfo; // { mean, stddev } } paramInfo; } aclrtRandomNumFuncParaInfo;其中分布参数通过 union 承载三个分支分别对应 Dropout 位掩码、均匀分布、截断正态分布填写时只使用与funcType匹配的那个分支即可。参数传递机制立即值 vs Device 地址所有标量参数min/max、mean/stddev、dropout 比例、seed、num都使用统一的参数描述结构aclrtRandomParaInfotypedef struct { uint8_t isAddr; // 0: valueOrAddr 为立即值; 1: valueOrAddr 为 Device 内存地址 uint8_t valueOrAddr[8]; // 最多承载 8 字节的立即值或地址 uint8_t size; // 参数实际字节数 uint8_t rsv[6]; } aclrtRandomParaInfo;这一机制带来两个实践结论参数既可以作为立即值内嵌在任务描述中isAddr 0样例采用此方式也可以指向一块 Device 内存isAddr 1后者适合参数本身较大或需要被多个任务共享动态更新的场景立即值上限为 8 字节因此 seed、num 这类uint64_t参数恰好 8 字节必须用sizeof如实填写size字段样例中每个参数都显式写size sizeof(...)这正是该 API 的正确用法。一个值得注意的细节由于三个分布参数结构共享同一个 union在样例的 Dropout 分支中作者填写dropoutBitmaskInfo.dropoutRation的立即值后转而通过normalDisInfo.mean.size/isAddr设置字节数与标志位——两者在内存上完全重叠效果等价。阅读或编写该样例时需理解这一 union 别名关系。四种随机数生成分布类型分布类型由枚举aclrtRandomNumFuncType标识取值见 acl_rt.htypedef enum { ACL_RT_RANDOM_NUM_FUNC_TYPE_DROPOUT_BITMASK 0, // dropout bitmask ACL_RT_RANDOM_NUM_FUNC_TYPE_UNIFORM_DIS, // 均匀分布 ACL_RT_RANDOM_NUM_FUNC_TYPE_NORMAL_DIS, // 正态分布 ACL_RT_RANDOM_NUM_FUNC_TYPE_TRUNCATED_NORMAL_DIS, // 截断正态分布 } aclrtRandomNumFuncType;1. 均匀分布UNIFORM_DIS支持数据类型浮点ACL_FLOAT、ACL_FLOAT16、ACL_BF16整型ACL_INT32、ACL_INT64、ACL_UINT32、ACL_UINT64分布参数min最小值、max最大值均按dataType对应类型填写。2. 正态分布NORMAL_DIS支持数据类型ACL_FLOAT、ACL_FLOAT16、ACL_BF16分布参数mean均值、stddev标准差。3. 截断正态分布TRUNCATED_NORMAL_DIS支持数据类型ACL_FLOAT、ACL_FLOAT16、ACL_BF16分布参数mean均值、stddev标准差与正态分布共用同一组参数结构仅funcType不同。样例中通过布尔参数isTruncated在两者间切换这一实现方式见 main.cpp 中 NormalFloatAsynctaskInfo.randomNumFuncParaInfo.funcType isTruncated ? ACL_RT_RANDOM_NUM_FUNC_TYPE_TRUNCATED_NORMAL_DIS : ACL_RT_RANDOM_NUM_FUNC_TYPE_NORMAL_DIS;4. Dropout 位掩码DROPOUT_BITMASK用于按概率ratio生成随机 Dropout 的位掩码输出为位图形式。分布参数dropoutRationDropout 比例输出类型按位掩码组织样例中以ACL_FLOAT作为dataType下发、结果按UINT8位图读取。随机数算法算法特性Philox4_32_10支持所有分布类型counter 为 128bit需要 16Byte 存储内存要求Counter 内存固定 16 字节要求字节对齐地址即可。样例中以constexpr size_t kCounterSize 16U分配并用aclrtMemset清零初始化void* counterAddr nullptr; CHECK_ERROR(aclrtMalloc(counterAddr, kCounterSize, ACL_MEM_MALLOC_HUGE_FIRST)); CHECK_ERROR(aclrtMemset(counterAddr, kCounterSize, 0, kCounterSize));输出内存按数据类型与随机数个数动态分配。样例申请num * sizeof(uint64_t)大小的 Device 缓冲区devOutput供各任务复用参数内存均值、标准差、范围等参数既可用立即值isAddr 0也可指向 Device 内存isAddr 1。样例代码剖析main.cppmain.cpp 的整体结构是准备资源 → 依次下发 5 类随机数任务 → 每类任务同步 Stream → 拷回结果 → 打印前 5 个值 → 释放资源。资源准备CHECK_ERROR(aclInit(NULL)); CHECK_ERROR(aclrtSetDevice(0)); aclrtStream stream; CHECK_ERROR(aclrtCreateStream(stream)); uint64_t seed 0; uint64_t num 128; size_t size num * sizeof(uint64_t); void* devOutput NULL; CHECK_ERROR(aclrtMalloc(devOutput, size, ACL_MEM_MALLOC_HUGE_FIRST));注意三点seed与num在样例中保持一致5 次任务共用同一 seed 与 counter 缓冲从源码结构看counter 状态在多次任务间持续演进这正是 Philox 类计数器算法支持跨任务连续取流的体现devOutput使用ACL_MEM_MALLOC_HUGE_FIRST策略分配兼顾大页内存性能。典型任务下发模式以 FP16 均匀分布为例每个分布封装成一个辅助函数模式统一aclError UniformFloat16Async( Float16 min, Float16 max, uint64_t seed, uint64_t num, void* counterDevAddr, void* devOutput, aclrtStream stream) { aclrtRandomNumTaskInfo taskInfo; taskInfo.dataType ACL_FLOAT16; taskInfo.randomNumFuncParaInfo.funcType ACL_RT_RANDOM_NUM_FUNC_TYPE_UNIFORM_DIS; taskInfo.randomParaAddr NULL; taskInfo.randomCounterAddr counterDevAddr; taskInfo.randomResultAddr devOutput; // 立即值方式填写 min/maxisAddr0 *((Float16*)taskInfo.randomNumFuncParaInfo.paramInfo.uniformDisInfo.min.valueOrAddr) min; taskInfo.randomNumFuncParaInfo.paramInfo.uniformDisInfo.min.size sizeof(Float16); taskInfo.randomNumFuncParaInfo.paramInfo.uniformDisInfo.min.isAddr 0; // ... max 同理 *((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); }主流程对每类任务执行下发 → 同步 → 拷回 → 打印的标准闭环INFO_LOG(Generate uniform distribution random numbers, data type: FP16); Float16 minFP16 0x3C00; // fp16(1.0) Float16 maxFP16 0x4000; // fp16(2.0) CHECK_ERROR(UniformFloat16Async(minFP16, maxFP16, seed, num, counterAddr, devOutput, stream)); CHECK_ERROR(aclrtSynchronizeStream(stream)); CHECK_ERROR(aclrtMemcpy(hostOutput.get(), size, devOutput, size, ACL_MEMCPY_DEVICE_TO_HOST)); Float16* fp16Data (Float16*)hostOutput.get(); for (int i 0; i 5; i) { printf(Random result[%d] %#x\n, i, fp16Data[i]); }样例依次覆盖 5 个场景顺序分布数据类型参数1正态分布floatmean3.0, stddev2.02截断正态分布BF16mean1.0(0x3F80), stddev2.0(0x4000)3均匀分布FP16min1.0(0x3C00), max2.0(0x4000)4均匀分布INT32min1, max1005Dropout 位掩码输出 UINT8 位图ratio0.4浮点低精度类型FP16/BF16的边界值以 IEEE 半精度/Brain Float 位模式常量直接书写如0x3C00即 fp16 的 1.0这提示使用者填写半精度立即值时应使用对应格式的二进制表示。预期输出样例运行后输出类似...为运行时的具体随机值[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.资源清理与退出样例退出路径完整释放了所有资源可作为标准范式aclrtFree(devOutput); aclrtFree(counterAddr); aclrtDestroyStream(stream); aclrtResetDeviceForce(0); aclFinalize();总结本样例展示了 CANN Runtime 将随机数生成作为内置系统任务抽象的完整用法通过aclrtRandomNumTaskInfo一个结构体描述分布类型、分布参数、seed、个数、结果与 counter 内存调用aclrtRandomNumAsync即可在 Stream 上下文中异步完成生成无需用户自行编写随机数 Kernel。关键约束可归纳为counter 固定 16 字节且需初始化、输出内存按num * 元素大小预留、参数支持立即值/Device 地址双模式、分布类型与数据类型需匹配支持矩阵。工程接入时按链接acl_rt→ 填写 taskInfo → 下发并同步 Stream三步即可完成同时注意确认目标硬件在产品支持列表中A2/A3 系列支持950 系列等不支持。延伸阅读样例源码main.cpp、run.sh、CMakeLists.txt接口定义aclrtRandomNumAsync 声明、随机数任务相关结构体API 参考执行控制接口中文样例说明README.md【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考