news 2026/9/19 1:46:16

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN Runtime CMO 缓存操作接口解析:aclrtCmoAsync 系列 API 使用与实现原理

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

【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime

CMO(Cache Maintenance Operations,缓存维护操作)是 CANN Runtime 提供的 Device 侧缓存刷新与失效接口族,用于在 Host 侧对 NPU 上的 Cache 与 DDR 内存做显式的预取、写回、失效等控制。本文以 11-06_CMO_memory_operation.md 为骨架,完整梳理aclrtCmoAsyncaclrtCmoAsyncWithBarrieraclrtCmoWaitBarrieraclrtCmoGetDescSizeaclrtCmoSetDescaclrtCmoAsyncWithDesc六个接口的产品支持情况、参数约束与典型用法,并结合本仓库源码(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 将这类操作抽象为CMO(Cache Maintenance Operations),并以aclrtCmo*系列异步接口向用户开放。除aclrtCmoAsync直接指定内存地址外,该接口族还提供基于内存描述符(Descriptor)的进阶用法,以及携带 barrierId 的屏障式同步用法,满足从简单预取到精细化缓存一致性控制的多种需求。

CMO 操作类型通过枚举 aclrtCmoType 表达,其定义位于 include/external/acl/acl_rt.h:

typedef 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 / CmoAsyncWithDesc
Ascend 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 cmoType=invalid 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输入屏障标识。当cmoTypeACL_RT_CMO_TYPE_INVALID时有效,支持传入大于 0 的数字,配合 aclrtCmoWaitBarrier 使用,等待具有指定 barrierId 的 Invalid 内存操作任务执行完成;当cmoType为其他值时,barrierId固定传 0。
stream输入执行内存操作任务的 Stream。此处只支持与模型绑定过的 Stream,绑定模型与 Stream 需调用 aclmdlRIBindStream 接口。

返回值说明:返回 0 表示成功,返回其他值表示失败,错误码参见 aclError。

实现要点rtsCmoAsyncWithBarrier对参数组合做了严格校验(见 api_c_memory.cc):

  • cmoTypeRT_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 0";
  • cmoTypeRT_CMO_PREFETCHRT_CMO_WRITEBACKRT_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.h:

typedef 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做了三重校验:

  1. taskInfo不能为空;
  2. logicIdNum(即 barrierNum)必须落在[1, ACL_RT_CMO_MAX_BARRIER_NUM](即 1~6)区间内;
  3. 遍历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); // 构造等待任务信息,等待 barrierId=1 的 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 接口从用户态到任务下发的调用链可归纳为:

  1. 用户态 ACL 层aclrtCmo*系列接口(声明见 include/external/acl/acl_rt.h 及 L4524-L4536、L5499-L5523);
  2. RT 接口层rtsCmoAsyncrtsCmoAsyncWithBarrierrtsLaunchBarrierTaskrtsGetCmoDescSizertsSetCmoDescrtsLaunchCmoAddrTask(位于 api_c_memory.cc 与 api_c_standard_soc.cc),负责参数合法性校验、错误码归一化与芯片特性位检查;
  3. API 分发层rtCmoTaskLaunch/rtCmoAddrTaskLaunch/rtBarrierTaskLaunch等,通过Api::Instance()分发到具体实现;
  4. 驱动/任务队列:最终封装为任务描述符下发到 Stream 对应的硬件队列执行。

从 api_c_memory.cc 的实现细节看,rtsCmoAsync甚至直接透传rtCmoAsync不做额外校验,而rtsCmoAsyncWithBarrier则承担了最重的参数组合校验,这种"入口收敛、校验前置"的写法值得参考。

五、使用注意事项与实践建议

  1. 先确认产品支持情况:CMO 接口族在产品间差异极大——地址直传式(aclrtCmoAsync)与描述符式(aclrtCmoAsyncWithDesc)面向 950/A3/A2 系列,而带屏障式(aclrtCmoAsyncWithBarrier/aclrtCmoWaitBarrier)仅面向 Atlas 200I/500 A2 推理产品。编写可移植代码时应通过产品型号分支处理,避免在不支持的产品上调用导致ACL_ERROR_RT_FEATURE_NOT_SUPPORT
  2. 注意 Stream 绑定约束aclrtCmoAsyncWithBarrieraclrtCmoWaitBarrier的 stream 参数只支持与模型绑定过的 Stream,需先通过 aclmdlRIBindStream 完成绑定,普通aclrtCreateStream创建的 Stream 不满足要求。
  3. 严格遵循参数组合规则barrierId仅在cmoType == ACL_RT_CMO_TYPE_INVALID时有效且必须大于 0,其余类型固定传 0;aclrtCmoWaitBarriertaskInfobarrierNum上限为 6(ACL_RT_CMO_MAX_BARRIER_NUM),且所有cmoType必须为 Invalid。违反组合规则会返回RT_ERROR_INVALID_VALUE并带出错误码 EE1011(详见 EE1011 资源不足问题 同族的错误码体系说明,具体以运行时错误码文档为准)。
  4. 描述符的生命周期管理aclrtCmoGetDescSize必须先于内存申请调用,描述符内存必须使用 Device 内存(如aclrtMalloc,参见 11-01_device_memory_malloc_and_free.md),并在aclrtCmoAsyncWithDesc使用完成后及时释放;reserve参数当前固定传 NULL。
  5. 异步语义:本接口族均为异步接口,任务在 Stream 上排队执行。若需同步等待结果,可配合 Stream 同步(aclrtSynchronizeStream)或事件(Event)机制使用,相关背景可参考 Stream同步与Event同步的区别与选择。
  6. 地址归属:所有接口的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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 1:45:50

TC275多核OS配置与调试实战:基于Davinci Cfg的AutoSAR开发经验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 1:43:10

Trae 与 Cursor 选谁?TaoToken 这样改模型通道再横评

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 1:42:42

基于STM32F103C8T6的桌面宠物:硬件架构与舵机控制实战

1. 桌面宠物项目的整体架构设计思路1.1 为什么选择STM32F103C8T6作为主控做桌面宠物这个想法,最早来源于我想在办公桌上放一个能互动的小玩意儿。市面上成品要么太贵,要么功能太单一,索性自己从头搭一套。核心需求很明确:体积小、…

作者头像 李华