CANN Runtime CMO 缓存操作接口解析:aclrtCmoAsync 系列 API 使用与实现原理

📅 发布时间:2026/9/19 1:46:28
CANN Runtime CMO 缓存操作接口解析:aclrtCmoAsync 系列 API 使用与实现原理
CANN Runtime CMO 缓存操作接口解析aclrtCmoAsync 系列 API 使用与实现原理【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtimeCMOCache Maintenance Operations缓存维护操作是 CANN Runtime 提供的 Device 侧缓存刷新与失效接口族用于在 Host 侧对 NPU 上的 Cache 与 DDR 内存做显式的预取、写回、失效等控制。本文以 11-06_CMO_memory_operation.md 为骨架完整梳理aclrtCmoAsync、aclrtCmoAsyncWithBarrier、aclrtCmoWaitBarrier、aclrtCmoGetDescSize、aclrtCmoSetDesc、aclrtCmoAsyncWithDesc六个接口的产品支持情况、参数约束与典型用法并结合本仓库源码acl_rt.h、api_c_memory.cc、api_c_standard_soc.cc剖析其底层校验逻辑与任务下发链路帮助开发者正确、安全地使用 CMO 缓存操作。一、CMO 缓存操作是什么在异构计算场景中数据在 Device 侧 Cache 与 DDR 内存之间的流动并不总是由硬件自动维护。某些场景下开发者需要主动控制缓存行为以提升访存效率预取Prefetch提前把内存数据加载到 Cache减少后续算子访问时的等待写回Writeback把 Cache 中的脏数据刷回内存同时保留 Cache 副本失效Invalid丢弃 Cache 中的数据强制后续访问从内存重新加载冲刷Flush把 Cache 中的数据刷回内存但不保留 Cache 副本。CANN Runtime 将这类操作抽象为CMOCache Maintenance Operations并以aclrtCmo*系列异步接口向用户开放。除aclrtCmoAsync直接指定内存地址外该接口族还提供基于内存描述符Descriptor的进阶用法以及携带 barrierId 的屏障式同步用法满足从简单预取到精细化缓存一致性控制的多种需求。CMO 操作类型通过枚举 aclrtCmoType 表达其定义位于 include/external/acl/acl_rt.htypedef enum aclrtCmoType { ACL_RT_CMO_TYPE_PREFETCH 0, // 内存预取从内存预取到Cache ACL_RT_CMO_TYPE_WRITEBACK, // 把Cache中的数据刷新到内存中并在Cache中保留副本 ACL_RT_CMO_TYPE_INVALID, // 丢弃Cache中的数据 ACL_RT_CMO_TYPE_FLUSH, // 把Cache中的数据刷新到内存中不保留Cache中的副本 } aclrtCmoType;二、接口总览与产品支持矩阵CMO 接口族共包含 6 个接口按能力可分为三类接口功能定位异步aclrtCmoAsync直接指定地址/大小的 Cache 内存操作是aclrtCmoAsyncWithBarrier携带 barrierId 的 Cache 内存操作是aclrtCmoWaitBarrier等待指定 barrierId 的 Invalid 任务完成是aclrtCmoGetDescSize获取 Cache 内存描述符占用大小—aclrtCmoSetDesc将源地址/大小写入内存描述符—aclrtCmoAsyncWithDesc基于内存描述符的 Cache 内存操作是各接口的产品支持情况差异显著汇总如下对应各接口产品支持情况小节产品系列CmoAsyncCmoAsyncWithBarrierCmoWaitBarrierCmoGetDescSize / CmoSetDesc / CmoAsyncWithDescAscend 950PR / Ascend 950DT支持不支持不支持支持Atlas A3 训练系列 / 推理系列支持不支持不支持支持Atlas A2 训练系列 / 推理系列支持不支持不支持支持Atlas 200I/500 A2 推理产品不支持支持支持不支持Atlas 推理系列产品不支持不支持不支持不支持Atlas 训练系列产品不支持不支持不支持不支持IPV350不支持不支持不支持不支持从源码可以印证这一产品分流的设计rtsCmoAsyncWithBarrier在入口处即检查芯片特性位RT_FEATURE_TASK_ASYNC_CMO不满足则直接返回RT_ERROR_FEATURE_NOT_SUPPORT见 api_c_memory.cc而rtsLaunchBarrierTask的注释同样明确only CHIP_MINI_V3, CHIP_AS31XM1 and cmoTypeinvalid support this function见 api_c_memory.cc与文档中仅 Atlas 200I/500 A2 推理产品支持带 Barrier 的 CMO 接口保持一致。三、接口详解3.1 aclrtCmoAsync直接 Cache 内存操作aclError aclrtCmoAsync(void *src, size_t size, aclrtCmoType cmoType, aclrtStream stream)功能说明在指定 Stream 上实现 Device 侧 Cache 内存操作异步接口。参数说明参数名输入/输出说明src输入待操作的 Device 内存地址。只支持本 Device 上的 Cache 内存操作。size输入待操作的 Device 内存大小单位 Byte。cmoType输入Cache 内存操作类型类型定义参见 aclrtCmoType。当前仅支持ACL_RT_CMO_TYPE_PREFETCH内存预取。stream输入执行内存操作任务的 Stream类型定义参见 aclrtStream。返回值说明返回 0 表示成功返回其他值表示失败错误码参见 aclError。实现要点该接口直接透传到底层运行时接口rtCmoAsync。从 api_c_standard_soc.cc 可以看到rtCmoAsync会把入参封装为rtCmoTaskInfo_t记录 opCode、lengthInner、sourceAddr、logicId 等再调用rtCmoTaskLaunch下发任务rtCmoTaskLaunch最终经Api::Instance()-CmoTaskLaunch走完任务提交链路。典型调用示例如下// 假设已在当前线程设置好 Device 并持有合法 Stream void *devPtr nullptr; size_t size 1024 * 1024; // 1MB // 申请 Device 内存接口参见 aclrtMalloc见 11-01 章节 aclrtMalloc(devPtr, size, ACL_MEM_MALLOC_HUGE_FIRST); aclrtStream stream nullptr; aclrtCreateStream(stream); // 将 1MB Device 内存预取到 Cache异步下发 aclError ret aclrtCmoAsync(devPtr, size, ACL_RT_CMO_TYPE_PREFETCH, stream); if (ret ! 0) { // 错误处理可调用 aclGetRecentErrMsg 获取最近一次错误信息 } aclrtDestroyStream(stream); aclrtFree(devPtr);3.2 aclrtCmoAsyncWithBarrier携带屏障标识的 Cache 内存操作aclError aclrtCmoAsyncWithBarrier(void *src, size_t size, aclrtCmoType cmoType, uint32_t barrierId, aclrtStream stream)功能说明在指定 Stream 上实现 Device 侧 Cache 内存操作同时携带barrierIdbarrierId表示 Cache 内存操作的屏障标识。异步接口。参数说明参数名输入/输出说明src输入待操作的 Device 内存地址。只支持本 Device 上的 Cache 内存操作。size输入待操作的 Device 内存大小单位 Byte。cmoType输入Cache 内存操作类型类型定义参见 aclrtCmoType。barrierId输入屏障标识。当cmoType为ACL_RT_CMO_TYPE_INVALID时有效支持传入大于 0 的数字配合 aclrtCmoWaitBarrier 使用等待具有指定 barrierId 的 Invalid 内存操作任务执行完成当cmoType为其他值时barrierId固定传 0。stream输入执行内存操作任务的 Stream。此处只支持与模型绑定过的 Stream绑定模型与 Stream 需调用 aclmdlRIBindStream 接口。返回值说明返回 0 表示成功返回其他值表示失败错误码参见 aclError。实现要点rtsCmoAsyncWithBarrier对参数组合做了严格校验见 api_c_memory.cccmoType为RT_CMO_INVALID时logicId即 barrierId不能为 0否则返回RT_ERROR_INVALID_VALUE并报出错误码 EE1011提示 If parameter cmoType is equal to RT_CMO_INVALID, the value of parameter logicId cannot be 0cmoType为RT_CMO_PREFETCH、RT_CMO_WRITEBACK或RT_CMO_FLUSH时logicId必须为 0其余未知类型直接返回不支持。此外接口还要求src非空、size大于 0校验通过后封装rtCmoTaskInfo_t并经rtCmoTaskLaunch下发。3.3 aclrtCmoWaitBarrier等待屏障任务完成aclError aclrtCmoWaitBarrier(aclrtBarrierTaskInfo *taskInfo, aclrtStream stream, uint32_t flag)功能说明等待具有指定 barrierId 的 Invalid 内存操作任务执行完成。异步接口。参数说明参数名输入/输出说明taskInfo输入Cache 内存操作的任务信息类型定义参见 aclrtBarrierTaskInfo。任务信息中的cmoType当前仅支持ACL_RT_CMO_TYPE_INVALID。stream输入执行等待任务的 Stream。此处只支持与模型绑定过的 Stream绑定方式同上需调用 aclmdlRIBindStream。flag输入预留参数当前固定配置为 0。返回值说明返回 0 表示成功返回其他值表示失败错误码参见 aclError。配套结构体aclrtBarrierTaskInfo及其内部结构定义于 include/external/acl/acl_rt.htypedef struct { aclrtCmoType cmoType; // Cache 操作类型此处必须为 ACL_RT_CMO_TYPE_INVALID uint32_t barrierId; // 屏障标识必须大于 0 } aclrtBarrierCmoInfo; #define ACL_RT_CMO_MAX_BARRIER_NUM 6U typedef struct { size_t barrierNum; // 屏障数量范围 [1, ACL_RT_CMO_MAX_BARRIER_NUM] aclrtBarrierCmoInfo cmoInfo[ACL_RT_CMO_MAX_BARRIER_NUM]; } aclrtBarrierTaskInfo;实现要点底层rtsLaunchBarrierTask见 api_c_memory.cc对taskInfo做了三重校验taskInfo不能为空logicIdNum即 barrierNum必须落在[1, ACL_RT_CMO_MAX_BARRIER_NUM]即 1~6区间内遍历cmoInfo数组要求每个元素的cmoType必须是RT_CMO_INVALID且logicId不能为 0。校验通过后调用rtBarrierTaskLaunch下发屏障任务从而实现对指定 barrierId 的 Invalid 缓存操作完成状态的等待。典型用法在支持该接口的 Atlas 200I/500 A2 推理产品上// 下发带屏障标识的 Invalid 缓存操作 uint32_t barrierId 1; aclrtCmoAsyncWithBarrier(devPtr, size, ACL_RT_CMO_TYPE_INVALID, barrierId, modelBoundStream); // 构造等待任务信息等待 barrierId1 的 Invalid 操作完成 aclrtBarrierTaskInfo taskInfo {}; taskInfo.barrierNum 1; taskInfo.cmoInfo[0].cmoType ACL_RT_CMO_TYPE_INVALID; taskInfo.cmoInfo[0].barrierId barrierId; aclError ret aclrtCmoWaitBarrier(taskInfo, modelBoundStream, 0); if (ret ! 0) { // 错误处理 }3.4 aclrtCmoGetDescSize获取描述符大小aclError aclrtCmoGetDescSize(size_t *size)功能说明获取当前 Device 上的 Cache 内存描述符占用的内存大小。参数说明参数名输入/输出说明size输出Cache 内存描述符大小单位 Byte。返回值说明返回 0 表示成功返回其他值表示失败错误码参见 aclError。实现要点底层rtsGetCmoDescSize通过Api::Instance()-GetCmoDescSize(size)查询见 api_c_standard_soc.cc描述符大小与具体芯片型号相关因此必须在运行时动态获取不要硬编码。3.5 aclrtCmoSetDesc设置内存描述符aclError aclrtCmoSetDesc(void *cmoDesc, void *src, size_t size)功能说明设置 Cache 内存描述符。此接口调用完成后会将源内存地址、内存大小记录到 Cache 内存描述符中。参数说明参数名输入/输出说明cmoDesc输入Cache 内存描述符地址指针。需先调用aclrtCmoGetDescSize获取描述符所需内存大小再申请 Device 内存例如通过aclrtMalloc参见 11-01_device_memory_malloc_and_free.md将 Device 内存地址作为入参传入此处。src输入待操作的 Device 内存地址。只支持本 Device 上的 Cache 内存操作。size输入待操作的 Device 内存大小单位 Byte。返回值说明返回 0 表示成功返回其他值表示失败错误码参见 aclError。实现要点底层rtsSetCmoDesc(cmoDesc, srcAddr, srcLen)调用Api::Instance()-SetCmoDesc见 api_c_standard_soc.cc本质是把源地址 长度这对信息固化到 Device 侧描述符内存中供后续aclrtCmoAsyncWithDesc直接复用。3.6 aclrtCmoAsyncWithDesc基于描述符的 Cache 内存操作aclError aclrtCmoAsyncWithDesc(void *cmoDesc, aclrtCmoType cmoType, aclrtStream stream, const void *reserve)功能说明使用内存描述符二级指针方式操作 Device 上的 Cache 内存。异步接口。参数说明参数名输入/输出说明cmoDesc输入Cache 内存描述符地址指针Device 侧内存地址。此处需先调用aclrtCmoSetDesc设置内存描述符再将内存描述符地址指针作为入参传入本接口。cmoType输入Cache 内存操作类型类型定义参见 aclrtCmoType。当前仅支持ACL_RT_CMO_TYPE_PREFETCH内存预取。stream输入执行内存操作任务的 Stream类型定义参见 aclrtStream。reserve输入预留参数当前固定传 NULL。返回值说明返回 0 表示成功返回其他值表示失败错误码参见 aclError。实现要点底层rtsLaunchCmoAddrTask调用rtCmoAddrTaskLaunch见 api_c_standard_soc.cc并做了两点约束reserve必须为nullptr且任务信息中的地址信息由描述符承载。同时从 api_c_memory.cc 的rtsLaunchCmoTask可以看到底层下发时会将qos固定设置为 6注释说明这是to avoid the cross-chip D2D problem避免跨芯片 D2D 问题这是 CMO 任务在驱动侧的一条重要保底策略。完整使用流程在支持该接口的产品上例如 Atlas A2/A3 训练系列size_t descSize 0; aclrtCmoGetDescSize(descSize); // 1. 获取描述符大小 void *cmoDesc nullptr; aclrtMalloc(cmoDesc, descSize, ACL_MEM_MALLOC_HUGE_FIRST); // 2. 为描述符申请 Device 内存 aclrtCmoSetDesc(cmoDesc, devPtr, size); // 3. 将源地址与大小写入描述符 aclError ret aclrtCmoAsyncWithDesc(cmoDesc, ACL_RT_CMO_TYPE_PREFETCH, stream, NULL); // 4. 基于描述符下发预取任务 if (ret ! 0) { // 错误处理 } aclrtFree(cmoDesc); // 5. 使用完毕后释放描述符内存四、CMO 任务下发调用链综合源码CMO 接口从用户态到任务下发的调用链可归纳为用户态 ACL 层aclrtCmo*系列接口声明见 include/external/acl/acl_rt.h 及 L4524-L4536、L5499-L5523RT 接口层rtsCmoAsync、rtsCmoAsyncWithBarrier、rtsLaunchBarrierTask、rtsGetCmoDescSize、rtsSetCmoDesc、rtsLaunchCmoAddrTask位于 api_c_memory.cc 与 api_c_standard_soc.cc负责参数合法性校验、错误码归一化与芯片特性位检查API 分发层rtCmoTaskLaunch/rtCmoAddrTaskLaunch/rtBarrierTaskLaunch等通过Api::Instance()分发到具体实现驱动/任务队列最终封装为任务描述符下发到 Stream 对应的硬件队列执行。从 api_c_memory.cc 的实现细节看rtsCmoAsync甚至直接透传rtCmoAsync不做额外校验而rtsCmoAsyncWithBarrier则承担了最重的参数组合校验这种入口收敛、校验前置的写法值得参考。五、使用注意事项与实践建议先确认产品支持情况CMO 接口族在产品间差异极大——地址直传式aclrtCmoAsync与描述符式aclrtCmoAsyncWithDesc面向 950/A3/A2 系列而带屏障式aclrtCmoAsyncWithBarrier/aclrtCmoWaitBarrier仅面向 Atlas 200I/500 A2 推理产品。编写可移植代码时应通过产品型号分支处理避免在不支持的产品上调用导致ACL_ERROR_RT_FEATURE_NOT_SUPPORT。注意 Stream 绑定约束aclrtCmoAsyncWithBarrier与aclrtCmoWaitBarrier的 stream 参数只支持与模型绑定过的 Stream需先通过 aclmdlRIBindStream 完成绑定普通aclrtCreateStream创建的 Stream 不满足要求。严格遵循参数组合规则barrierId仅在cmoType ACL_RT_CMO_TYPE_INVALID时有效且必须大于 0其余类型固定传 0aclrtCmoWaitBarrier的taskInfo中barrierNum上限为 6ACL_RT_CMO_MAX_BARRIER_NUM且所有cmoType必须为 Invalid。违反组合规则会返回RT_ERROR_INVALID_VALUE并带出错误码 EE1011详见 EE1011 资源不足问题 同族的错误码体系说明具体以运行时错误码文档为准。描述符的生命周期管理aclrtCmoGetDescSize必须先于内存申请调用描述符内存必须使用 Device 内存如aclrtMalloc参见 11-01_device_memory_malloc_and_free.md并在aclrtCmoAsyncWithDesc使用完成后及时释放reserve参数当前固定传 NULL。异步语义本接口族均为异步接口任务在 Stream 上排队执行。若需同步等待结果可配合 Stream 同步aclrtSynchronizeStream或事件Event机制使用相关背景可参考 Stream同步与Event同步的区别与选择。地址归属所有接口的src均只支持本 Device上的 Cache 内存操作跨 Device 地址请先通过合法方式迁移数据。六、总结CMO 缓存操作接口族是 CANN Runtime 在地址直传与描述符传参两条路径上提供的缓存维护能力配合枚举 aclrtCmoType 的四种操作类型可覆盖预取、写回、失效、冲刷四类典型需求而barrierId机制则为 Invalid 操作提供了精细的完成等待语义。使用前务必核对产品支持矩阵与参数约束尤其是 Stream 的模型绑定要求与 barrierId 的取值规则。若需深入理解底层实现可继续阅读 api_c_memory.cc 与 api_c_standard_soc.cc 中的 RT 层实现以及 acl_rt.h 中的结构体定义。【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考