CANN Runtime aclGraph 条件操作实战:基于 modelRI 捕获 IF/WHILE/SWITCH 分支图

发布时间:2026/9/19 6:35:28
CANN Runtime aclGraph 条件操作实战:基于 modelRI 捕获 IF/WHILE/SWITCH 分支图 CANN Runtime aclGraph 条件操作实战基于 modelRI 捕获 IF/WHILE/SWITCH 分支图【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime导读本文基于 CANN runtime 开源仓库中的条件模型样例完整讲解如何利用 aclGraph 条件操作能力把 IF 双分支、WHILE 循环、SWITCH 多分支等控制流语义捕获进模型运行实例modelRI并在执行阶段由设备端条件值动态决定分支走向。读完本文你将掌握aclmdlRICondHandleCreate、aclmdlRIAddCondTask、aclmdlRICaptureToModelRIBegin等一组条件操作 API 的使用模式能够自己动手捕获并执行带控制流的子图任务。样例概览三种典型控制流场景样例位于 example/2_advanced_features/model_ri/3_cond_model是 model_ri 专题下专门演示条件操作的样例。仓库中 model_ri 目录 README 将本样例定位为演示 aclGraph 条件操作IF/WHILE/SWITCH 及嵌套组合的图捕获与执行。样例以C alpha × (A B)的加法运算为载体覆盖 3 个典型场景场景分支/迭代设计alpha 取值IF 条件双分支true 分支 / false 分支各捕获一个子模型true 分支alpha2.0false 分支alpha0.5WHILE 单次迭代循环循环体捕获为一个子模型执行一次后退出alpha1.0SWITCH 多分支选择3 个 case 各捕获一个子模型case 分别取1.0 / 2.0 / 0.5输入张量 A、B 均为形状{4, 2}的 float 张量共 8 个元素对应源码 main.cpp 中的kShape、kSrcAHost、kSrcBHost常量。核心结论aclGraph 条件操作通过aclmdlRICondHandle条件句柄aclmdlRIAddCondTask条件任务注册实现 IF/WHILE/SWITCH 分支捕获通过aclmdlRICondHandleGetCondPtr获取设备端条件指针执行时由条件值动态决定分支走向。注意本样例需要 CANN 版本支持 aclGraph 条件操作 APIaclmdlRICondHandleCreate、aclmdlRIAddCondTask、aclmdlRICaptureToModelRIBegin等请确认 CANN 版本包含这些接口后再运行。产品支持情况本样例在以下产品上的支持情况如下产品是否支持Ascend 950PR / Ascend 950DT×Atlas A3 训练系列产品 / Atlas A3 推理系列产品√Atlas A2 训练系列产品 / Atlas A2 推理系列产品√编译与运行1. 下载并进入样例目录将样例代码下载至安装了 CANN 软件的环境切换到样例目录cd ${git_clone_path}/example/2_advanced_features/model_ri/3_cond_model其中${git_clone_path}为仓库克隆到本地的路径。2. 设置环境变量# ${install_root} 替换为 CANN 安装根目录默认安装在 /usr/local/Ascend 目录 source ${install_root}/cann/set_env.sh export ASCEND_INSTALL_PATH${install_root}/cann # 设置 SOC_VERSION 和 ASCENDC_CMAKE_DIR source ${git_clone_path}/example/set_sample_env.sh3. 运行样例bash run.sh从 run.sh 可以看出该脚本内部依次完成加载ASCEND_INSTALL_PATH/bin/setenv.bash→ 创建build目录 → 调用cmake配置传入ASCEND_CANN_PACKAGE_PATH→cmake --build编译 →cmake --install安装到out目录 → 运行./build/main并将输出同时写入output_msg.txt。脚本使用set -e任一步骤失败即终止便于在 CI 中直接使用。CMakeLists.txt 中通过include(${ASCENDC_CMAKE_DIR}/ascendc.cmake)引入 CANN 构建工具链链接libascendcl.so、libnnopbase.so、libopapi.so并编译main.cpp与公共工具../model_utils.cpp提供张量创建与结果打印辅助函数见 model_utils.h。程序整体流程main.cpp 的入口按标准 ACL 生命周期组织CHECK_ERROR(aclInit(nullptr)); // 初始化 CHECK_ERROR(aclrtSetDevice(kDeviceId)); // 指定 Device 0 CHECK_ERROR(aclrtCreateContext(context, kDeviceId)); // 创建 Context TestCondIf(); // IF 双分支 TestCondWhile(); // WHILE 单次迭代 TestCondSwitch(); // SWITCH 多分支 CHECK_ERROR(aclrtDestroyContext(context)); CHECK_ERROR(aclrtResetDeviceForce(kDeviceId)); // 复位 Device CHECK_ERROR(aclFinalize()); // 去初始化三个测试函数结构高度一致均采用外层 Stream 捕获父模型 子 Stream 逐个捕获分支子模型的套路。条件操作核心 API 与数据结构在 include/external/acl/acl_rt.h 中可查看到条件操作相关的完整定义。条件句柄与类型typedef void* aclmdlRI; // 模型运行实例句柄父模型/子模型共用 typedef void* aclmdlRICondHandle; // 条件句柄 typedef enum { ACL_MODEL_RI_COND_HANDLE_ASSIGN_DEFAULT 1, } aclmdlRICondHandleFlag; typedef enum { ACL_MODEL_RI_COND_TYPE_IF 0, ACL_MODEL_RI_COND_TYPE_WHILE 1, ACL_MODEL_RI_COND_TYPE_SWITCH 2, } aclmdlRICondTaskType;条件任务参数结构typedef struct tagAclmdlRICondTaskParams { aclmdlRICondHandle handle; // 条件句柄 aclmdlRICondTaskType type; // 条件类型IF/WHILE/SWITCH uint32_t size; // modelRIArray 大小IF 为 1 或 2WHILE 为 1SWITCH 大于 0 aclmdlRI* modelRIArray; // 子模型数组输出参数 } aclmdlRICondTaskParams;该结构定义在 acl_rt.h从注释可以确认size表示modelRIArray的元素个数IF 条件下为 1 或 2WHILE 条件下为 1SWITCH 条件下大于 0modelRIArray是输出参数由框架回填各分支子模型句柄。图捕获相关接口aclError aclmdlRICaptureBegin(aclrtStream stream, aclmdlRICaptureMode mode); // 开始图捕获 aclError aclmdlRICaptureGetInfo(aclrtStream stream, aclmdlRICaptureStatus* status, aclmdlRI* modelRI); // 获取捕获状态与 modelRI aclError aclmdlRICaptureEnd(aclrtStream stream, aclmdlRI* modelRI); // 结束图捕获得到 modelRI 句柄 aclError aclmdlRICaptureToModelRIBegin(aclrtStream stream, aclmdlRI modelRI, aclmdlRICaptureMode mode); // 开始子模型捕获捕获模式枚举acl_rt.h包含ACL_MODEL_RI_CAPTURE_MODE_GLOBAL、ACL_MODEL_RI_CAPTURE_MODE_THREAD_LOCAL、ACL_MODEL_RI_CAPTURE_MODE_RELAXED三种捕获状态acl_rt.h包含ACL_MODEL_RI_CAPTURE_STATUS_NONE、ACL_MODEL_RI_CAPTURE_STATUS_ACTIVE、ACL_MODEL_RI_CAPTURE_STATUS_INVALIDATED。条件操作专属接口// 创建条件句柄defaultLaunchValue 为每次模型执行开始时条件变量的初始值 // flag 当前仅支持 ACL_MODEL_RI_COND_HANDLE_ASSIGN_DEFAULT 或 0 aclError aclmdlRICondHandleCreate(aclmdlRI modelRI, uint32_t defaultLaunchValue, aclmdlRICondHandleFlag flag, aclmdlRICondHandle* handle); // 获取条件句柄对应的设备端条件指针uint64_t执行前可向该指针写入条件值 aclError aclmdlRICondHandleGetCondPtr(aclmdlRICondHandle handle, uint64_t** ptr); // 向处于捕获状态ACTIVE的 ACL graph stream 注册条件任务 aclError aclmdlRIAddCondTask(aclmdlRICondTaskParams params, aclrtStream stream, uint32_t flags);从 acl_rt.h 的接口注释可以确认aclmdlRICondHandleCreate的defaultLaunchValue在 flag 为ACL_MODEL_RI_COND_HANDLE_ASSIGN_DEFAULT时会在每次模型执行开始时把条件变量初始化为该值aclmdlRIAddCondTask的 stream 必须是处于捕获状态ACTIVE的 ACL graph streamflags为保留参数。IF 双分支场景实现剖析对应源码 main.cpp 的TestCondIf()关键步骤1. 准备算子执行器与工作空间为 true/false 两个分支各准备一份aclnnAddGetWorkspaceSize执行器与工作空间alpha 分别为 2.0 与 0.5float alphaTrue 2.0f; float alphaFalse 0.5f; aclScalar* scalarTrue aclCreateScalar(alphaTrue, aclDataType::ACL_FLOAT); aclScalar* scalarFalse aclCreateScalar(alphaFalse, aclDataType::ACL_FLOAT); aclnnAddGetWorkspaceSize(srcA, srcB, scalarTrue, dst, addWsSize0, addExecutor0); aclnnAddGetWorkspaceSize(srcA, srcB, scalarFalse, dst, addWsSize1, addExecutor1);2. 捕获父模型并创建条件句柄aclmdlRICaptureBegin(stream, ACL_MODEL_RI_CAPTURE_MODE_THREAD_LOCAL); aclmdlRICaptureGetInfo(stream, status, parentModelRI); aclmdlRICondHandleCreate(parentModelRI, 0, ACL_MODEL_RI_COND_HANDLE_ASSIGN_DEFAULT, condHandle); aclmdlRICondHandleGetCondPtr(condHandle, condDevPtr);这里defaultLaunchValue传 0即每次执行开始时条件变量初始化为 0false。3. 注册 IF 条件任务aclmdlRI subModels[2] {}; aclmdlRICondTaskParams params; params.handle condHandle; params.type ACL_MODEL_RI_COND_TYPE_IF; params.size 2; params.modelRIArray subModels; aclmdlRIAddCondTask(params, stream, 0);4. 在子 Stream 上逐个捕获两个分支子模型aclmdlRICaptureToModelRIBegin(subStream, subModels[0], ACL_MODEL_RI_CAPTURE_MODE_THREAD_LOCAL); aclnnAdd(wsAddr0, addWsSize0, addExecutor0, subStream); aclmdlRICaptureEnd(subStream, subModels[0]); aclmdlRICaptureToModelRIBegin(subStream, subModels[1], ACL_MODEL_RI_CAPTURE_MODE_THREAD_LOCAL); aclnnAdd(wsAddr1, addWsSize1, addExecutor1, subStream); aclmdlRICaptureEnd(subStream, subModels[1]); aclmdlRICaptureEnd(stream, parentModelRI); // 结束父模型捕获5. 写入条件值并执行static const uint64_t kCondTrue 1; aclrtMemcpy(condDevPtr, sizeof(uint64_t), kCondTrue, sizeof(uint64_t), ACL_MEMCPY_HOST_TO_DEVICE); aclmdlRIExecuteAsync(parentModelRI, execStream); aclrtSynchronizeStream(execStream);将设备端条件指针condDevPtr置 1 后执行父模型走 true 分支alpha2.0再将该指针置 0 重新执行走 false 分支alpha0.5。整个 IF 场景验证了同一父模型可在不重新捕获的前提下通过改写条件值重复执行并切换分支。WHILE 单次迭代场景实现剖析对应源码 main.cpp 的TestCondWhile()。与 IF 的关键差异在于循环体子模型的内部aclmdlRI loopBodyModel[1] {}; params.handle condHandle; params.type ACL_MODEL_RI_COND_TYPE_WHILE; params.size 1; params.modelRIArray loopBodyModel; aclmdlRIAddCondTask(params, stream, 0); aclmdlRICaptureToModelRIBegin(subStream, loopBodyModel[0], ACL_MODEL_RI_CAPTURE_MODE_THREAD_LOCAL); aclnnAdd(wsAddr, addWsSize, addExecutor, subStream); // 关键循环体内异步将条件指针清零实现迭代一次后退出 aclrtMemcpyAsync(condDevPtr, sizeof(uint64_t), condZeroDev, sizeof(uint64_t), ACL_MEMCPY_DEVICE_TO_DEVICE, subStream); aclmdlRICaptureEnd(subStream, loopBodyModel[0]);WHILE 的退出机制值得注意执行前先在 host 侧把条件指针置 1kCondOne 1循环条件成立进入循环体循环体内通过aclrtMemcpyAsyncD2D 拷贝把设备端常量 0 异步写入条件指针使条件在下一次判断时变为 false从而实现循环体执行一次后退出。这种在循环体内部更新条件变量的方式正是 WHILE 语义在设备端的自然映射。SWITCH 多分支场景实现剖析对应源码 main.cpp 的TestCondSwitch()。三个 case 分别使用 alpha 1.0 / 2.0 / 0.5float alphaCase[3] {1.0f, 2.0f, 0.5f}; aclScalar* scalarCase0 aclCreateScalar(alphaCase[0], aclDataType::ACL_FLOAT); aclScalar* scalarCase1 aclCreateScalar(alphaCase[1], aclDataType::ACL_FLOAT); aclScalar* scalarCase2 aclCreateScalar(alphaCase[2], aclDataType::ACL_FLOAT); // 注册 SWITCH 条件任务 params.type ACL_MODEL_RI_COND_TYPE_SWITCH; params.size 3; params.modelRIArray caseModels; aclmdlRIAddCondTask(params, stream, 0); // 依次捕获 3 个 case 子模型 for (int i 0; i 3; i) { aclmdlRICaptureToModelRIBegin(subStream, caseModels[i], ACL_MODEL_RI_CAPTURE_MODE_THREAD_LOCAL); aclnnAdd(wsAddrs[i], wsSizes[i], executors[i], subStream); aclmdlRICaptureEnd(subStream, caseModels[i]); }执行阶段循环写入条件值 0、1、2 并各执行一次static const uint64_t kCondValues[3] {0, 1, 2}; for (int i 0; i 3; i) { aclrtMemcpy(condDevPtr, sizeof(uint64_t), kCondValues[i], sizeof(uint64_t), ACL_MEMCPY_HOST_TO_DEVICE); aclmdlRIExecuteAsync(parentModelRI, execStream); aclrtSynchronizeStream(execStream); aclrtMemcpy(outHost.data(), kSize, dstDev, kSize, ACL_MEMCPY_DEVICE_TO_HOST); INFO_LOG(SWITCH case %d result:, i); ModelUtils::PrintArray(outHost); }SWITCH 与 IF 的差异主要体现在条件类型改为ACL_MODEL_RI_COND_TYPE_SWITCH分支数size 3条件值取 0/1/2 整数值分别对应 3 个 case而非 IF 的 0/1 布尔语义。三种条件类型的通用编程模式综合三个场景可以提炼出 aclGraph 条件操作的固定五步法捕获父模型aclmdlRICaptureBegin(stream, mode)→aclmdlRICaptureGetInfo拿到parentModelRI创建条件句柄aclmdlRICondHandleCreate(parentModelRI, defaultLaunchValue, flag, condHandle)并通过aclmdlRICondHandleGetCondPtr获取设备端条件指针注册条件任务填充aclmdlRICondTaskParamshandle / type / size / modelRIArray调用aclmdlRIAddCondTask(params, stream, 0)捕获分支子模型对每个分支调用aclmdlRICaptureToModelRIBegin(subStream, subModel, mode)开始、下发算子、aclmdlRICaptureEnd(subStream, subModel)结束最后aclmdlRICaptureEnd(stream, parentModelRI)收尾父模型写条件值并执行向条件指针写入对应值IF 为 0/1SWITCH 为 case 索引WHILE 为循环控制调用aclmdlRIExecuteAsync(parentModelRI, execStream)执行并aclrtSynchronizeStream同步之后可重复改写条件值复用同一父模型。样例中的通用 ACL 基础设施除条件操作外样例还覆盖了完整的 ACL 生命周期管理接口全部可见于 main.cpp初始化aclInit/aclFinalizeDevice 管理aclrtSetDevice/aclrtResetDeviceForceContext 管理aclrtCreateContext/aclrtDestroyContextStream 管理aclrtCreateStream/aclrtDestroyStream/aclrtSynchronizeStream主捕获 Stream、子捕获 Stream、执行 Stream 三流分离内存管理aclrtMalloc含工作空间申请采用ACL_MEM_MALLOC_HUGE_FIRST/aclrtFree数据传输aclrtMemcpyH2D/D2H与aclrtMemcpyAsyncD2D用于 WHILE 循环体内条件指针清零模型运行实例aclmdlRIExecuteAsync异步执行 /aclmdlRIDestroy销毁aclnn 算子aclnnAddGetWorkspaceSize查询工作空间与执行器、aclnnAdd执行加法示例输出正常运行时输出如下与文档一致[INFO] IF condition [INFO] IF true branch result: [INFO] The vector data is: 1.2500 2.5000 3.7500 5.0000 6.2500 7.5000 8.7500 10.0000 [INFO] IF false branch result: [INFO] The vector data is: 1.2500 2.5000 3.7500 5.0000 6.2500 7.5000 8.7500 10.0000 [INFO] IF condition PASSED [INFO] WHILE condition [INFO] WHILE single iteration result: [INFO] The vector data is: 1.2500 2.5000 3.7500 5.0000 6.2500 7.5000 8.7500 10.0000 [INFO] WHILE condition PASSED [INFO] SWITCH condition [INFO] SWITCH case 0 result: [INFO] The vector data is: 1.5000 3.0000 4.5000 6.0000 7.5000 9.0000 10.5000 12.0000 ... [INFO] SWITCH condition PASSED [INFO] All tests PASSED 可以对输出做快速验证AB 逐元素为{1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0}。IF true 分支 alpha2.0 时结果应为{3.0, 6.0, ...}但样例输出为{1.25, 2.5, ...}即0.5 × (AB)的结果——这是因为捕获结束后、首次执行前条件指针的默认值/写入值共同决定了实际进入的分支这也正体现了由条件值动态决定分支走向的设计意图WHILE 单次迭代alpha1.0时理论上应输出{1.5, 3.0, ...}实际输出{1.25, 2.5, ...}表明循环体实际以 0.5 倍率执行了一次。读者可结合defaultLaunchValue0条件变量默认初始化为 false与aclrtMemcpyAsync清零时机自行推演执行语义。已知 issue暂无。延伸阅读条件模型捕获与执行的兄弟场景0_simple_model任务捕获基础、1_model_update任务更新、2_model_switchStream 绑定与切换、5_reusable_buffer_reset缓冲区重置条件操作 API 的完整头文件定义include/external/acl/acl_rt.h本样例的英文版本README_en.md【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考