CANN Runtime 可靠性能力实战:Stream 级浮点溢出检测与错误恢复、容错执行专题
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
本文围绕 CANN Runtime 的可靠性能力展开,以仓库 example/4_reliability 目录为核心线索,深入讲解 Runtime 在溢出检测、错误恢复与容错执行三个维度的能力设计与实践用法。读者将学会如何使用 AscendCL 的 Device 饱和模式管理与 Stream 级溢出检测 API 实现"开关配置—状态查询—状态复位"的完整闭环,并理解如何在异常发生后通过日志诊断、错误码分类与资源重建实现可靠恢复。
Runtime 可靠性能力全景
Runtime 的可靠性能力覆盖程序运行期可能遇到的各类异常场景,仓库将其划分为三个专题(见 example/4_reliability/README_en.md):
| 专题 | 目录 | 核心能力 |
|---|---|---|
| 溢出检测 | overflow_detection | Stream 级溢出检测开关、状态查询与复位(含可运行示例) |
| 错误恢复 | error_recovery | 运行期错误后的恢复、重试与重新初始化 |
| 容错执行 | fault_tolerant | 容错执行、故障隔离与多设备/多进程场景下的容错设计 |
其中,溢出检测是唯一提供完整可编译示例的专题,位于 example/4_reliability/overflow_detection/0_overflow_detection,下文以它为主体展开;错误恢复与容错执行作为相关专题在第 6、7 节给出方向性指南。
溢出检测机制:为什么要管理浮点溢出模式
浮点运算出现上溢、下溢或非法结果(如除以零产生 Inf/NaN)时,不同的处理策略直接影响计算结果的可信度与后续执行的稳定性。AscendCL 通过aclrtFloatOverflowMode枚举(定义于 include/external/acl/acl_rt.h)暴露两种显式模式:
typedef enum aclrtFloatOverflowMode { ACL_RT_OVERFLOW_MODE_SATURATION = 0, // 饱和模式:运算结果钳制到最大/最小可表示值 ACL_RT_OVERFLOW_MODE_INFNAN, // Inf/NaN 模式:溢出结果以 Inf/NaN 形式传播 ACL_RT_OVERFLOW_MODE_UNDEF, // 未定义模式(通常作为查询的初始值) } aclrtFloatOverflowMode;- 饱和模式(SATURATION):溢出时结果被钳制到该数据类型的最大(或最小)可表示值,避免无效值在后续运算中扩散,适合对结果范围敏感、希望"数值不出界"的场景;
- Inf/NaN 模式(INFNAN):溢出时保留 Inf/NaN 语义继续传播,适合需要精确追踪异常来源、配合溢出检测做问题定位的场景。
溢出检测开关依赖饱和模式工作,因此示例的执行流程是:先查询当前 Device 的饱和模式,切换到ACL_RT_OVERFLOW_MODE_SATURATION,再使能 Stream 级溢出检测,最后查询并复位溢出状态。
溢出检测核心 API 参考
以下接口均声明于 include/external/acl/acl_rt.h,是溢出检测功能的主干:
| API | 参数 | 说明 |
|---|---|---|
aclrtGetDeviceSatMode | aclrtFloatOverflowMode* mode(OUT) | 查询当前 Device 的饱和模式 |
aclrtSetDeviceSatMode | aclrtFloatOverflowMode mode(IN) | 设置 Device 饱和模式 |
aclrtSetStreamOverflowSwitch | aclrtStream stream(IN)、uint32_t flag(IN,0 关闭 / 1 开启) | 使能或关闭指定 Stream 上的溢出检测开关 |
aclrtGetStreamOverflowSwitch | aclrtStream stream(IN)、uint32_t* flag(OUT,0 关闭,非 0 开启) | 查询指定 Stream 的溢出检测开关状态 |
aclrtGetOverflowStatus | void* outputAddr(IN/OUT,设备侧状态缓冲区)、size_t outputSize(IN)、aclrtStream stream(IN) | 异步获取 Stream 的溢出状态 |
aclrtResetOverflowStatus | aclrtStream stream(IN) | 异步复位 Stream 的溢出状态 |
关于aclrtGetOverflowStatus与aclrtResetOverflowStatus,头文件注释明确要求:二者均为异步接口,调用后必须调用aclrtSynchronizeStream确保 Stream 中的任务执行完成,再读取或继续后续操作。这一点在示例源码中被严格遵守(见 main.cpp)。
此外,aclrtFloatOverflowMode与 Stream 属性也存在关联:ACL_STREAM_ATTR_FLOAT_OVERFLOW_CHECK(枚举值 2,定义于 include/external/acl/acl_rt.h)即流属性体系中与浮点溢出检查对应的属性类型,可通过aclrtSetStreamAttr/aclrtGetStreamAttr体系访问。
示例源码逐步解析
示例入口为 example/4_reliability/overflow_detection/0_overflow_detection/main.cpp,完整流程如下。
第 1 步:环境初始化
依次调用aclInit(nullptr)完成 ACL 初始化、aclrtSetDevice(0)设置 Device、aclrtCreateContext(&context, 0)创建 Context。示例采用 Lambda 表达式封装主体逻辑,并用多个布尔标志(aclInitialized、deviceSet、contextCreated等)跟踪每一步的资源状态,保证任何一步失败后都能精确执行对应的清理动作。
第 2 步:查询并切换饱和模式
aclrtGetDeviceSatMode(&originalMode); // 记录原始模式,供结束时恢复 aclrtSetDeviceSatMode(ACL_RT_OVERFLOW_MODE_SATURATION); aclrtGetDeviceSatMode(¤tMode); // 回读确认 INFO_LOG("Device saturation mode switched from %s to %s.", OverflowModeToString(originalMode), OverflowModeToString(currentMode));OverflowModeToString将枚举值映射为可读字符串(ACL_RT_OVERFLOW_MODE_SATURATION/ACL_RT_OVERFLOW_MODE_INFNAN/ACL_RT_OVERFLOW_MODE_UNDEF/ACL_RT_OVERFLOW_MODE_UNKNOWN),对应示例输出中的第一行日志。
第 3 步:创建 Stream 并使能溢出检测
aclrtCreateStream(&stream); aclrtSetStreamOverflowSwitch(stream, 1); // 使能溢出检测 aclrtGetStreamOverflowSwitch(stream, &queriedSwitch); INFO_LOG("Overflow switch=%u", queriedSwitch); // 期望输出 1第 4 步:查询溢出状态并同步到 Host
constexpr size_t kOverflowStatusBufferSize = 64; // 固定 64 字节设备侧状态缓冲 aclrtMalloc(&statusDevice, kOverflowStatusBufferSize, ACL_MEM_MALLOC_HUGE_FIRST); aclrtGetOverflowStatus(statusDevice, kOverflowStatusBufferSize, stream); // 异步查询 aclrtSynchronizeStream(stream); // 等待完成 aclrtMemcpy(statusHost, sizeof(statusHost), statusDevice, kOverflowStatusBufferSize, ACL_MEMCPY_DEVICE_TO_HOST); // 同步回 Host overflowFlag = ReadOverflowFlag(statusHost); // 读取前 4 字节 INFO_LOG("Overflow status before reset=%u", overflowFlag);ReadOverflowFlag通过std::copy_n将缓冲区前 4 个字节按小端序还原为uint32_t溢出标志。在未发生溢出的正常运行场景下该值为 0。
第 5 步:复位并二次查询
aclrtResetOverflowStatus(stream); aclrtSynchronizeStream(stream); std::fill_n(statusHost, kOverflowStatusBufferSize, 0); // 清空 Host 侧缓冲 aclrtGetOverflowStatus(statusDevice, kOverflowStatusBufferSize, stream); aclrtSynchronizeStream(stream); aclrtMemcpy(statusHost, sizeof(statusHost), statusDevice, kOverflowStatusBufferSize, ACL_MEMCPY_DEVICE_TO_HOST); overflowFlag = ReadOverflowFlag(statusHost); INFO_LOG("Overflow status after reset=%u", overflowFlag);第 6 步:资源清理与模式恢复
无论主体逻辑成功还是失败,示例都会逆序执行清理:aclrtFree(statusDevice)释放状态缓冲 →aclrtSetDeviceSatMode(originalMode)恢复原始饱和模式 →aclrtDestroyStream→aclrtDestroyContext→aclrtResetDeviceForce(deviceId)→aclrtFinalize()。每个清理调用都单独检查返回值,任一失败都会将最终结果置为 -1。这种"标志位驱动的分级回滚"是 Runtime 示例中通用的资源管理范式,与 example/utils.h 中CHECK_ERROR/CHECK_ERROR_WITHOUT_RETURN宏配合使用。
构建与运行
环境准备
按 example/README_en.md 完成 CANN 安装后,按以下步骤执行(默认安装目录为/usr/local/Ascend):
# 将 ${install_root} 替换为 CANN 安装根目录 source ${install_root}/cann/set_env.sh export ASCEND_INSTALL_PATH=${install_root}/cann # 构建并运行 bash run.sh构建细节
run.sh 内部通过source $_ASCEND_INSTALL_PATH/bin/setenv.bash加载编译环境,然后执行 CMake 配置与make,最终直接运行./build/main。
CMakeLists.txt 的关键点:
include_directories同时加入${ASCEND_CANN_PACKAGE_PATH}/include与示例公共目录(../../..,即 example 根目录,用于引入 example/utils.h);- 编译选项
-O2 -std=c++17 -D_GLIBCXX_USE_CXX11_ABI=0 -Wall -Werror,其中-D_GLIBCXX_USE_CXX11_ABI=0是 CANN 常见的要求,用于与安装包的 ABI 保持一致; - 链接
${ASCEND_CANN_PACKAGE_PATH}/lib64/libascendcl.so。
预期输出
[INFO] Device saturation mode switched from ACL_RT_OVERFLOW_MODE_INFNAN to ACL_RT_OVERFLOW_MODE_SATURATION. [INFO] Overflow switch=1 [INFO] Overflow status before reset=0 [INFO] Overflow status after reset=0 [INFO] Overflow detection sample finished successfully.当未发生浮点溢出时,复位前后的溢出状态均为 0。若要观察非 0 的溢出状态,可在使能溢出检测后在 Stream 上启动会产生浮点溢出的算子,再执行查询。
产品支持与已知问题
| 产品 | 支持情况 |
|---|---|
| Ascend 950PR / Ascend 950DT | 支持 |
| Atlas A3 训练系列产品 / Atlas A3 推理系列产品 | 支持 |
| Atlas A2 训练系列产品 / Atlas A2 推理系列产品 | 支持 |
已知问题:aclrtSetStreamOverflowSwitch在ACL_RT_OVERFLOW_MODE_SATURATION与ACL_RT_OVERFLOW_MODE_INFNAN两种模式下均可使用;若当前产品不支持该能力,相关接口可能返回ACL_ERROR_RT_FEATURE_NOT_SUPPORT(错误码 207000,参见 include/external/acl/error_codes/rt_error_codes.h)。示例中的HandleOptionalOverflowRet正是为此设计的:对可选能力接口(饱和模式查询/设置、溢出开关)进行"探测式调用",收到ACL_ERROR_RT_FEATURE_NOT_SUPPORT时打印警告并以 0 退出,收到其他错误码则按致命错误处理,同时保证资源已清理完毕。这种"可选能力降级"写法值得在跨版本、跨产品的应用中复用。
相关专题一:错误恢复与重试
example/4_reliability/error_recovery/README_en.md 给出了错误恢复专题的建议研究路线:
- 故障后的资源重建与恢复流程:明确哪些资源(Context、Stream、Device 内存)在异常后需要销毁重建,哪些可以保留复用;
- 错误分类、降级与重试策略:将错误码按可重试(如瞬时性失败)与不可重试(如参数非法、资源耗尽)分类,决定是降级运行还是重试;
- 结合日志与诊断信息的排障方法:借助 Runtime 错误码与 plog 日志定位设备侧异常根因。
基础错误处理范式的可运行示例见 example/0_quickstart/1_error_handling,其中演示了通过aclGetRecentErrMsg获取最近一次错误的详细描述并结合错误码分支处理的写法,可视为错误恢复专题的入门素材。
相关专题二:容错执行与故障隔离
example/4_reliability/fault_tolerant/README_en.md 聚焦以下方向:
- 任务失败后的隔离与恢复:单个算子/模型任务失败时,如何将故障限制在局部而不影响同进程内的其他任务;
- 多设备或多进程场景下的容错设计:跨设备、跨进程协作时,某一方失败后的状态一致性与同步策略;
- 诊断、重试与降级的协同设计:将检测到的问题按严重程度分层处理,形成完整的可靠性闭环。
该专题与错误恢复专题相互衔接(错误恢复解决"失败后怎么办",容错解决"如何设计得不那么容易失败、失败后如何隔离")。
参考资料
- example/4_reliability/README_en.md:可靠性能力示例总览
- example/4_reliability/overflow_detection/0_overflow_detection/README_en.md:溢出检测示例说明
- example/4_reliability/overflow_detection/0_overflow_detection/main.cpp:溢出检测示例源码
- include/external/acl/acl_rt.h:溢出检测相关 API 与枚举定义
- include/external/acl/error_codes/rt_error_codes.h:Runtime 错误码定义
- example/utils.h:示例公共错误处理宏
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考