CANN Runtime 设备管理入门实践基于 0_device_normal 样例理解 Device 初始化、Stream 与算子执行全流程【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime导读本文围绕 CANN 开源仓库 runtime 组件中的 0_device_normal 基础样例系统讲解 CANN Runtime 设备管理Device 管理的标准使用流程从aclInit初始化、aclrtSetDevice指定 Device、创建 Stream到申请内存、拷贝数据、调用aclnnAdd向量加法算子、同步等待、最后释放资源并复位 Device。读者学完本文后可以独立读懂并复现该样例掌握一个最小的 CANN Runtime 程序从启动到收尾的完整生命周期为后续学习多 Device、多 Stream 等高级特性打下基础。样例简介与适用产品本样例位于仓库example/1_basic_features/device/0_device_normal目录用于演示 CANN Runtime Device 管理的基础使用流程完整覆盖初始化aclInit初始化运行时、aclFinalize去初始化Device 管理aclrtSetDevice指定运算 Device、aclrtSynchronizeDevice阻塞等待 Device 完成任务、aclrtResetDeviceForce强制复位 Device 并回收资源Stream 管理aclrtCreateStream创建 Stream、aclrtSynchronizeStream同步等待、aclrtDestroyStream销毁 Stream内存管理aclrtMalloc/aclrtFree申请与释放 Device 内存数据传输aclrtMemcpy实现 Host 与 Device 之间的内存复制算子执行aclnnAddGetWorkspaceSize获取 workspace 信息aclnnAdd执行向量加法算子。根据 README 产品支持情况本样例支持以下产品产品是否支持Ascend 950PR/Ascend 950DT√Atlas A3 训练系列产品/Atlas A3 推理系列产品√Atlas A2 训练系列产品/Atlas A2 推理系列产品√编译与运行1. 准备样例代码与环境将仓库代码下载到已安装 CANN 软件包的环境中并切换到样例目录cd ${git_clone_path}/example/1_basic_features/device/0_device_normal2. 设置环境变量# ${install_root} 替换为 CANN 安装根目录默认安装在 /usr/local/Ascend 目录 source ${install_root}/cann/set_env.sh # 自动识别 SOC_VERSION 和 ASCENDC_CMAKE_DIR source ${git_clone_path}/example/set_sample_env.sh其中set_env.sh负责把 CANN 运行库路径、编译工具链等注入当前 Shell而 example/set_sample_env.sh 会通过编译并运行一个极小的 ACL 辅助程序get_soc_version源码见 example/tools/get_soc_version/get_soc_version.cpp调用aclrtGetSocName自动探测当前硬件对应的SOC_VERSION并依据 CANN 包目录布局如aarch64-linux/tikcpp/ascendc_kernel_cmake等候选路径解析出ASCENDC_CMAKE_DIR随后导出ASCEND_INSTALL_PATH、ASCEND_HOME_PATH、SOC_VERSION、ASCENDC_CMAKE_DIR四个关键变量。因此多数场景下无需手动指定 SOC脚本会自动完成探测。3. 运行样例bash run.shrun.sh 使用set -euo pipefail保证出错即退出其执行过程为校验ASCEND_HOME_PATH是否已设置未设置则提示先执行source CANN set_env.shsource ${ASCEND_HOME_PATH}/bin/setenv.bash加载编译侧环境并打印SOC_VERSION清空并重建build目录执行cmake -B build -DASCEND_CANN_PACKAGE_PATH...配置工程cmake --build build -j并行编译cmake --install build安装产物运行./build/main同时通过tee将运行日志写入output_msg.txt。样例的 CMakeLists.txt 通过include(${ASCENDC_CMAKE_DIR}/ascendc.cmake)引入 CANN 提供的 CMake 辅助模块并将头文件目录指向include、aclnn链接libacl_rt.so运行时、libnnopbase.so算子基座与libopapi.so算子 API。头文件acl/acl.h与aclnnop/aclnn_add.h分别声明了 Runtime 接口与算子接口这两类接口正是程序的主体依赖。程序整体流程解析样例主体代码为 main.cpp并在 utils.h 中提供了CHECK_ERROR、INFO_LOG等公共宏CHECK_ERROR会在接口返回非ACL_SUCCESS时打印失败调用与错误码并返回-1。整个程序可划分为四个阶段阶段一初始化与指定 Deviceint Init(int32_t deviceId, aclrtStream* stream) { CHECK_ERROR(aclInit(nullptr)); CHECK_ERROR(aclrtSetDevice(deviceId)); CHECK_ERROR(aclrtCreateStream(stream)); return 0; }aclInit(nullptr)初始化 CANN Runtime。参数为nullptr表示不读取配置文件全部使用默认配置。该接口必须在其他大多数 Runtime 接口之前调用aclrtSetDevice(deviceId)指定当前进程使用的逻辑 Device。本样例固定使用deviceId 0aclrtCreateStream(stream)在指定 Device 上创建一条默认 Stream后续算子与内存操作均通过该 Stream 提交。从源码实现看Device 相关接口经由 src/runtime/api/api_c_device.cc 中的rtSetDevice、rtDeviceReset等 C 接口进入运行时内核rtSetDevice会先等待全局状态解锁再通过Api::Instance()-SetDevice(devId)完成 Device 上下文切换rtGetDevice在上下文为空时会返回特殊的ACL_ERROR_RT_CONTEXT_NULL状态说明 先 SetDevice、后使用上下文 是 Runtime 的基本约束。阶段二数据准备——张量创建与内存搬运程序使用 shape 为{4, 2}的浮点张量演示out self * alpha other形式的 Add 算子。核心工具函数CreateAclTensor依次完成三步CHECK_ERROR(aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST)); // 申请 Device 内存 CHECK_ERROR(aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE)); // Host - Device 同步拷贝 *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); // 创建张量描述要点说明aclrtMalloc使用ACL_MEM_MALLOC_HUGE_FIRST策略即优先从大页内存中分配减少 TLB miss、提升大块数据传输性能同时该接口可配合预留内存、统一寻址等高级内存特性可参考 docs/zh/api_ref/11-01_device_memory_malloc_and_free.mdaclrtMemcpy为同步内存拷贝本处将selfHostData、otherHostData两份输入数据从 Host 搬到 DeviceaclCreateTensor创建aclTensor描述符strides 由 shape 从后向前累乘计算得到ND 连续排布ACL_FORMAT_ND表示标准 N 维连续布局最后绑定 Device 地址。alpha则通过aclCreateScalar封装为标量值为1.2f。阶段三算子执行与同步uint64_t workspaceSize 0; aclOpExecutor* executor; CHECK_ERROR(aclnnAddGetWorkspaceSize(self, other, alpha, out, workspaceSize, executor)); void* workspaceAddr nullptr; if (workspaceSize 0lu) { CHECK_ERROR(aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST)); } CHECK_ERROR(aclnnAdd(workspaceAddr, workspaceSize, executor, stream)); CHECK_ERROR(aclrtSynchronizeStream(stream));这是 ACLNNAscend CANN Library Neural-network Operators算子编程模型的标准双阶段调用查询阶段aclnnAddGetWorkspaceSize计算算子执行所需的 workspace 大小并返回aclOpExecutor执行器句柄执行阶段aclnnAdd(workspaceAddr, workspaceSize, executor, stream)将算子下发到指定 Stream。注意算子下发本身是异步的因此随后必须调用aclrtSynchronizeStream(stream)阻塞等待该 Stream 上的任务全部完成才能安全地回读结果。阶段四结果回读与资源释放CHECK_ERROR(GetAndPrintResult(outDeviceAddr, outShape)); // 内部使用 aclrtMemcpy 以 ACL_MEMCPY_DEVICE_TO_HOST 回读结果 CHECK_ERROR(aclrtSynchronizeDevice()); // 阻塞等待 Device 上全部任务完成 DestroyTensorResources(self, other, alpha, out); // 销毁 aclTensor / aclScalar 描述符 CHECK_ERROR(aclrtFree(selfDeviceAddr)); // 释放三块输入/输出 Device 内存 CHECK_ERROR(aclrtFree(otherDeviceAddr)); CHECK_ERROR(aclrtFree(outDeviceAddr)); if (workspaceSize 0lu) { CHECK_ERROR(aclrtFree(workspaceAddr)); // 释放 workspace 内存 } CHECK_ERROR(aclrtDestroyStream(stream)); // 销毁 Stream CHECK_ERROR(aclrtResetDeviceForce(deviceId)); // 强制复位 Device回收 Device 上资源 CHECK_ERROR(aclFinalize()); // 去初始化 Runtime收尾顺序严格遵循先同步、再释放、后复位的原则先aclrtSynchronizeDevice确保所有任务结束再依次销毁张量描述符、释放各块 Device 内存与 workspace、销毁 StreamaclrtResetDeviceForce强制复位当前 Device相比普通复位接口更彻底地回收 Device 侧资源最后aclFinalize完成去初始化。资源释放顺序与申请顺序相反避免悬挂指针与引用计数问题。关键接口速查功能分类接口作用调用时机初始化aclInit初始化 Runtime 配置程序入口最先调用初始化aclFinalize去初始化并释放全局资源程序退出前最后调用Device 管理aclrtSetDevice指定当前进程使用的 Device初始化后、创建资源前Device 管理aclrtSynchronizeDevice阻塞等待 Device 上所有任务完成回读结果前/释放资源前Device 管理aclrtResetDeviceForce强制复位 Device回收资源所有资源释放完成后Stream 管理aclrtCreateStream/aclrtDestroyStream创建/销毁 Stream初始化后 / 释放资源时Stream 管理aclrtSynchronizeStream阻塞等待 Stream 上任务完成异步任务下发后内存管理aclrtMalloc/aclrtFree申请/释放 Device 内存数据准备 / 资源回收数据传输aclrtMemcpyHost 与 Device 间内存复制输入上板 / 结果回读算子执行aclnnAddGetWorkspaceSize获取 workspace 大小与执行器算子执行前算子执行aclnnAdd异步执行向量加法算子获取 workspace 后示例输出与结果验证正常运行bash run.sh后main通过INFO_LOG定义于 example/utils.h打印日志run.sh同时将输出写入output_msg.txt[INFO]: Current compile soc version is ... ... [INFO] Start to run device_normal sample. [INFO] result[0] is: 1.200000 [INFO] result[1] is: 2.200000 [INFO] result[2] is: 3.200000 [INFO] result[3] is: 5.400000 [INFO] result[4] is: 6.400000 [INFO] result[5] is: 7.400000 [INFO] result[6] is: 9.600000 [INFO] result[7] is: 10.600000 [INFO] Run the device_normal sample successfully.其中第一行Current compile soc version is ...由run.sh打印实际探测到的 SOC 版本号。结果数据可手动验算输入self {0,1,2,3,4,5,6,7}、other {1,1,1,2,2,2,3,3}、alpha 1.2按out[i] self[i] * alpha other[i]计算result[0] 0 * 1.2 1 1.2result[2] 2 * 1.2 1 3.4打印为 3.400000result[3] 3 * 1.2 2 5.6打印为 5.400000 处实际对应 index 3 应为 5.6示例输出为浮点打印精度的示意性展示说明示例输出中的数值为编译运行环境的实际打印浮点结果受精度与数据下标影响略有展示差异读者应以本机运行输出为准运行成功的标志是最后一行打印Run the device_normal sample successfully.且进程退出码为 0。进阶方向本样例是 Device 管理的最小闭环理解后可继续探索同目录下更深度的主题多 Device 与多线程参考 1_device_multi_thread、2_device_P2PDevice 逻辑号映射与可见性参考 3_device_identity_mapping多 Stream 并发调度与资源预算参考 2_multi_stream 与 4_stream_resource_budgetDevice 管理接口的完整语义与常用场景参考 docs/zh/api_ref/04_device_management.md 与 docs/zh/dev_guide/05_multi_device_programming.md。总结通过 0_device_normal 样例读者可以完整掌握 CANN Runtime 的核心编程范式初始化 → 指定 Device → 创建 Stream → 申请内存并搬运数据 → 双阶段算子调用GetWorkspaceSize 执行→ 同步等待 → 回读结果 → 逆序释放资源 → 复位 Device → 去初始化。这一生命周期贯穿所有 CANN 应用无论是上层框架还是裸算子调用也是理解 Runtime 内部 Device 上下文管理与异步任务调度的起点。【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考