拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CANN Runtime 模型运行实例(Model RI)捕获与执行实战:基于 0_simple_model 样例的 Stream 任务捕获、模式切换与异步推理详解

CANN Runtime 模型运行实例Model RI捕获与执行实战基于 0_simple_model 样例的 Stream 任务捕获、模式切换与异步推理详解【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime本篇文章以 CANN / runtime 仓库 example/2_advanced_features/model_ri/0_simple_model/README_en.md 中的0_simple_model样例为主线系统讲解 CANN Runtime 的模型运行实例Model Runtime InstanceModel RI / modelRI机制如何在 Stream 上开启捕获Capture、把下发的任务暂存为可复用的模型实例并通过该实例反复异步执行推理。文章将同时结合仓库中的样例源码、CMake 构建脚本、接口头文件include/external/acl/acl_rt.h与 API 参考文档docs/zh/api_ref/15_model_running_instance_management.md讲清楚捕获模式GLOBAL / THREAD_LOCAL / RELAXED的设计意图与安全函数限制帮助读者掌握捕获—构建—执行—销毁的完整编程模型并具备直接编译运行该样例并自行验证的能力。1. 背景为什么需要模型运行实例捕获在常规的 CANN 编程模型中应用调用aclrtMemcpyAsync、aclnnAdd、aclnnMul等接口向 Stream 下发任务每一次下发都需要经历Host 侧创建任务描述 → 与 Runtime/驱动交互 → 在 Device 侧调度执行的完整链路。当一段任务序列例如拷贝输入数据 计算 A 计算 B需要被反复执行时重复下发会产生可观的 Host 侧开销并放大任务提交的时延抖动。CANN Runtime 提供的模型运行实例Model RI机制正是针对这一场景的优化手段在aclmdlRICaptureBegin与aclmdlRICaptureEnd之间所有下发到指定 Stream 上的任务并不会立即执行而是被暂存到系统内部创建的模型运行实例中调用aclmdlRIExecuteAsync执行该实例时这些暂存任务才被真正提交到 Device 执行。由于任务结构只构建一次、随后被反复复用Host 侧的下发开销被显著降低。按照 docs/zh/api_ref/15_model_running_instance_management.md 中的功能说明这种先捕获、后执行的方式同时解决了两个问题任务下发的确定性捕获期间下发的任务顺序被完整记录在模型运行实例中执行时按序复放执行效率模型运行实例可以被aclmdlRIExecuteAsync多次复用无需每次重建任务描述。0_simple_model样例演示的计算模型为out (self other * alpha) * other它先执行一个aclnnAdd对应outTmp self other * alpha再执行一个aclnnMul对应out outTmp * other并把Host → Device 输入拷贝这一数据搬运操作一并捕获进模型完整地展示了一个多算子计算图的捕获、构建与执行过程。2. 产品支持情况根据样例文档与 docs/zh/api_ref/15_model_running_instance_management.md 中的接口级产品支持矩阵Model RI 捕获相关接口aclmdlRICaptureBegin/aclmdlRICaptureEnd/aclmdlRIExecuteAsync等在以下产品上支持产品是否支持Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持需要说明的是API 文档同时标注了 Atlas 训练系列产品、Atlas 200I/500 A2 推理产品、IPV350 等对捕获接口为不支持因此在移植样例到具体硬件前应以目标产品的接口支持矩阵为准。样例文档给出的支持表仅覆盖其声明验证过的三类产品。3. 目录结构与样例组成0_simple_model位于仓库 example/2_advanced_features/model_ri/0_simple_model其目录内容如下example/2_advanced_features/model_ri/0_simple_model/ ├── CMakeLists.txt # 编译配置链接 libascendcl / libnnopbase / libopapi ├── README.md # 中文说明 ├── README_en.md # 英文说明本文主体来源 ├── main.cpp # 样例主程序捕获 执行 modelRI └── run.sh # 一键编译运行脚本其中main.cpp是核心实现它依赖 example/2_advanced_features/model_ri/model_utils.h 与 example/2_advanced_features/model_ri/model_utils.cpp 提供的工具函数CreateAclTensor创建绑定了 Device 地址的aclTensor、GetShapeSize计算张量元素个数、PrintArray打印输出向量。此外样例还使用了仓库 example 目录公共的初始化辅助函数与日志宏utils.h、CHECK_ERROR、INFO_LOG等。在 model_ri 这一功能目录example/2_advanced_features/model_ri/README_en.md中0_simple_model是最基础的入门样例其余样例在其基础上延伸出更多能力1_model_update对已捕获模型运行实例的任务更新2_model_switch模型运行实例中的 Stream 绑定、跳转与切换3_cond_modelaclGraph 条件操作IF/WHILE/SWITCH 及嵌套组合的图捕获与执行4_model_sync_externalACL Graph 跨边界同步场景下 Event 的 Record External / Wait External 用法5_reusable_buffer_reset枚举异步清零模型的 Stream/Task 结构并执行校验。4. 样例数据流与计算逻辑4.1 输入输出数据main.cpp中定义的数据如下main.cppvectorfloat selfHostData {1, 2, 3, 4, 5, 6, 7, 8}; vectorfloat otherHostData {2, 2, 2, 2, 2, 2, 2, 2}; vectorfloat outHostData {0, 0, 0, 0, 0, 0, 0, 0}; vectorfloat outTempHostData {0, 0, 0, 0, 0, 0, 0, 0}; vectorint64_t shape {4, 2}; float alphaValue 1.1f;self为 8 个元素的 float 向量shape 为{4, 2}other为全 2 的向量alpha为标量 1.1。4.2 计算流程模型捕获前样例先为两个算子分别查询 workspace 大小并创建执行器main.cppaclnnAddGetWorkspaceSize(self, other, alpha, outTmp, addWorkspaceSize, addExecutor); if (addWorkspaceSize 0) { CHECK_ERROR(aclrtMalloc(addWorkspaceAddr, addWorkspaceSize, ACL_MEM_MALLOC_HUGE_FIRST)); } aclnnMulGetWorkspaceSize(outTmp, other, out, mulWorkspaceSize, mulExecutor); if (mulWorkspaceSize 0) { CHECK_ERROR(aclrtMalloc(mulWorkspaceAddr, mulWorkspaceSize, ACL_MEM_MALLOC_HUGE_FIRST)); }不同算子的 workspace 可能不同这里为 add 与 mul 各自申请了独立的 Device 内存。随后开启捕获并把以下三类操作收入模型运行实例aclrtMemcpyAsync将selfHost中的数据从 Host 异步拷贝到selfDeviceHost 锁页内存到 Device入图aclnnAdd计算outTmp self other * alpha入图aclnnMul计算out outTmp * other入图。其中otherDevice的 Host→Device 数据填充使用的是同步aclrtMemcpy属于非安全函数必须在 RELAXED 模式下执行且不会入图、直接执行。4.3 输出结果按照样例文档中的 Sample Output程序循环执行模型 4 次每次输出[INFO] execute model, loop count: 1. [INFO] The vector data is: 6.4000 8.4000 10.4000 12.4000 14.4000 16.4000 18.4000 20.4000 ... [INFO] execute model, loop count: 4. [INFO] The vector data is: 6.4000 8.4000 10.4000 12.4000 14.4000 16.4000 18.4000 20.4000读者可以自行验算(self other * 1.1) * other即(1 2*1.1, 2 2*1.1, ..., 8 2*1.1) * 2 (6.4, 8.4, ..., 20.4)与输出完全吻合。向量打印由 model_utils.cpp 中的ModelUtils::PrintArray完成固定 4 位小数。5. 关键接口与捕获模式详解本节对照 include/external/acl/acl_rt.h 中的接口声明与 API 文档逐一定义样例涉及的 Model RI 核心接口。5.1 捕获模式枚举aclmdlRICaptureMode头文件中定义如下include/external/acl/acl_rt.htypedef enum { ACL_MODEL_RI_CAPTURE_MODE_GLOBAL 0, ACL_MODEL_RI_CAPTURE_MODE_THREAD_LOCAL, ACL_MODEL_RI_CAPTURE_MODE_RELAXED, } aclmdlRICaptureMode;三种模式用于限制非安全函数包括aclrtMemset、aclrtMemcpy、aclrtMemcpy2d以及使用非 Host 锁页内存进行异步内存复制的接口如aclrtMemcpyAsync的作用范围其语义如下GLOBAL全局模式所有线程都不可调用非安全函数。本线程若要调用需用aclmdlRICaptureThreadExchangeMode切换为 RELAXED其他线程调用该接口切换为 RELAXED 或 THREAD_LOCAL 后也可调用。THREAD_LOCAL线程局部模式当前线程禁止调用非安全函数但其他线程不在捕获状态的线程可以调用。当前线程要调用非安全函数需先切换为 RELAXED。RELAXED宽松模式所有线程都可以调用非安全函数此时即使在不在捕获状态的线程中把模式切换为其他值也不会生效其他线程仍按 RELAXED 处理。简单记忆GLOBAL 最严格THREAD_LOCAL 次之RELAXED 最宽松aclmdlRICaptureBegin以传入的 mode 开启捕获捕获过程中可通过aclmdlRICaptureThreadExchangeMode动态切换当前线程的模式。5.2aclmdlRICaptureBegin开始捕获aclError aclmdlRICaptureBegin(aclrtStream stream, aclmdlRICaptureMode mode);stream指定要捕获的 Streammode捕获模式限制非安全函数的作用范围。在 Begin 与 End 之间所有下发到该 Stream 的任务不会立即执行而是暂存在系统内部模型运行实例中只有调用aclmdlRIExecuteAsync才会真正执行。文档特别提示捕获的任务越多、经 Event 推导或内部任务操作使更多 Stream 进入捕获状态调度资源消耗越大因此需提前规划调度资源使用。此外若 Begin 时 mode 为非 RELAXED 值Begin 与 End 必须位于同一线程。5.3aclmdlRICaptureThreadExchangeMode切换当前线程捕获模式aclError aclmdlRICaptureThreadExchangeMode(aclmdlRICaptureMode *mode);mode为输入输出参数调用后当前线程的捕获模式被设置为*mode中的值同时*mode返回该线程之前设置的模式。样例中的典型用法如下main.cppaclmdlRICaptureMode mode ACL_MODEL_RI_CAPTURE_MODE_RELAXED; // 通过该函数设置 mode此处修改为 RELAXED可以执行非安全的函数 CHECK_ERROR(aclmdlRICaptureThreadExchangeMode(mode)); // 同步复制为非安全函数要设置为 RELAXED该调用不会入图会直接执行 CHECK_ERROR(aclrtMemcpy(otherDevice, size, otherHostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE)); // 再次调用会回到上次设置的 Mode CHECK_ERROR(aclmdlRICaptureThreadExchangeMode(mode));这里展示了临时放宽、用完归还的惯用法第一次调用把模式换成 RELAXED*mode中带回原模式执行完同步拷贝后再调用一次恢复原模式。5.4aclmdlRICaptureEnd结束捕获并获得模型运行实例aclError aclmdlRICaptureEnd(aclrtStream stream, aclmdlRI *modelRI);stream要结束捕获的 Stream必须与 Begin 传入的 Stream 相同modelRI输出参数返回暂存了所捕获任务的模型运行实例句柄。Begin 与 End 成对使用两个接口的 Stream 应一致。样例捕获结束后的代码为main.cppCHECK_ERROR(aclmdlRICaptureEnd(stream, modelRI)); const char* jsonPath ./modelRI.json; CHECK_ERROR(aclmdlRIDebugJsonPrint(modelRI, jsonPath, 0));5.5aclmdlRIDebugJsonPrint导出模型信息维测aclError aclmdlRIDebugJsonPrint(aclmdlRI modelRI, const char* path, uint32_t flags);modelRI有效的模型运行实例path导出文件路径含文件名该路径需存在且可读可写否则接口返回失败flags配置选项取值如下ACL_MDLRI_DEBUG_JSON_PRINT_SUMMARY0x0UL打印 modelRI 基础信息例如 Model ID、Stream ID、Task ID 等ACL_MDLRI_DEBUG_JSON_PRINT_VERBOSE0x1UL在基础信息之上增加打印 modelRI 中 AI Core 算子的核函数参数信息。样例以flags 0即 SUMMARY将模型运行实例信息导出到./modelRI.json。导出的 JSON 可通过 tracing 方式例如chrome://tracing/查看模型的可视化信息便于定位任务结构与依赖关系。另外API 文档指出旧接口aclmdlRIDebugPrint已弃用建议改用本接口头文件中亦标注了ACL_DEPRECATED_MESSAGE见 include/external/acl/acl_rt.h。5.6aclmdlRIExecuteAsync异步执行模型aclError aclmdlRIExecuteAsync(aclmdlRI modelRI, aclrtStream stream);modelRI要执行的模型运行实例可通过捕获接口如aclmdlRICaptureGetInfo/aclmdlRICaptureEnd或构建接口aclmdlRIBuildBegin/aclmdlRIBuildEnd获得stream用于执行模型推理任务的 Stream。约束说明见 docs/zh/api_ref/15_model_running_instance_management.md同一模型运行实例的多次执行会自动串行如需并行执行应分别捕获生成多个模型运行实例执行前请确保设备资源充足。样例的执行循环如下main.cppfor (int i 0; i loopCount; i) { INFO_LOG(execute model, loop count: %d., i 1); CHECK_ERROR(aclmdlRIExecuteAsync(modelRI, stream)); CHECK_ERROR(aclrtSynchronizeStream(stream)); CHECK_ERROR(aclrtMemcpy(outHostData.data(), size, outDevice, size, ACL_MEMCPY_DEVICE_TO_HOST)); ModelUtils::PrintArray(outHostData); }loopCount 4每次执行后调用aclrtSynchronizeStream阻塞等待任务完成再把 Device 侧结果拷回 Host 打印从而验证同一模型实例被复用了 4 次且结果一致。5.7aclmdlRIDestroy销毁模型运行实例aclError aclmdlRIDestroy(aclmdlRI modelRI);modelRI为待销毁的模型运行实例。文档提示所有任务执行完毕后若无需再使用内部模型应及时调用该接口释放资源。6. 编译与运行6.1 环境准备样例编译运行前需要安装 CANN 环境。按样例文档的指引环境安装与运行细节以 example 目录 README 为准。主要步骤如下# ${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 # 编译并运行 bash run.sh其中 example/set_sample_env.sh 会通过 example/common/resolve_cann_env.sh 解析 CANN 安装路径并借助 example/tools/get_soc_version/get_soc_version.cpp 小工具调用 Runtime ACL 接口aclrtGetSocName自动探测 SOC_VERSION同时自动定位 ASCENDC_CMAKE_DIRtikcpp 的ascendc.cmake所在目录随后导出ASCEND_INSTALL_PATH、ASCEND_HOME_PATH、SOC_VERSION、ASCENDC_CMAKE_DIR等环境变量。6.2 构建脚本解析run.sh 的构建流程为source $_ASCEND_INSTALL_PATH/bin/setenv.bash rm -rf build mkdir -p build cmake -B build -DASCEND_CANN_PACKAGE_PATH${_ASCEND_INSTALL_PATH} cmake --build build -j cmake --install build ./build/main | tee output_msg.txt它依次完成加载 CANN 环境 → 创建 build 目录 → 配置 CMake → 并行编译 → 安装 → 运行可执行文件并把输出同时打印到终端与output_msg.txt。6.3 CMake 配置要点CMakeLists.txt 中值得注意的配置项set(SOC_VERSION $ENV{SOC_VERSION})从环境变量读取 SoC 版本set(ASCENDC_CMAKE_DIR $ENV{ASCENDC_CMAKE_DIR})从环境变量读取 AscendC 的 CMake 目录头文件搜索路径包含${ASCEND_CANN_PACKAGE_PATH}/include、${ASCEND_CANN_PACKAGE_PATH}/aclnn以及样例公共目录../../..对应 example 根目录..对应 model_ri 目录链接库为libascendcl.soRuntime/AscendCL、libnnopbase.so与libopapi.so算子 API 与执行器。该样例不涉及 AscendC 自定义算子编译因此无需额外链接libascendc等算子编译相关库。7. 完整调用链路梳理将 main.cpp 的调用序列按功能归类得到完整生命周期如下阶段关键接口作用初始化aclInit/aclFinalize初始化 / 去初始化 AscendCL 配置Device 管理aclrtSetDevice/aclrtResetDeviceForce指定计算 Device / 强制复位 Device 并回收资源Context 管理aclrtCreateContext/aclrtDestroyContext创建 / 销毁 ContextStream 管理aclrtCreateStream/aclrtSynchronizeStream/aclrtDestroyStreamForce创建 Stream / 阻塞等待任务完成 / 强制销毁 Stream 并丢弃所有任务模型管理aclmdlRICaptureBegin/aclmdlRICaptureThreadExchangeMode/aclmdlRICaptureEnd/aclmdlRIDebugJsonPrint/aclmdlRIExecuteAsync/aclmdlRIDestroy捕获任务、切换模式、构建 modelRI、导出 JSON、异步执行、销毁实例内存管理aclrtMalloc/aclrtMallocHost/aclrtFree申请 Device 内存 / 申请 Host 锁页内存 / 释放 Device 内存数据传输aclrtMemcpy/aclrtMemcpyAsync同步内存拷贝 / 异步内存拷贝完整主流程对应 main.cppaclInit(NULL)初始化 AscendCLaclrtSetDevice(0)指定 DeviceaclrtCreateContext创建 Context通过ModelUtils::CreateAclTensor创建绑定 Device 内存的aclTensor内部调用aclrtMalloc与aclCreateTensor见 model_utils.cpp并创建aclScalar(alpha)查询 add/mul 算子的 workspace 大小并申请内存、获取执行器创建 Stream申请 Host 锁页内存并同步填充self数据aclmdlRICaptureBegin(stream, ACL_MODEL_RI_CAPTURE_MODE_GLOBAL)开启全局捕获在捕获区间内依次执行aclrtMemcpyAsync入图、切换 RELAXED 后执行同步aclrtMemcpy不入图、直接执行、aclnnAdd入图、aclnnMul入图aclmdlRICaptureEnd结束捕获得到modelRIaclmdlRIDebugJsonPrint导出modelRI.json循环 4 次aclmdlRIExecuteAsyncaclrtSynchronizeStream D2H 拷贝 打印结果依次释放aclmdlRIDestroy→aclrtDestroyStreamForce→ 销毁 tensor/scalar →aclrtFree各类 Device 内存 →aclrtDestroyContext→aclrtResetDeviceForce→aclFinalize。从源码结构可以看到样例刻意区分了两类数据搬运方式以演示捕获模式的语义aclrtMemcpyAsync使用 Host 锁页内存是安全函数可入图同步aclrtMemcpy是非安全函数只能在 RELAXED 模式下直接执行、不会入图。这也解释了aclmdlRICaptureThreadExchangeMode在代码中成对出现的必要性。8. 常见问题与注意事项Begin/End 必须成对且 Stream 一致aclmdlRICaptureEnd使用的 Stream 必须与aclmdlRICaptureBegin相同且当 Begin 以非 RELAXED 模式开始时两者必须位于同一线程。非安全函数必须在 RELAXED 下调用捕获区间内调用aclrtMemcpy、aclrtMemset、aclrtMemcpy2d或使用非 Host 锁页内存的aclrtMemcpyAsync等非安全函数前须先用aclmdlRICaptureThreadExchangeMode切换到 RELAXED否则接口会失败。同一模型实例的执行是串行的如需并发执行同一段任务应分别捕获生成多个模型运行实例。资源规划捕获的任务越多、被 Event 推导或内部任务操作拉入捕获状态的 Stream 越多调度资源消耗越大需提前规划。及时销毁模型运行实例执行完毕后应调用aclmdlRIDestroy释放资源避免长期占用 Device 侧调度与内存资源。JSON 导出路径需存在且可写aclmdlRIDebugJsonPrint的path指向的文件路径必须具备读写权限否则接口返回失败。调试信息查看导出后的modelRI.json可通过chrome://tracing/等 tracing 工具可视化查看 Model ID、Stream ID、Task ID 与任务依赖关系。9. 延伸阅读若希望继续深入 Model RI 的更多能力可按如下顺序阅读仓库内的相关样例与文档1_model_update任务更新结合aclmdlRICaptureTaskGrpBegin/End与aclmdlRIUpdate2_model_switchStream 绑定与跳转切换3_cond_modelIF/WHILE/SWITCH 条件图捕获4_model_sync_external跨边界同步的 Record External / Wait External5_reusable_buffer_reset异步清零模型的任务结构枚举与缓冲区校验docs/zh/api_ref/15_model_running_instance_management.md全部 Model RI 接口的完整参数与约束说明docs/zh/design/features/aclgraph.mdACL Graph 捕获机制的设计背景。同时样例文档声明当前已知 Issue无可放心作为入门参考。若需了解 CANN 环境安装与 example 目录的整体编译运行方式请参考 example/README_en.md。【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门