
CANN Runtime 回调机制实战指南HostFunc 回调、回调线程管理与异常回调错误定位【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime导读本指南基于 CANN Runtime 开源仓库中的callback示例模块系统讲解三类核心回调能力基于aclrtLaunchHostFunc的 Stream Host 回调、回调任务在 Stream 上的调度与执行顺序以及基于异常回调Exception Callback与 Runtime 错误查询接口的完整错误处理链路。读完本文后你将掌握 CANN AscendCL 中回调函数的注册方式、内部线程管理机制、遇错即停stop-on-failure的流配置以及如何组合使用aclrtSubscribeReport、aclrtLaunchCallback、aclrtSetExceptionInfoCallback与aclrtGetErrorVerbose等接口构建可落地的异常诊断方案。一、示例模块概览在 example/2_advanced_features/callback/ 目录下仓库提供了三个由浅入深的回调示例覆盖 Stream 回调、HostFunc 回调与异常回调子目录主题核心演示内容0_simple_callbackReport 回调用aclrtLaunchHostFunc在 NPU 任务前后插入 CPU 回调验证回调线程由 Runtime 内部创建与管理1_callback_hostfuncHostFunc 回调调度在 Stream 上派发 Host 侧函数验证其排空既有任务、阻塞后续任务的执行顺序语义2_callback_exception异常回调与错误查询用异常回调获取任务异常信息并在同步失败后补充查询近期错误、线程级错误与设备详细错误构成完整错误处理链三个示例支持的产品范围一致覆盖以下硬件平台Ascend 950PR/Ascend 950DT、Atlas A3 训练系列/Atlas A3 推理系列、Atlas A2 训练系列/Atlas A2 推理系列产品。二、构建与运行环境准备所有示例的运行步骤一致先加载 CANN 环境变量并自动识别芯片型号再执行构建脚本# 将 ${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 # 进入对应示例目录后构建并运行 cd example/2_advanced_features/callback/0_simple_callback bash run.sh环境安装与运行的完整说明参见 example/README_en.md。run.sh会在脚本内部完成编译与执行无需手工调用 CMake。三、示例 0_simple_callback用 aclrtLaunchHostFunc 在任务前后插入 CPU 回调3.1 能力与接口该示例演示在 Stream 上调用aclrtLaunchHostFunc启动 Host 回调任务在 NPU 任务之前和之后分别插入 CPU 回调函数。其关键设计在于——该接口内部会自动创建并管理回调线程用户既不需要手动创建线程也不需要显式注册线程。核心接口清单声明见 include/external/acl/acl_rt.h初始化与资源管理aclInit/aclFinalize、aclrtSetDevice/aclrtResetDeviceForce、aclrtCreateContext/aclrtDestroyContext、aclrtCreateStream/aclrtDestroyStreamForce流错误模式aclrtSetStreamFailureMode主机回调aclrtLaunchHostFunc内存与数据传输aclrtMalloc/aclrtFree、aclrtMemcpy、aclrtSynchronizeStreamaclrtLaunchHostFunc的接口语义为向 Stream 的任务队列追加一个主机函数调用。其签名为typedef void (*aclrtHostFunc)(void *args); aclError aclrtLaunchHostFunc(aclrtStream stream, aclrtHostFunc fn, void* args);三个参数分别指定目标 Stream、回调函数指针与传入回调的用户数据可传nullptr。回调在传入 Stream 上按队列顺序执行保证与同一 Stream 上其他任务严格有序。3.2 源码实现剖析核心逻辑位于 example/2_advanced_features/callback/0_simple_callback/callback_sample.cpp。示例首先完成初始化并在创建 Stream 后调用aclrtSetStreamFailureMode(stream_, ACL_STOP_ON_FAILURE)将流设置为遇错即停模式int CallBackSample::Init() { CHECK_ERROR(aclInit(nullptr)); CHECK_ERROR(aclrtSetDevice(deviceId_)); CHECK_ERROR(aclrtCreateContext(context_, deviceId_)); CHECK_ERROR(aclrtCreateStream(stream_)); CHECK_ERROR(aclrtSetStreamFailureMode(stream_, ACL_STOP_ON_FAILURE)); return 0; }随后是回调的核心编排逻辑先下发一个前置回调、一个 NPU 算子任务LongOP、再下发一个后置回调最后同步等待流排空int* userData new int(520); CHECK_ERROR(aclrtLaunchHostFunc(stream_, CallBackBeforeLaunchFunc, userData)); LongOP(blockDim, stream_, numDevice); INFO_LOG(After begin a task, launch one hostfunc.); CHECK_ERROR(aclrtLaunchHostFunc(stream_, CallBackFunc, userData)); CHECK_ERROR(aclrtSynchronizeStream(stream_));两个回调函数签名一致通过void* arg接收用户数据并强转为int*后读取void CallBackSample::CallBackBeforeLaunchFunc(void* arg) { int* data static_castint*(arg); INFO_LOG(This callback before task, result: user data is: %d., *data); }值得注意的执行顺序回调 1 →LongOPNPU 任务 → 回调 2。由于三者处于同一 StreamNPU 任务的结束会通过事件通知驱动回调 1 执行随后才运行回调 2从而验证回调在 Stream 任务队列中顺序执行的语义。3.3 底层线程管理原理从 src/runtime/api/impl/api_impl.cc 的实现可以确认aclrtLaunchHostFunc的线程管理细节获取当前 Context若传入stm nullptr则回退到当前 Context 的默认流加锁检查 Context 级回调线程是否已存在若不存在则调用CreateContextCallBackThread()在 Context 级别创建回调线程——这正是无需用户手动建线程的根源通过JudgeNeedSubscribe判断新 Stream 是否需要在回调线程中注册订阅关系必要时调用SubscribeCallback完成 Stream 到回调线程的绑定最后调用CallbackLaunchWithoutEvent或面向 Stars 架构的StarsLaunchSubscribeProc将回调追加到 Stream 任务队列。从源码结构看回调线程按 Context 维度复用同一 Context 下多个 Stream 共享一个回调线程首次使用时创建后续复用因此多 Stream 场景下只需承担一次线程创建开销。3.4 运行输出[INFO] This callback before task, result: user data is: 520. [INFO] After begin a task, launch one hostfunc. [INFO] This callback after task, result: user data is: 520. [INFO] After assigning the task, the current int is: ...输出验证了回调在 NPU 任务前后均按预期执行且用户数据520被正确传递。四、示例 1_callback_hostfunc回调的派发与执行顺序4.1 示例目标该示例单独演示 HostFunc 回调的调度语义在 Stream 上派发一个 Host 侧函数该函数在当前已派发的任务全部完成后被调用同时会阻塞后续追加的任务。这一桥接点语义使回调天然具备同步屏障能力。源码见 example/2_advanced_features/callback/1_callback_hostfunc/main.cpp核心流程如下CHECK_ERROR(aclrtCreateStream(stream)); // 默认遇错继续执行设置为遇错即停后任务出错将不再执行后续任务 CHECK_ERROR(aclrtSetStreamFailureMode(stream, ACL_STOP_ON_FAILURE)); EasyOP(blockDim, stream, numDevice); // 程序自动创建线程监听回调无需主动创建 CHECK_ERROR(aclrtLaunchHostFunc(stream, CallBackFunc, nullptr)); CHECK_ERROR(aclrtSynchronizeStream(stream));回调函数本身只需符合void (*)(void*)原型即可void CallBackFunc(void* arg) { INFO_LOG(Hostfunc callback!!!); }该示例还演示了默认流与显式创建流的差异代码中注释明确说明默认流随 Context 创建而创建接口传空即使用默认流而本例显式调用aclrtCreateStream创建独立流后再下发任务。4.2 执行顺序语义由于aclrtLaunchHostFunc将回调作为 Stream 任务队列中的一个节点插入其执行时序受 Stream 内部顺序约束排在回调之前的任务回调会在这些任务执行完成后被触发可作为流水线中的任务完成通知排在回调之后的任务会被回调阻塞直到回调函数返回后才继续下发执行可作为前置处理未完成前的闸门。4.3 运行输出[INFO] Hostfunc callback!!! [INFO] After assigning the task through the created stream, the current result is: ... [INFO] Resource cleanup completed.资源清理阶段依次调用aclrtFree、aclrtDestroyStreamForce、aclrtDestroyContext、aclrtResetDeviceForce与aclFinalize其中aclrtDestroyStreamForce用于强制销毁流并丢弃其中所有任务。五、示例 2_callback_exception异常回调与完整错误处理链5.1 能力与接口该示例是三个示例中最复杂的演示如何通过错误回调函数获取任务异常信息并在同步失败后补充查询近期错误消息、线程级最后一个错误、设备详细错误信息构成完整的 Runtime 错误处理链。核心接口分为三组回调控制与异常处理aclrtSubscribeReport/aclrtProcessReport/aclrtUnSubscribeReport、aclrtLaunchCallback、aclrtSetExceptionInfoCallback、aclrtGetThreadLastTaskId、aclrtGetTaskIdFromExceptionInfo、aclrtGetStreamIdFromExceptionInfo、aclrtGetThreadIdFromExceptionInfo、aclrtGetDeviceIdFromExceptionInfo、aclrtGetErrorCodeFromExceptionInfo、aclrtGetArgsFromExceptionInfo、aclrtGetFuncHandleFromExceptionInfo、aclrtPeekAtLastError/aclrtGetLastError、aclGetRecentErrMsg、aclrtGetErrorVerbose初始化与资源管理aclInit/aclFinalize、aclrtSetDevice/aclrtResetDeviceForce、aclrtCreateContext/aclrtDestroyContext、aclrtCreateStream/aclrtDestroyStreamForce、aclrtSetStreamFailureMode内存与数据传输aclrtMalloc/aclrtFree、aclrtMemcpy、aclrtSynchronizeStream5.2 回调线程的订阅式模型与aclrtLaunchHostFunc自动管理线程不同异常回调采用订阅-处理模型需要用户自行创建处理线程aclrtSubscribeReport(threadId, stream)将指定线程注册为指定 Stream 的回调处理线程aclrtProcessReport(timeout)由已订阅线程周期性调用等待并处理回调事件传入毫秒级超时aclrtUnSubscribeReport(threadId, stream)取消线程订阅。对应源码见 example/2_advanced_features/callback/2_callback_exception/exception_callback_sample.cpp。示例先启动一个工作线程其线程函数在循环中反复调用aclrtProcessReport处理回调直到主线程置位循环标志void ExceptionCallBackSample::ThreadFunc(void* arg) { const int waitTime 100; aclrtSetCurrentContext(context_); while (CallbackUtils::IsLoopFlag(arg)) { aclrtProcessReport(waitTime); } INFO_LOG(Thread exit); }主线程侧将std::thread的 ID 转为整型后完成订阅并注册异常回调thread td(ThreadFunc, isLoop); uint64_t tidInt std::stoull(oss.str()); CHECK_ERROR(aclrtSubscribeReport(tidInt, stream_)); CHECK_ERROR(aclrtSetExceptionInfoCallback(ExceptionCallBackFunc));注意从 include/external/acl/acl_rt.h 的头文件声明看aclrtSetExceptionInfoCallback已被标记为 deprecated建议改用新增的aclrtExceptionInfoCallbackRegister/aclrtExceptionInfoCallbackUnregister接口。5.3 构造异常场景并捕获错误示例依次下发一个正常任务EasyOP与一个刻意构造的错误任务ErrorOP错误任务会触发异常回调EasyOP(blockDim, stream_, numDevice); ErrorOP(blockDim, stream_); CHECK_ERROR(aclrtGetThreadLastTaskId(taskId)); INFO_LOG(The last task id is: %u., taskId); CHECK_ERROR(aclrtLaunchCallback(CallBackFunc, userData, ACL_CALLBACK_BLOCK, stream_));这里用到了aclrtLaunchCallback——它向 Stream 任务队列添加一个 Host 侧回调第三个参数blockType取ACL_CALLBACK_BLOCK表示阻塞类型回调枚举定义见 acl_rt.h 附近的aclrtCallbackBlockType。同时aclrtGetThreadLastTaskId用于查询当前线程最近一次派发的任务 ID。5.4 异常回调中的信息提取异常回调ExceptionCallBackFunc接收aclrtExceptionInfo*句柄从中提取任务 ID、Stream ID、线程 ID、设备 ID 与错误码并可进一步查询异常任务的内核参数与函数句柄void ExceptionCallBackSample::ExceptionCallBackFunc(aclrtExceptionInfo* exceptionInfo) { INFO_LOG(Exception occurred, callback function.); uint32_t errorMsg aclrtGetTaskIdFromExceptionInfo(exceptionInfo); INFO_LOG(The error task id is %u., errorMsg); errorMsg aclrtGetStreamIdFromExceptionInfo(exceptionInfo); INFO_LOG(The error stream id is %u., errorMsg); errorMsg aclrtGetThreadIdFromExceptionInfo(exceptionInfo); INFO_LOG(The error thread id is %u., errorMsg); errorMsg aclrtGetDeviceIdFromExceptionInfo(exceptionInfo); INFO_LOG(The error device id is %u., errorMsg); errorMsg aclrtGetErrorCodeFromExceptionInfo(exceptionInfo); INFO_LOG(The error code id is %u., errorMsg); void* devArgsPtr nullptr; uint32_t devArgsLen 0; aclError argsRet aclrtGetArgsFromExceptionInfo(exceptionInfo, devArgsPtr, devArgsLen); INFO_LOG(Exception args query ret%d, devArgsPtr%p, devArgsLen%u, ...); aclrtFuncHandle funcHandle nullptr; aclError funcRet aclrtGetFuncHandleFromExceptionInfo(exceptionInfo, funcHandle); INFO_LOG(Exception func handle query ret%d, funcHandle%p, ...); }上述异常信息查询接口aclrtGetTaskIdFromExceptionInfo等的声明均位于 include/external/acl/acl_rt.h使用方式统一传入aclrtExceptionInfo句柄返回对应字段aclrtGetArgsFromExceptionInfo与aclrtGetFuncHandleFromExceptionInfo额外通过出参返回内核参数地址、长度与函数句柄便于进一步定位失败的内核调用。5.5 同步失败后的错误链式查询在aclrtSynchronizeStream返回非ACL_SUCCESS后示例通过LogRuntimeErrorState依次查询四类错误信息构成异常回调 → 同步失败 → 主动诊断的完整闭环void LogRuntimeErrorState(int32_t deviceId, const char* stage) { aclrtErrorInfo errorInfo {}; aclError verboseRet aclrtGetErrorVerbose(deviceId, errorInfo); if (verboseRet ACL_SUCCESS) { INFO_LOG(%s verbose error info: errorType%d, tryRepair%u, hasDetail%u, stage, static_castint32_t(errorInfo.errorType), static_castuint32_t(errorInfo.tryRepair), static_castuint32_t(errorInfo.hasDetail)); } aclError peekError aclrtPeekAtLastError(ACL_RT_THREAD_LEVEL); aclError lastError aclrtGetLastError(ACL_RT_THREAD_LEVEL); const char* recentErrMsg aclGetRecentErrMsg(); ERROR_LOG(%s runtime diagnostics: peekErr%d, lastErr%d, recentErrMsg%s, ...); }各查询接口的分工如下接口查询内容声明位置aclrtGetErrorVerbose(deviceId, errorInfo)设备级详细错误返回aclrtErrorInfo结构含errorType、tryRepair、hasDetail等字段acl_rt.haclrtPeekAtLastError(level)查看线程/进程级最后一个错误不消费acl_rt.haclrtGetLastError(level)获取并消费线程/进程级最后一个错误acl_rt.haclGetRecentErrMsg()获取最近一次错误的字符串消息acl_rt.h其中ACL_RT_THREAD_LEVEL是aclrtLastErrLevel枚举中表示线程级错误的值见 acl_rt.h。peek与get的区别在于是否消费错误状态Peek只读不清除Get读取后会清除二者搭配可用于先诊断再恢复错误状态。同步失败后示例仍继续尝试aclrtMemcpy回读数据并根据返回值区分成功与失败场景分别记录日志最后通过置位isLoop退出处理线程、td.join()回收线程、aclrtUnSubscribeReport取消订阅。5.6 运行输出[INFO] Begin an easy task and an error task. The error task will trigger the exception callback. [INFO] The last task id is: ... [INFO] Exception occurred, callback function. [INFO] The error task id is ... [INFO] The error stream id is ... [INFO] The error thread id is ... [INFO] The error device id is ... [INFO] The error code id is ... [ERROR] aclrtSynchronizeStream(stream_) returned error code ... [INFO] Thread exit [INFO] Run the callback_exception sample successfully.六、遇错即停回调与错误模式的关键配合三个示例的初始化阶段都调用了aclrtSetStreamFailureMode(stream_, ACL_STOP_ON_FAILURE)。该接口设置 Stream 任务执行出错时的处理方式默认行为是遇错继续执行continue on error显式设置为ACL_STOP_ON_FAILURE值0x00000001U见 acl_rt.h后遇错即停一个任务执行失败后后续任务不再下发执行。接口声明位于 acl_rt.haclError aclrtSetStreamFailureMode(aclrtStream stream, uint64_t mode)。在异常回调示例中该设置尤为关键ErrorOP出错后流立即停止aclrtSynchronizeStream返回错误码触发后续诊断逻辑而aclrtLaunchCallback下发的回调仍会按错误语义得到处理异常回调则通过订阅线程被及时触发。理解这一配合关系有助于设计出错即停 回调上报 主动诊断的可靠错误处理框架。七、实践要点总结两类回调入口需区分aclrtLaunchHostFunc由 Runtime 内部自动创建并复用 Context 级回调线程见 api_impl.cc适合简单、无需自建线程的场景aclrtSubscribeReportaclrtProcessReport的订阅模型需要用户自建线程并周期处理报告适合需要精细控制回调处理线程的场景。回调是 Stream 上的有序节点回调排在既有任务之后执行、阻塞后续任务天然可作为任务完成通知或执行闸门多 Stream 场景下可通过订阅模型为不同 Stream 分配不同处理线程。异常信息提取接口族统一aclrtGetXxxFromExceptionInfo系列接口从aclrtExceptionInfo句柄提取任务/流/线程/设备/错误码信息配合aclrtGetArgsFromExceptionInfo、aclrtGetFuncHandleFromExceptionInfo可深挖失败内核细节。错误查询分层次、分消费语义aclGetRecentErrMsg提供字符串消息aclrtPeekAtLastError只读不消费、aclrtGetLastError读取即消费aclrtGetErrorVerbose提供设备级结构化详情errorType/tryRepair/hasDetail组合使用可还原完整错误现场。注意接口演进aclrtSetExceptionInfoCallback在头文件中已标注 deprecated新开发建议使用aclrtExceptionInfoCallbackRegister/aclrtExceptionInfoCallbackUnregister。API 声明统一以头文件为准所有接口的完整签名与枚举定义见 include/external/acl/acl_rt.h示例使用的辅助宏与公共工具见 example/utils.h 与 example/2_advanced_features/callback/callback_utils.h。【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考