CANN Runtime 多线程 Device 管理实战:样例 1_device_multi_thread 从编译运行到接口原理
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
本篇技术指南围绕 CANN Runtime 仓库中的样例 1_device_multi_thread 展开,讲解多线程场景下 Device 管理的完整流程:主线程如何指定 Device 并设置 Device 资源限制,工作线程如何查询 Device 属性、下发核函数任务并最终释放资源。读完本文,你可以掌握aclrtSetDevice、aclrtSetDeviceResLimit等 Device 管理接口的正确调用方式,以及该样例的编译运行方法和预期输出,用于在自己的多核函数、多线程推理/训练框架中做 Device 资源隔离与属性查询。
样例概述与产品支持
该样例演示多线程场景下的 Device 管理流程(Multi-thread Device Management Flow),其核心行为是:
- 主线程:指定用于计算的 Device(
aclrtSetDevice(0)),并设置 Device 资源限制(aclrtSetDeviceResLimit,将 Vector Core 数量限制为 1); - 工作线程:创建 Stream 后,查询 Device 数量、运行模式、资源限制等信息,然后下发核函数任务(向量加法),并同步等待任务完成、校验结果;
- 收尾:工作线程释放 Device、Host 内存与 Stream 资源,主线程调用
aclFinalize完成去初始化。
样例支持的产品如下:
| 产品 | 是否支持 |
|---|---|
| Ascend 950PR/Ascend 950DT | 支持 |
| Atlas A3 训练系列产品/Atlas A3 推理系列产品 | 支持 |
| Atlas A2 训练系列产品/Atlas A2 推理系列产品 | 支持 |
样例目录结构与关键文件:
- main.cpp:样例主逻辑,包含主线程与工作线程的全部 Runtime API 调用;
- run.sh:一键编译运行脚本,内部完成 CMake 配置、构建与执行;
- CMakeLists.txt:CMake 工程文件,负责编译 AscendC 核函数库与主程序。
编译与运行
运行前需要一台已安装 CANN 软件、且能访问昇腾 AI 处理器的环境。
第 1 步:下载样例代码到安装 CANN 的环境,切换到样例目录。
cd ${git_clone_path}/example/1_basic_features/device/1_device_multi_thread第 2 步:设置环境变量。
# ${install_root} 替换为 CANN 安装根目录,默认安装在 /usr/local/Ascend 目录 source ${install_root}/cann/set_env.sh # 设置 SOC_VERSION 和 ASCENDC_CMAKE_DIR # -SOC_VERSION: 昇腾 AI 处理器型号,如 Ascend910_9362、Ascend910B2 等 # -ASCENDC_CMAKE_DIR: 样例涉及调用 AscendC 算子,需配置 AscendC 编译器 ascendc.cmake 路径, # 例如 /usr/local/Ascend/cann/x86_64-linux/tikcpp/ascendc_kernel_cmake source ${git_clone_path}/example/set_sample_env.sh其中 set_sample_env.sh 会协助用户自动识别SOC_VERSION与ASCENDC_CMAKE_DIR:从源码结构看,它首先通过example/tools/get_soc_version下编译出的小型辅助二进制调用aclrtGetSocName取得处理器版本字符串,再按主机架构(x86_64/aarch64)在 CANN 安装目录下探测tikcpp/ascendc_kernel_cmake目录并导出SOC_VERSION、ASCENDC_CMAKE_DIR、ASCEND_HOME_PATH等变量,免去手工填写型号。
第 3 步:执行运行脚本。
bash run.shrun.sh 的关键逻辑是:先检查ASCEND_HOME_PATH是否已设置(未设置则提示先 source CANN 的set_env.sh),然后依次执行cmake -B build -DASCEND_CANN_PACKAGE_PATH=...、cmake --build build -j、cmake --install build,最后运行./build/main并将输出同时写入output_msg.txt。
CMakeLists.txt 中,SOC_VERSION与ASCENDC_CMAKE_DIR直接取自环境变量,工程要求 CMake 最低版本 3.16.0,并包含 CANN 头文件目录与库目录;ascendc_library负责编译 kernel_add.cpp 生成静态核函数库kernels,主程序main链接kernels与pthread(多线程所需)。
多线程执行流程与源码走读
样例代码 main.cpp 分为三个部分:
主线程:指定 Device 并设置资源限制
main函数中的调用顺序(对应 main.cpp):
CHECK_ERROR(aclInit(nullptr)); // 初始化 CANN Runtime CHECK_ERROR(aclrtSetDevice(0)); // 指定 Device 0 用于计算 CHECK_ERROR(aclrtSetDeviceResLimit(0, ACL_RT_DEV_RES_VECTOR_CORE, 1)); // 限制 Vector Core 数量为 1 std::thread t1(RunThread); // 启动工作线程 t1.join(); CHECK_ERROR(aclFinalize()); // 去初始化几个关键点:
aclrtSetDevice必须在aclInit之后、任何 Device 相关操作之前调用,它把当前线程/进程绑定到 Device 0。样例中主线程先设置一次,工作线程内部(Init函数)再次调用aclrtSetDevice(0),保证工作线程也持有了 Device 上下文——这是多线程场景的典型做法:每个需要下发任务的线程都应确认 Device 已指定。aclrtSetDeviceResLimit(0, ACL_RT_DEV_RES_VECTOR_CORE, 1)表示为当前进程设置 Device 0 的 Vector Core 数量上限为 1。资源类型来自头文件 acl_rt.h 中的枚举:
typedef enum { ACL_RT_DEV_RES_CUBE_CORE = 0, // Cube Core 数量限制 ACL_RT_DEV_RES_VECTOR_CORE, // Vector Core 数量限制 } aclrtDevResLimitType;接口声明见 acl_rt.h:
ACL_FUNC_VISIBILITY aclError aclrtSetDeviceResLimit(int32_t deviceId, aclrtDevResLimitType type, uint32_t value);该接口的作用是限制当前进程可使用的 AI 处理器计算核数量,常用于多进程共享一颗昇腾处理器时做资源配额(如把 24 个 Vector Core 中的 1 个分给某个进程)。设置后的效果会在后文工作线程的aclrtGetDeviceResLimit查询中验证,输出为VECTOR_CORE 1。 3.资源限制的生命周期:按 API 参考 的说明,设置后进程内对该 Device 的查询将返回该限制值;aclrtResetDeviceResLimit可将限制恢复为硬件默认配置。样例在工作线程末尾(aclrtFree之后、销毁 Stream 之前)调用了aclrtResetDeviceResLimit(0),演示了完整的“设置 → 查询使用 → 重置”闭环。
工作线程:查询 Device 信息
工作线程RunThread首先通过Init函数再次aclrtSetDevice(0)并aclrtCreateStream创建 Stream,随后调用DeviceInfoQuery()依次查询(对应 main.cpp):
| 接口 | 查询内容 | 说明 |
|---|---|---|
aclrtGetSocName() | 昇腾 AI 处理器型号 | 如Ascend910_9362 |
aclrtGetDeviceCount(&deviceCount) | 可用 Device 数量 | Device ID 取值范围为[0, deviceCount-1] |
aclrtQueryDeviceStatus(0, &deviceStatus) | Device 状态 | 正常状态下返回 0 |
aclrtGetRunMode(&runMode) | 运行模式 | 0 为ACL_DEVICE(真机模式),1 为ACL_HOST(Host 仿真模式) |
aclrtGetDeviceUtilizationRate(0, &utilizationInfo) | 模块利用率 | 返回 Cube/Vector/AI CPU/内存四类利用率,utilizationExtend置空即可 |
aclrtDeviceGetStreamPriorityRange(&least, &greatest) | Stream 优先级范围 | 硬件支持的 Stream 优先级最小/最大值 |
aclrtGetDeviceInfo(0, ACL_DEV_ATTR_VECTOR_CORE_NUM, &vectorCoreNum) | Device 属性 | 此处查询 Vector Core 数量 |
这些接口覆盖了 Device 管理类别中“查询”方向的全部常用能力,与样例 README 中列出的 API 清单一致。值得注意的是aclrtGetRunMode:样例示例输出中显示RunMode is ACL_HOST,说明该示例输出是在 Host 侧仿真运行模式(ACL_HOST)下得到的,在真机(ACL_DEVICE)上该字段会不同。
工作线程:内存、数据搬运与核函数下发
查询完成后,工作线程执行一次完整的向量加法计算(对应 main.cpp):
- 内存分配:
aclrtMallocHost分配 3 块 Host 侧内存(hostSrcA、hostSrcB、hostDst);aclrtMalloc分配 3 块 Device 侧内存,标志位使用ACL_MEM_MALLOC_HUGE_FIRST(优先使用大块内存分配,减少碎片)。数据规模为TOTAL_SIZE = 1024个 float,即DATA_SIZE = 1024 * sizeof(float)字节。 - 数据初始化与 H2D 拷贝:将
hostSrcA[i] = hostSrcB[i] = i,然后用aclrtMemcpy(..., ACL_MEMCPY_HOST_TO_DEVICE)把两个输入数组拷到 Device。 - 查询资源限制:
aclrtGetDeviceResLimit(0, ACL_RT_DEV_RES_VECTOR_CORE, &resLimitValue)取回主线程设置的 Vector Core 限制值(应为 1),并打印Get device resLimit success. VECTOR_CORE 1.。 - 核函数下发:
AddDo(resLimitValue, stream, devSrcA, devSrcB, devDst, TOTAL_SIZE)将向量加法核函数提交到工作线程自己创建的 Stream 上。AddDo的声明见 kernel_add.h,其 AscendC 实现位于 kernel_add.cpp;aclrtSynchronizeStream(stream)阻塞等待 Stream 上任务全部完成。 - D2H 拷贝与校验:
aclrtMemcpy(..., ACL_MEMCPY_DEVICE_TO_HOST)取回结果,打印前 10 个元素并与期望值hostSrcA[i] + hostSrcB[i]对比。 - 资源释放:
aclrtResetDeviceResLimit(0)重置资源限制 →aclrtFree/aclrtFreeHost释放全部内存 →aclrtDestroyStream销毁 Stream →aclrtResetDeviceForce(0)强制复位 Device、回收该进程在 Device 上的所有剩余资源。aclrtResetDeviceForce属于较强的清理手段(回收 Device 资源),通常用于进程退出前的兜底清理。
多线程视角的要点
从源码结构看,该样例验证了两个多线程使用惯例:
- Device 上下文是线程相关的:主线程调用
aclrtSetDevice后,工作线程仍需在Init中再次调用aclrtSetDevice(0),两个线程各自持有 Device 0 的上下文,工作线程的 Stream、内存分配与核函数下发都发生在自己的工作线程上,互不干扰。 - 资源限制是进程级的:主线程设置的
aclrtSetDeviceResLimit对工作线程中同一 Device 的查询立即可见,这为“主控线程统一配额、业务线程执行任务”的框架化编程提供了依据。
涉及的 CANN Runtime API 一览
样例涉及的关键功能点及接口如下(与 README_en.md 一致):
- 初始化:
aclInit初始化、aclFinalize去初始化; - Device 管理:
aclrtSetDevice指定计算 Device;aclrtGetSocName查询处理器型号;aclrtGetDeviceCount获取可用 Device 数量;aclrtQueryDeviceStatus查询 Device 状态;aclrtGetRunMode获取运行模式;aclrtGetDeviceUtilizationRate查询 Cube/Vector/AI CPU 等模块利用率;aclrtDeviceGetStreamPriorityRange查询硬件支持的 Stream 优先级范围;aclrtGetDeviceInfo获取 Device 属性信息;aclrtSetDeviceResLimit/aclrtGetDeviceResLimit/aclrtResetDeviceResLimit设置、获取、重置当前进程的 Device 资源限制;aclrtResetDeviceForce强制复位 Device 并回收资源; - Stream 管理:
aclrtCreateStream创建 Stream、aclrtSynchronizeStream阻塞等待 Stream 任务完成、aclrtDestroyStream销毁 Stream; - 内存管理:
aclrtMalloc申请 Device 内存、aclrtMallocHost申请 Host 内存、aclrtFree/aclrtFreeHost释放内存; - 数据传输:
aclrtMemcpy内存复制。
接口声明均可在 acl_rt.h 中查阅;资源限制相关接口的参数与产品支持矩阵参见 Runtime 配置 API 参考,Device 管理接口(如aclrtSetDevice、aclrtGetDeviceCount)参见 Device 管理 API 参考。
示例输出
运行bash run.sh后,标准输出形如(完整输出同时保存在样例目录的output_msg.txt中):
[INFO] Start to run device_multi_thread sample. [INFO] Current Ascend chipset platform is: Ascend910_9362. [INFO] Get device count success. deviceCount: 2. [INFO] Query device status success. deviceStatus: 0. [INFO] RunMode is ACL_HOST. [INFO] Get device resLimit success. VECTOR_CORE 1. [INFO] The results (first 10 elements) of the kernel function: [INFO] Result: hostDst[0]: 0.000000 Expected value: 0.000000 [INFO] Result: hostDst[1]: 2.000000 Expected value: 2.000000 ... [INFO] Result: hostDst[9]: 18.000000 Expected value: 18.000000 [INFO] Run the device_multi_thread sample successfully.输出中的几个验证点:
deviceCount: 2表明当前环境可见 2 个 Device,具体数值随硬件配置变化;RunMode is ACL_HOST表示当前处于 Host 仿真运行模式,真实 NPU 环境下将显示ACL_DEVICE;VECTOR_CORE 1与主线程aclrtSetDeviceResLimit(..., 1)的设置值一致,证明资源限制已生效;- 结果序列
0, 2, 4, ..., 18符合srcA[i] + srcB[i] = i + i的期望值,说明核函数执行正确。
小结
该样例是理解 CANN Runtime 多线程 Device 管理的起点:主线程负责“选卡 + 配额”(aclrtSetDevice+aclrtSetDeviceResLimit),工作线程负责“查询 + 执行 + 清理”(Device 属性查询、Stream/内存管理、核函数下发与aclrtResetDeviceForce兜底)。如果你在多进程/多线程框架中需要限制某个进程占用的 Cube 或 Vector Core 数量、查询 Device 利用率与 Stream 优先级范围,可以直接参考 main.cpp 的调用顺序;同目录下的其他 Device 样例(device 目录 README)还覆盖了单 Device 常规使用、多 Device 与 P2P、设备身份映射等场景,可结合本样例的接口基础继续扩展。
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考