news 2026/9/18 21:58:36

CANN Runtime 内置任务(Built-in Task)下发与执行实战:Reduce 归约与随机数生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN Runtime 内置任务(Built-in Task)下发与执行实战:Reduce 归约与随机数生成

CANN Runtime 内置任务(Built-in Task)下发与执行实战:Reduce 归约与随机数生成

【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime

导读

本文围绕 CANN Runtime 的"内置任务(built-in task)"能力展开,该能力让开发者无需自研算子、不加载 kernel 二进制即可在 Stream 上异步下发两类高频基础计算任务:Reduce 归约运算aclrtReduceAsync)与随机数生成aclrtRandomNumAsync,支持均匀/正态/截断正态分布与 Dropout Bitmask)。文中以 example/2_advanced_features/built_in_task 目录下的两个可编译运行示例为骨架,结合 include/external/acl/acl_rt.h 中的真实 API 声明与数据结构,完整讲解任务下发 API 的参数语义、任务信息结构体字段布局、内存要求、产品支持范围与构建运行步骤。读完本文,你可以直接在 CANN 环境中把这两个示例跑起来,并掌握如何在自己的业务代码中组织aclrtRandomNumTaskInfo、如何为随机数任务准备 16 字节 counter 内存,以及 Reduce 任务"就地前缀和"的输出规律。

一、什么是 Runtime 内置任务

CANN Runtime 的典型执行模型是"编译 kernel → 加载二进制 → 在 Stream 上下发执行"。但对于求和、极值比较、随机数生成这类基础性、通用性的计算,逐次走完整的 kernel 加载链路既不必要也不高效。Runtime 因此提供了内置任务(built-in task)机制:运行时内部预置对应实现,应用只需通过一个异步下发 API,把数据地址、参数与目标 Stream 一并交给 Runtime,任务即以异步、有序的方式进入 Stream 的任务队列执行。

从 example/2_advanced_features/built_in_task/README_en.md 的目录组织可以确认,当前仓库围绕该能力提供两个官方示例:

示例目录核心 API能力说明
0_reduce_taskaclrtReduceAsyncReduce 任务下发与结果读取
1_random_num_taskaclrtRandomNumAsync随机数任务下发,支持多种分布与数据类型

两者的共同特点是:不需要任何自定义算子源码,只依赖acl_rt一个动态库即可链接运行(见两个示例的 CMakeLists.txt 中target_link_libraries(main PRIVATE acl_rt)),非常适合作为理解"Stream 异步执行模型 + Runtime 内建计算"的入门与实战素材。

二、示例构建与运行(通用步骤)

两个示例的构建方式完全一致,均由 run.sh 一键完成 cmake 配置、编译并直接执行。文档给出的运行步骤为:

# 将 ${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 源码,其内部执行链路为:

  1. source $_ASCEND_INSTALL_PATH/bin/setenv.bash:加载 CANN 环境变量;
  2. 在示例目录下创建build/目录并进入;
  3. 执行cmake .. -DASCEND_CANN_PACKAGE_PATH=${_ASCEND_INSTALL_PATH}:将 CANN 安装路径传给 CMake;
  4. make -j$(nproc)编译生成build/main
  5. 直接运行./build/main

构建细节(来自 0_reduce_task/CMakeLists.txt)还包括:

  • 通过include_directories(${ASCEND_CANN_PACKAGE_PATH}/include)引入 CANN 头文件,代码中使用#include <acl/acl.h>即可获得全部接口;
  • 通过link_directories(${ASCEND_CANN_PACKAGE_PATH}/lib64)引入运行库目录,链接acl_rt
  • 编译选项为-O2 -std=c++17 -D_GLIBCXX_USE_CXX11_ABI=0 -Wall -Werror,即要求 C++17 并启用_GLIBCXX_USE_CXX11_ABI=0(与 CANN 预编译库的 ABI 保持一致,这是示例在各类发行版上可正常链接的关键之一)。

产品支持范围(以当前仓库文档为准):

功能Ascend 950PR/Ascend 950DTAtlas A3 训练/推理系列Atlas A2 训练/推理系列
Reduce 任务(aclrtReduceAsync支持支持支持
随机数任务(aclrtRandomNumAsync不支持支持支持

注意:随机数内置任务在 Ascend 950 系列上不受支持,部署前请务必核对目标产品型号。

三、示例 1:Reduce 归约任务

3.1 任务语义:就地前缀和(Automatic Sum)

Reduce(归约)是并行计算中的基础操作,用于对数组元素执行求和、求最大值等运算。本示例使用的是ACL_RT_MEMCPY_SDMA_AUTOMATIC_SUM(自动求和,即前缀和),调用一次aclrtReduceAsync即可让 Device 侧在 Stream 上异步完成计算。

示例的完整逻辑位于 0_reduce_task/main.cpp:输入{1.0, 2.0, 3.0, 4.0},输出为:

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] = 1+1,result[1] = 2+2,result[2] = 3+3,result[3] = 4+4),即典型的前缀和语义。因此使用该接口时,必须保证输出缓冲区与输入缓冲区均有效——示例中特意把输入数据先拷贝到devInputdevOutput两份内存(aclrtMemcpy两次,见 main.cpp),src 与 dst 是两个不同地址。

3.2 核心 API:aclrtReduceAsync

头文件 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数据大小(字节)
kind归约类型,见aclrtReduceKind
type数据类型(如ACL_FLOAT
stream任务下发的目标 Stream
reserve保留参数,必须为NULL

归约类型枚举完整定义位于 acl_rt.h:

typedef 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;

示例中以ACL_FLOAT(32 位浮点)为数据类型调用:

ret = aclrtReduceAsync( devOutput, devInput, size, // 输出、输入、字节数 ACL_RT_MEMCPY_SDMA_AUTOMATIC_SUM, // 归约类型:自动求和 ACL_FLOAT, // 数据类型 stream, NULL); CHECK_ERROR(ret);

3.3 完整执行流程

示例的程序流程就是一套标准的 AscendCL 生命周期,可作为所有内置任务示例的通用模板:

  1. 初始化aclInit(NULL)初始化 AscendCL 配置;
  2. 设备管理aclrtSetDevice(0)指定计算 Device;
  3. 创建 StreamaclrtCreateStream(&stream)
  4. 准备数据:Host 侧构造float hostInput[4],用aclrtMalloc(..., ACL_MEM_MALLOC_HUGE_FIRST)在 Device 申请输入/输出内存,再用aclrtMemcpy(..., ACL_MEMCPY_HOST_TO_DEVICE)上板;
  5. 下发任务:调用aclrtReduceAsync(异步,立即返回);
  6. 同步等待aclrtSynchronizeStream(stream)阻塞等待 Stream 上任务执行完毕;
  7. 回拷结果aclrtMemcpy(..., ACL_MEMCPY_DEVICE_TO_HOST)将结果拷回 Host 并打印;
  8. 释放资源aclrtFree释放 Device 内存、aclrtDestroyStream销毁 Stream、aclrtResetDeviceForce(0)强制复位计算 Device 并回收资源、aclFinalize()反初始化。

值得注意的是示例使用aclrtResetDeviceForce(强制复位)而非普通的aclrtResetDevice,用于确保 Device 资源被彻底回收,便于示例反复运行。

四、示例 2:随机数任务

4.1 核心 API:aclrtRandomNumAsync

头文件 include/external/acl/acl_rt.h 中的真实声明为:

ACL_FUNC_VISIBILITY aclError aclrtRandomNumAsync( const aclrtRandomNumTaskInfo* taskInfo, const aclrtStream stream, void* reserve);

其参数只有三个:任务信息结构体指针、目标 Stream、保留字段(须为NULL)。所有"生成什么分布、什么数据类型、范围/均值/方差、种子、数量、结果写到哪"的信息,全部封装在aclrtRandomNumTaskInfo中。

4.2 任务信息结构体:aclrtRandomNumTaskInfo

头文件 include/external/acl/acl_rt.h 中的真实定义(注意rsv为 8 字节,与示例文档中打印的结构略有差异,以头文件为准):

typedef struct { aclDataType dataType; aclrtRandomNumFuncParaInfo randomNumFuncParaInfo; void* randomParaAddr; void* randomResultAddr; void* randomCounterAddr; aclrtRandomParaInfo randomSeed; aclrtRandomParaInfo randomNum; uint8_t rsv[8]; } aclrtRandomNumTaskInfo;

各字段说明:

字段说明
dataType随机数输出数据类型,如ACL_FLOATACL_FLOAT16ACL_BF16ACL_INT32
randomNumFuncParaInfo函数类型与分布参数(详见下节)
randomParaAddr参数地址,示例中固定为NULL(参数直接内嵌在结构体中)
randomResultAddr随机数结果输出地址(Device 内存)
randomCounterAddr随机数状态 counter 地址(Device 内存,固定 16 字节)
randomSeed随机种子,aclrtRandomParaInfo类型
randomNum生成的随机数个数,aclrtRandomParaInfo类型
rsv[8]保留字段

其中的通用参数类型aclrtRandomParaInfo(acl_rt.h)定义了"立即数 or 设备内存地址"的双通道传参方式:

typedef struct { uint8_t isAddr; // 是否为地址:0 表示 valueOrAddr 直接存立即数值,1 表示存设备内存地址 uint8_t valueOrAddr[8]; // 8 字节,保存立即数值或设备内存地址 uint8_t size; // 参数大小(字节) uint8_t rsv[6]; } aclrtRandomParaInfo;

示例中所有参数均采用立即数方式isAddr = 0),例如随机种子与数量的填充:

*((uint64_t*)taskInfo.randomSeed.valueOrAddr) = seed; // 种子写进 valueOrAddr taskInfo.randomSeed.size = sizeof(uint64_t); taskInfo.randomSeed.isAddr = 0; *((uint64_t*)taskInfo.randomNum.valueOrAddr) = num; // 数量写进 valueOrAddr taskInfo.randomNum.size = sizeof(uint64_t); taskInfo.randomNum.isAddr = 0;

如需把参数放入 Device 内存,则把isAddr置 1、valueOrAddr存地址、size填参数字节数即可,文档中"参数可使用立即值或设备内存"的说法与此结构完全对应。

4.3 函数类型与分布参数

函数类型枚举aclrtRandomNumFuncType与参数联合体定义于 acl_rt.h:

typedef 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; // Dropout 比例 } aclrtDropoutBitmaskInfo; // uniform distribution typedef struct { aclrtRandomParaInfo min; // 最小值 aclrtRandomParaInfo max; // 最大值 } aclrtUniformDisInfo; // normal distribution typedef struct { aclrtRandomParaInfo mean; // 均值 aclrtRandomParaInfo stddev; // 标准差 } aclrtNormalDisInfo;

四种生成类型及支持的数据类型汇总如下:

生成类型函数类型标识参数支持的数据类型
均匀分布(Uniform)ACL_RT_RANDOM_NUM_FUNC_TYPE_UNIFORM_DISmin/max浮点:ACL_FLOATACL_FLOAT16ACL_BF16;整型:ACL_INT32ACL_INT64ACL_UINT32ACL_UINT64
正态分布(Normal)ACL_RT_RANDOM_NUM_FUNC_TYPE_NORMAL_DISmean/stddevACL_FLOATACL_FLOAT16ACL_BF16
截断正态分布(Truncated Normal)ACL_RT_RANDOM_NUM_FUNC_TYPE_TRUNCATED_NORMAL_DISmean/stddevACL_FLOATACL_FLOAT16ACL_BF16
Dropout Bitmask 生成ACL_RT_RANDOM_NUM_FUNC_TYPE_DROPOUT_BITMASKratio(比例)输出UINT8,ratio 支持ACL_FLOATACL_FLOAT16ACL_BF16

示例 1_random_num_task/main.cpp 以分组封装函数的形式演示了这些类型:UniformFloatAsyncUniformFloat16AsyncUniformBF16AsyncUniformInt32/Int64/Uint32/Uint64Async覆盖均匀分布的全部数据形态;NormalFloatAsync/NormalFloat16Async/NormalBF16Async通过isTruncated布尔参数在正态与截断正态之间切换(见 main.cpp 中isTruncated ? ACL_RT_RANDOM_NUM_FUNC_TYPE_TRUNCATED_NORMAL_DIS : ACL_RT_RANDOM_NUM_FUNC_TYPE_NORMAL_DIS);DropoutBitmask用于生成 Dropout 掩码。

4.4 随机数算法与内存要求

算法:当前仓库文档明确记录的算法为Philox4_32_10,其特点为支持所有分布类型,counter 为 128 bit,需 16 字节存储。

内存要求(来自 1_random_num_task/README_en.md):

  1. Counter 内存:固定 16 字节,任意字节对齐地址均可。示例中用constexpr size_t kCounterSize = 16U;申请并用aclrtMemset清零(main.cpp);
  2. 输出内存:根据数据类型与随机数个数动态计算大小,示例统一按num * sizeof(uint64_t)申请"足够大"的缓冲区以容纳 128 个各类型结果(main.cpp);
  3. 参数内存:均值、标准差、取值范围等参数可内嵌立即数(示例做法),也可使用设备内存地址。

关于 counter 需要注意:Philox 算法依赖 counter 状态推进来生成随机数,因此同一个 counter 内存在连续多次任务间会被改写,示例在 5 组任务中反复复用同一个counterAddr而未重置,恰好演示了"复用 counter 得到连续随机序列"的用法;若希望每次生成相同序列,则需要重置 counter 或换用不同种子。

4.5 运行结果示例

[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.

打印方式与数据类型对应:float 用%f,BF16/FP16 用%#x输出 16 位十六进制位模式(BF16(1.0) =0x3F80、FP16(1.0) =0x3C00、BF16(2.0) =0x4000,见 main.cpp),INT32 用%d,UINT8 用%u

五、内置任务在 Runtime 中的定位

从接口定义可见,内置任务与普通 kernel 下发(如aclrtLaunchKernel)位于同一层级的 acl_rt.h 头文件中,二者共享相同的Stream 异步执行模型:API 调用后立即返回,任务按序进入 Stream 队列,通过aclrtSynchronizeStream或事件机制同步。这一模型带来的工程收益包括:

  • 零自定义算子成本:Reduce、随机数这类基础运算不再需要编写和编译 kernel;
  • 异步流水线化:与 Stream 上的其他任务(拷贝、kernel、事件)天然有序衔接,可组合出完整的计算流水;
  • 可预测的内存模型:counter 固定 16 字节、参数支持立即数/地址两种传法,内存管理可控。

需要特别强调的是产品差异aclrtReduceAsync在 Ascend 950 系列与 Atlas A2/A3 系列均受支持,而aclrtRandomNumAsync在 Ascend 950 系列上不受支持(参见 1_random_num_task/README_en.md 的产品支持表)。在 Atlas A2/A3 上使用随机数任务时,建议像示例一样:先aclInit/aclrtSetDevice初始化环境,再创建 Stream、申请 counter 与输出内存,最后下发任务并通过aclrtSynchronizeStream收尾,整体流程可直接复用到生产代码中。

六、参考与延伸阅读

  • 目录总览:example/2_advanced_features/built_in_task/README_en.md
  • Reduce 示例文档:0_reduce_task/README_en.md,源码:0_reduce_task/main.cpp,构建脚本:0_reduce_task/run.sh
  • 随机数示例文档:1_random_num_task/README_en.md,源码:1_random_num_task/main.cpp,构建脚本:1_random_num_task/run.sh
  • API 与数据结构定义:include/external/acl/acl_rt.h(aclrtReduceAsync见 L4164-L4166,aclrtRandomNumAsync见 L5411-L5412,相关枚举与结构体见 L715-L821)
  • 示例运行总说明:example/README_en.md

【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 21:58:23

基于Matlab的多元线性回归逐步显著性检验程序实现

简介&#xff1a;一份基于研究生教材《数理统计》例4.4.1编写的多元线性回归及显著性检验Matlab程序文档&#xff0c;面向统计学习者、数据分析人员和需要快速完成回归建模的开发者。文档完整呈现了程序原理、数据存储格式与可直接运行的Matlab代码&#xff0c;并在教材原有回归…

作者头像 李华
网站建设 2026/9/18 21:56:31

Aspen Plus速率模型模拟MEA吸收CO2:从原理到实操

“大多数工艺模拟教程讲吸收塔时&#xff0c;都是用平衡级模型&#xff1a;给定塔板数、塔板效率&#xff0c;把一个复杂传质过程压成一个黑箱。但真正做过MEA吸收CO2工艺设计的人都知道&#xff0c;这套做法在涉及化学反应放热、组分高度非理想、气液两相负荷变化大的体系时&a…

作者头像 李华
网站建设 2026/9/18 21:55:49

正交试验设计原理与Python工程实践

简介&#xff1a;本资源是一份系统讲解正交试验设计原理与应用的PPT课件&#xff0c;面向高校统计学、工业工程、农学及生物医学等专业的师生&#xff0c;以及从事实验优化与质量改进的工程技术人员。课件深入浅出地阐释了正交试验的核心思想——以少量代表性试验替代全面试验&…

作者头像 李华
网站建设 2026/9/18 21:55:38

ArcKit加拿大联邦叠加层:FITA、PIA、AIA、OCAP等12命令合规体系

ArcKit加拿大联邦叠加层&#xff1a;FITA、PIA、AIA、OCAP等12命令合规体系 【免费下载链接】arc-kit The Enterprise Architecture Governance Harness — strategy, architecture, delivery, and assurance using AI coding assistants 项目地址: https://gitcode.com/GitH…

作者头像 李华
网站建设 2026/9/18 21:55:16

YuE 词生歌开源模型本地部署与完整歌曲生成实战

凌晨一点&#xff0c;我把一段自己写的中文词塞进 YuE&#xff0c;敲下回车&#xff0c;然后去泡了杯茶。二十多分钟后终端还在滚 token&#xff0c;音箱里传出第一句时我愣了一下——咬字是糊的&#xff0c;但旋律走向、和声色彩和我想的基本一致。那一刻我就知道&#xff0c;…

作者头像 李华
网站建设 2026/9/18 21:52:33

AI Agent驱动Unity编译与测试:工具链封装与踩坑实战

最近一直在折腾一件事&#xff1a;让 AI Agent 直接驱动 Unity 编辑器的编译与测试流程。起因很简单&#xff0c;项目组里大量重复性的“改代码—等编译—跑测试—看结果”操作&#xff0c;占用了不少开发时间&#xff0c;而且这些操作本质上是有明确规则的机械化流程&#xff…

作者头像 李华