- CANN
- Ascend
- 人工智能
- 任务调度
【免费下载链接】runtime
本项目提供CANN运行时组件和维测功能组件。
导读
本文聚焦 CANN Runtime 提供的 CntNotify(计数型通知)管理接口,系统讲解aclrtCntNotifyCreate、aclrtCntNotifyRecord、aclrtCntNotifyWaitWithTimeout、aclrtCntNotifyReset、aclrtCntNotifyGetId、aclrtCntNotifyDestroy六个接口的声明、参数、行为模式与适用场景,并深入仓库源码揭示其底层实现链路(ACL 封装层 → RTS 运行时 API →CountNotify内核对象)。读完本文,你将掌握如何利用计数值实现多 Stream 之间、乃至 Device 之间的复杂同步等待逻辑,并理解 CntNotify 与传统 Notify 在计数能力上的本质差异。
适用前提:本组接口目前仅在Ascend 950PR / Ascend 950DT产品上支持,其余 Atlas 系列产品与 IPV350 均不支持,开发前请先确认目标硬件平台。
一、CntNotify 概述:与 Notify 的本质区别
CntNotify(Count Notify,计数型通知)是 CANN Runtime 中用于任务同步的通知原语。官方文档(docs/zh/api_ref/09_cntNotify_management.md)明确指出:CntNotify 通常也用于 Device 与 Device 之间的状态/动作通信通知,但它是利用计数值实现任务间的同步。
它与普通 Notify 的核心区别在于计数值能力:
| 对比项 | Notify | CntNotify |
|---|---|---|
| 计数值范围 | 仅支持1 | 支持[1 ~ uint32_t 最大值] |
| 同步粒度 | 单一信号量(置位/等待) | 可基于数值比较、位运算实现多条件同步 |
| 典型场景 | Event/Notify 置位等待 | 多 Stream 计数同步、Device 间状态通知 |
由于计数值可以累加、覆盖、按位运算,CntNotify 天然适合“等待 N 次事件完成后再继续”这类需要计数语义的场景,而不仅仅是一次性的信号通知。
从类型定义看,aclrtCntNotify与aclrtNotify、aclrtStream一样是不透明句柄(见 docs/zh/api_ref/25-05_Typedefs.md 与 include/external/acl/acl_base_rt.h):
typedef void* aclrtCntNotify;开发者无需关心句柄内部结构,只需通过本组管理接口完成创建、记录、等待、复位、查询与销毁。
二、产品支持情况(务必先确认平台)
以下产品支持情况直接引用官方文档(docs/zh/api_ref/09_cntNotify_management.md)与源码中 arch5162 等平台的接口裁剪定义(src/runtime/cmake/arch5162_unsupported_acl_api.def):
| 产品 | CntNotify 支持情况 |
|---|---|
| Ascend 950PR / Ascend 950DT | ✅ 支持 |
| Atlas A3 训练系列产品 / Atlas A3 推理系列产品 | ❌ 不支持 |
| Atlas A2 训练系列产品 / Atlas A2 推理系列产品 | ❌ 不支持 |
| Atlas 200I/500 A2 推理产品 | ❌ 不支持 |
| Atlas 推理系列产品 | ❌ 不支持 |
| Atlas 训练系列产品 | ❌ 不支持 |
| IPV350 | ❌ 不支持 |
也就是说,CntNotify 目前是 Ascend 950 系列专属能力。在非 950 平台上调用这些接口将返回失败,代码中需要做好平台兼容判断或错误处理。
三、CntNotify 相关的枚举与结构体
在讲解接口前,先掌握两个关键枚举(定义于 include/external/acl/acl_rt.h,文档说明见 docs/zh/api_ref/25-02_Enumerations.md)和两个结构体(定义见 docs/zh/api_ref/25-04_Structs.md)。
3.1 Record 行为模式:aclrtCntNotifyRecordMode
typedef enum { ACL_RT_CNT_NOTIFY_RECORD_SET_VALUE_MODE = 0, // 覆盖模式,CntNotify计数值 = value ACL_RT_CNT_NOTIFY_RECORD_ADD_MODE = 1, // 累加模式,CntNotify计数值 = 当前值 + value ACL_RT_CNT_NOTIFY_RECORD_BIT_OR_MODE = 2, // bit或模式,CntNotify计数值 = 当前值 | value ACL_RT_CNT_NOTIFY_RECORD_BIT_AND_MODE = 4, // bit与模式,CntNotify计数值 = 当前值 & value } aclrtCntNotifyRecordMode;- SET_VALUE(覆盖):直接将计数值写为
value,适合对 CntNotify 重新初始化。 - ADD(累加):每次 Record 使计数值增加
value,是实现“N 次事件计数”的常用模式。 - BIT_OR / BIT_AND(位运算):按位写入或清除位,可与下方 Wait 的位掩码模式配合实现多信号位同步。
3.2 Wait 行为模式:aclrtCntNotifyWaitMode
typedef enum { ACL_RT_CNT_NOTIFY_WAIT_LESS_MODE = 0, // 当前计数值 < value,则解除Wait ACL_RT_CNT_NOTIFY_WAIT_EQUAL_MODE = 1, // 当前计数值 = value,则解除Wait ACL_RT_CNT_NOTIFY_WAIT_BIGGER_MODE = 2, // 当前计数值 > value,则解除Wait ACL_RT_CNT_NOTIFY_WAIT_BIGGER_OR_EQUAL_MODE = 3, // 当前计数值 >= value,则解除Wait ACL_RT_CNT_NOTIFY_WAIT_EQUAL_WITH_BITMASK_MODE = 4, // 当前计数值 & value = value,则解除Wait } aclrtCntNotifyWaitMode;五种模式覆盖了数值比较(小于、等于、大于、大于等于)与位掩码匹配,配合 Record 的不同写入模式,可构造出非常灵活的同步条件。
3.3 结构体:aclrtCntNotifyRecordInfo与aclrtCntNotifyWaitInfo
typedef struct { aclrtCntNotifyRecordMode mode; // Record的行为模式 uint32_t value; } aclrtCntNotifyRecordInfo;typedef struct { aclrtCntNotifyWaitMode mode; // Wait的行为模式 uint32_t value; uint32_t timeout; // 超时时间,单位是秒,其中,0表示永久等待 uint8_t isClear; // wait解除阻塞后是否将CntNotify的计数值自动清空为0,取值:1表示清空,0表示不清空 uint8_t rev[3]; // 预留字节 } aclrtCntNotifyWaitInfo;几个关键点的使用提示:
- timeout:单位为秒,
0表示永久等待。这是aclrtCntNotifyWaitWithTimeout名称中 "WithTimeout" 的由来,也是防止同步条件永远不满足时死等的关键防护。 - isClear:解除等待后是否自动清零计数值。多轮复用同一个 CntNotify 时建议置
1,避免上一轮的计数残留影响下一轮同步。 - rev[3]:预留字段,固定填 0 即可。
四、接口详解(六大管理接口)
以下六个接口按“创建 → 记录 → 等待 → 复位 → 查询 → 销毁”的生命周期顺序展开。所有接口的返回值约定一致:返回 0 表示成功,返回其他值表示失败,具体错误码含义请参见 aclError。
4.1 aclrtCntNotifyCreate:创建 CntNotify
aclError aclrtCntNotifyCreate(aclrtCntNotify *cntNotify, uint64_t flag)功能说明:创建 CntNotify。flag 为预留参数,当前必须固定配置为 0。这一点在底层实现中也有强校验——在 src/runtime/api/api_david.cc 中,rtCntNotifyCreateServer对flags != 0ULL的情况直接返回RT_ERROR_INVALID_VALUE:
COND_RETURN_EXT_ERRCODE_AND_MSG_OUTER_WITH_PARAM(flags != 0ULL, RT_ERROR_INVALID_VALUE, flags, "0");也就是说,flag传非 0 值会直接报参数非法。
参数说明:
| 参数名 | 输入/输出 | 说明 |
|---|---|---|
| cntNotify | 输出 | CntNotify 的指针,类型为 aclrtCntNotify。创建成功后,句柄由运行时分配并写出。 |
| flag | 输入 | 预留参数,当前固定配置为 0。 |
源码佐证:ACL 层封装见 src/acl/aclrt_impl/notify.cpp,内部通过rtCntNotifyCreateServer下发到 Runtime;Runtime 层再依据当前 Device 创建CountNotify内核对象,并通过driver_->NotifyIdAlloc(...)向驱动申请物理 notify ID(见 src/runtime/feature/cntnotify/count_notify.cc)。若申请不到 notify 资源,会携带错误码EE1023(“Too many CntNotify objects are created”),即创建的 CntNotify 数量过多导致资源耗尽,可参考 docs/zh/FAQ/EE1023资源不足问题.md 排查。
4.2 aclrtCntNotifyRecord:在指定 Stream 上记录 CntNotify(异步)
aclError aclrtCntNotifyRecord(aclrtCntNotify cntNotify, aclrtStream stream, aclrtCntNotifyRecordInfo *info)功能说明:在指定 Stream 上记录一个 CntNotify,异步接口。aclrtCntNotifyRecord与aclrtCntNotifyWaitWithTimeout配合使用时,主要用于多 Stream 之间同步等待的场景。
参数说明:
| 参数名 | 输入/输出 | 说明 |
|---|---|---|
| cntNotify | 输入 | 需记录的 CntNotify,类型见 aclrtCntNotify。 |
| stream | 输入 | 指定 Stream,类型见 aclrtStream。使用默认 Stream 时填NULL。多 Stream 同步等待场景下(例如 Stream2 等 Stream1),此处配置为Stream1(即“记录发生在哪个 Stream 上”)。 |
| info | 输入 | 控制 Record 的行为模式,见 aclrtCntNotifyRecordInfo。 |
源码佐证:在 src/runtime/feature/cntnotify/count_notify.cc 中,CountNotify::Record在指定 Stream 上分配TS_TASK_TYPE_NOTIFY_RECORD类型的任务,调用NotifyRecordTaskInit初始化后经DavidSendTask异步下发。注意实现中info指针不能为NULL,ACL 层通过ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(info)做了非空校验(见 src/acl/aclrt_impl/notify.cpp),传空指针会直接报输入参数错误。
4.3 aclrtCntNotifyWaitWithTimeout:阻塞 Stream 等待 CntNotify 完成(异步)
aclError aclrtCntNotifyWaitWithTimeout(aclrtCntNotify cntNotify, aclrtStream stream, aclrtCntNotifyWaitInfo *info)功能说明:阻塞指定 Stream 的运行,直到指定的 CntNotify 满足等待条件,异步接口(阻塞的是 Stream 上的后续任务,而非调用线程)。
参数说明:
| 参数名 | 输入/输出 | 说明 |
|---|---|---|
| cntNotify | 输入 | 需等待的 CntNotify,类型见 aclrtCntNotify。 |
| stream | 输入 | 指定 Stream,类型见 aclrtStream。使用默认 Stream 时填NULL。多 Stream 同步等待场景下(例如 Stream2 等 Stream1),此处配置为Stream2(即“哪个 Stream 被阻塞等待”)。 |
| info | 输入 | 控制 Wait 的行为模式,见 aclrtCntNotifyWaitInfo。 |
使用要点:
- 等待条件由
info->mode+info->value共同决定(小于/等于/大于/大于等于/位掩码匹配)。 info->timeout单位秒,0表示永久等待;建议生产环境配置合理超时值,避免条件永不满足时任务永久挂起。info->isClear决定解除等待后是否自动清零,多轮复用场景建议置1。- 该接口同样对
info做非空校验,见 src/acl/aclrt_impl/notify.cpp。
4.4 aclrtCntNotifyReset:复位 CntNotify(异步)
aclError aclrtCntNotifyReset(aclrtCntNotify cntNotify, aclrtStream stream)功能说明:复位一个 CntNotify,将其计数值清空为 0,异步接口。
参数说明:
| 参数名 | 输入/输出 | 说明 |
|---|---|---|
| cntNotify | 输入 | 待复位的 CntNotify,类型见 aclrtCntNotify。 |
| stream | 输入 | 指定 Stream,类型见 aclrtStream。使用默认 Stream 时填NULL。 |
使用要点:与WaitInfo.isClear的“等待解除后自动清零”不同,Reset是显式、主动的复位动作,适用于在同步周期开始时将计数值归零、重新开始一轮计数。多轮流水线场景建议在每轮开始前调用 Reset,确保计数起点一致。
4.5 aclrtCntNotifyGetId:获取 CntNotify 的 ID
aclError aclrtCntNotifyGetId(aclrtCntNotify cntNotify, uint32_t *notifyId)功能说明:获取 CntNotify 的 ID。该 ID 可用于日志打点、profiling 关联、跨模块传递标识等场景。
参数说明:
| 参数名 | 输入/输出 | 说明 |
|---|---|---|
| cntNotify | 输入 | 待获取的 CntNotify,类型见 aclrtCntNotify。 |
| notifyId | 输出 | CntNotify ID,uint32 类型。 |
源码佐证:CountNotify内核对象持有notifyid_成员(见 src/runtime/feature/cntnotify/count_notify.hpp),GetCntNotifyId()直接返回该 ID;Runtime 层rtGetCntNotifyId通过Api::Instance()->GetCntNotifyId(...)实现(见 src/runtime/api/api_david.cc)。
4.6 aclrtCntNotifyDestroy:销毁 CntNotify
aclError aclrtCntNotifyDestroy(aclrtCntNotify cntNotify)功能说明:销毁 CntNotify,释放其占用的驱动 notify 资源。
参数说明:
| 参数名 | 输入/输出 | 说明 |
|---|---|---|
| cntNotify | 输入 | 待销毁的 CntNotify,类型见 aclrtCntNotify。 |
使用要点:销毁后该句柄不可再用于任何 Record/Wait 等操作。程序退出前应确保所有 CntNotify 均被销毁,否则可能造成 notify 资源泄漏,后续进程创建 CntNotify 时会因资源不足返回 EE1023 类错误。底层CountNotify析构时会将自身从当前 Device 的 CntNotify 列表移除,并调用driver_->NotifyIdFree(...)归还 notify ID(见 src/runtime/feature/cntnotify/count_notify.cc)。
五、典型用法:多 Stream 计数同步
官方文档明确指出,Record 与 WaitWithTimeout 配合是“多 Stream 之间同步等待”的标准用法。下面给出“Stream1 上完成一次记录、Stream2 上等待其完成”的完整流程骨架:
aclrtCntNotify cntNotify = nullptr; aclrtStream stream1 = nullptr; // 记录方 aclrtStream stream2 = nullptr; // 等待方 aclrtCntNotifyRecordInfo recInfo = {}; aclrtCntNotifyWaitInfo waitInfo = {}; // 1. 创建 CntNotify(flag 固定为 0) aclError ret = aclrtCntNotifyCreate(&cntNotify, 0); if (ret != 0) { /* 处理错误 */ } // 2. 创建两个 Stream ret = aclrtCreateStream(&stream1); ret = aclrtCreateStream(&stream2); // 3. 在 Stream1 上记录一次计数:累加模式,每次 +1 recInfo.mode = ACL_RT_CNT_NOTIFY_RECORD_ADD_MODE; recInfo.value = 1; ret = aclrtCntNotifyRecord(cntNotify, stream1, &recInfo); // 4. 在 Stream2 上等待计数值 >= 1;超时 10 秒,解除后自动清零 waitInfo.mode = ACL_RT_CNT_NOTIFY_WAIT_BIGGER_OR_EQUAL_MODE; waitInfo.value = 1; waitInfo.timeout = 10; // 0 表示永久等待 waitInfo.isClear = 1; // 1 表示解除后清空计数值 ret = aclrtCntNotifyWaitWithTimeout(cntNotify, stream2, &waitInfo); // 5. 查询 ID(用于日志/profiling 关联) uint32_t notifyId = 0; ret = aclrtCntNotifyGetId(cntNotify, ¬ifyId); // 6. 复位并复用(可选) ret = aclrtCntNotifyReset(cntNotify, stream2); // 7. 销毁 ret = aclrtCntNotifyDestroy(cntNotify);关键参数记忆法(官方文档原话逻辑):
- Record 的
stream是“被记录事件发生的 Stream”——Stream2 等 Stream1 时填Stream1; - Wait 的
stream是“被阻塞的 Stream”——Stream2 等 Stream1 时填Stream2; - 使用默认 Stream 时,
stream均填NULL。
如果需要对“N 个生产者 Stream 都完成一轮任务后再继续”,可以让每个生产者 Stream 各执行一次ACL_RT_CNT_NOTIFY_RECORD_ADD_MODE的 Record,等待方在目标 Stream 上使用BIGGER_OR_EQUAL模式等待计数值达到 N,这正是普通 Notify 无法表达的计数同步语义。
六、底层实现链路与测试验证
6.1 调用链全景
从调用方到内核对象的完整链路如下:
应用代码 │ aclrtCntNotifyCreate / Record / WaitWithTimeout / Reset / GetId / Destroy ▼ ACL 封装层(src/acl/aclrt_impl/notify.cpp) │ ACL_PROFILING_REG 注册 profiling 埋点 + ACL_LOG_INFO 日志 │ 参数非空校验(ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT) ▼ RTS 运行时 API 层(src/runtime/api/api_david.cc) │ GLOBAL_STATE_WAIT_IF_LOCKED() 全局状态门控 │ RT_VALIDATE_AND_UNWRAP_OBJECT 句柄解包 ▼ CountNotify 内核对象(src/runtime/feature/cntnotify/count_notify.cc) │ Setup(): NotifyIdAlloc 申请驱动 notify ID │ Record(): 分配 TS_TASK_TYPE_NOTIFY_RECORD 任务并异步下发 │ Wait(): 分配 TS_TASK_TYPE_NOTIFY_WAIT 任务并异步下发 ▼ 驱动层(NpuDriver::GetDevResAddress / NotifyIdAlloc / NotifyIdFree)值得注意的细节:
- 所有接口都经过全局状态门控(
GLOBAL_STATE_WAIT_IF_LOCKED()),保证运行时初始化/去初始化期间的调用安全。 - 句柄解包校验:
RT_VALIDATE_AND_UNWRAP_OBJECT会将用户传入的aclrtCntNotify句柄安全转换为内部的CountNotify*,非法句柄会直接报错而不是产生空指针崩溃。 - 任务类型分离:Record 与 Wait 分别对应
TS_TASK_TYPE_NOTIFY_RECORD和TS_TASK_TYPE_NOTIFY_WAIT两种任务,任务信息结构体定义于 src/runtime/core/inc/task/task_info_struct.hpp,其中isCountNotify字段用于区分计数型通知与普通事件记录/等待。
6.2 资源类型与计数值语义
CountNotify::GetCntNotifyAddress(见 src/runtime/feature/cntnotify/count_notify.cc)揭示了内部资源划分:根据通知类型映射到不同的驱动资源,包括RT_RES_TYPE_STARS_CNT_NOTIFY_RECORD(记录)、RT_RES_TYPE_STARS_CNT_NOTIFY_ADD(累加)、RT_RES_TYPE_STARS_CNT_NOTIFY_BIT_WR(位写)、RT_RES_TYPE_STARS_CNT_NOTIFY_BIT_CLR(位清),分别对应覆盖、累加、bit 或、bit 与四种 Record 模式的底层硬件切片。
6.3 测试用例验证
仓库 UT 测试对六个接口均有覆盖(见 tests/ut/acl/testcase/acl_runtime_unittest.cpp 与 tests/ut/acl/testcase/acl_runtime_unittest.cpp),典型用例包括:
aclrtCntNotifyCreate/aclrtCntNotifyDestroy:验证创建与销毁调用链及 mock 转发;aclrtCntNotifyRecord:以{ACL_RT_CNT_NOTIFY_RECORD_SET_VALUE_MODE, 0}构造 RecordInfo,并验证info传nullptr时返回参数错误;aclrtCntNotifyWaitWithTimeout:以{ACL_RT_CNT_NOTIFY_WAIT_LESS_MODE, 0, 0, true, 0}(永久等待 + 解除后清零)构造 WaitInfo,并覆盖空指针校验;aclrtCntNotifyReset/aclrtCntNotifyGetId:验证复位与 ID 查询(含notifyId空指针校验)。
此外,运行时侧在 950 平台测试中有专门的rt_utest_david_event.cc、rt_utest_david_stream.cc等用例(见 tests/ut/runtime/runtime/test/platform/950/ 目录),进一步印证该功能主要面向 David 架构(950 系列)实现,与文档中“仅 Ascend 950 支持”的说明一致。
七、使用建议与注意事项
- 平台前置检查:CntNotify 目前仅 Ascend 950PR/950DT 支持。在多平台复用的代码中,建议先用平台查询接口确认设备能力,再决定是否走 CntNotify 同步路径,避免在其他 Atlas 产品上运行时失败。
- flag 必须为 0:
aclrtCntNotifyCreate的flag为预留参数,传非 0 值会被底层直接判定为非法参数(见 src/runtime/api/api_david.cc)。 - info 不能为空:Record 与 WaitWithTimeout 的
info指针均做了非空校验,使用前务必初始化结构体(建议= {}清零)。 - 善用 timeout 与 isClear:Wait 的
timeout建议配置有限值防止死等;isClear在多轮复用场景置 1 可免去手动 Reset,但也要注意“清零时机在解除等待之后”,若后续还有依赖旧计数值的逻辑需谨慎。 - 生命周期管理:CntNotify 使用完毕后必须
Destroy,否则占用驱动 notify 资源,持续创建会触发 EE1023 资源不足错误;复用时注意在每轮同步周期开始前Reset归零计数值。 - 异步语义:Record、WaitWithTimeout、Reset 均为异步接口,它们阻塞的是 Stream 上的任务调度而非调用线程,理解这一点有助于写出不阻塞 Host 的高吞吐流水线。
参考资源
- 接口管理文档:docs/zh/api_ref/09_cntNotify_management.md
- 枚举定义:docs/zh/api_ref/25-02_Enumerations.md(
aclrtCntNotifyRecordMode、aclrtCntNotifyWaitMode) - 结构体定义:docs/zh/api_ref/25-04_Structs.md(
aclrtCntNotifyRecordInfo、aclrtCntNotifyWaitInfo) - 类型定义:docs/zh/api_ref/25-05_Typedefs.md(
aclrtCntNotify) - 错误码说明:docs/zh/api_ref/25-01_aclError.md
- ACL 头文件声明:include/external/acl/acl_rt.h
- ACL 封装实现:src/acl/aclrt_impl/notify.cpp
- RTS 运行时 API:src/runtime/api/api_david.cc
- 内核对象实现:src/runtime/feature/cntnotify/count_notify.cc 与 count_notify.hpp
- UT 测试用例:tests/ut/acl/testcase/acl_runtime_unittest.cpp
- CANN
- Ascend
- 人工智能
- 任务调度
【免费下载链接】runtime
本项目提供CANN运行时组件和维测功能组件。
相关推荐
CANN Runtime Stream管理接口详解:创建、同步、销毁与高级特性
CANN Runtime Stream管理接口详解:创建、同步、销毁与高级特性 CANN(Compute Architecture for Neural Net
CANNAscend人工智能任务调度CANN opbase 中 aclDestroyTensor 接口详解:aclTensor 的创建与销毁生命周期管理
CANN opbase 中 aclDestroyTensor 接口详解:aclTensor 的创建与销毁生命周期管理 导读 在 CANN 算子库基础框架库 op
人工智能算子库CANNAscendCANN Runtime Event管理接口完全指南:创建、记录、同步、计时与IPC跨进程共享
CANN Runtime Event管理接口完全指南:创建、记录、同步、计时与IPC跨进程共享 Event(事件)是 CANN Runtime 中用于 任务同步
CANNAscend人工智能任务调度
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考