news 2026/9/19 22:47:05

CANN SHMEM 设备侧 SDMA NotifyWait 机制使用指南:显式 QP 多核并发与无 QP 单核搬运实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN SHMEM 设备侧 SDMA NotifyWait 机制使用指南:显式 QP 多核并发与无 QP 单核搬运实战

CANN SHMEM 设备侧 SDMA NotifyWait 机制使用指南:显式 QP 多核并发与无 QP 单核搬运实战

【免费下载链接】shmemCANN SHMEM 是面向昇腾平台的多机多卡内存通信库,基于OpenSHMEM 标准协议,实现跨设备的高效内存访问与数据同步。项目地址: https://gitcode.com/cann/shmem

本文围绕 CANN SHMEM 开源仓库中的notifywait示例(README_en.md)展开,系统讲解设备侧 SDMA(System DMA)异步数据搬运与 NotifyWait 完成通知机制:如何在指定 SDMA QP 上下发 record 类型的 SQE、在 Host 侧通过aclrtWaitAndResetNotify等待通知,以及显式 QP 接口与无 QP 接口的差异与选型。读完本文,你将掌握notifywait示例的编译运行方法、容量与 AIV 限制评估,以及基于 SDMA 的 AllGather 多核/单核两种实现路径,并能在自己的算子中直接复用这套"搬运 + 通知 + 等待"的流水线同步模式。

一、机制概览:为什么需要 NotifyWait

在昇腾设备侧,SDMA 引擎承担 GM 与 GM 之间、GM 与 UB 之间的高速数据搬运。aclshmemx_sdma_qp_put_nbi/aclshmemx_sdma_qp_get_nbi这类接口是**非阻塞(nbi = non-blocking)**的——函数返回只代表 SQE 已提交到 QP,并不代表搬运完成。因此调用方必须通过某种手段确认"数据就绪"后才能安全地消费数据或复用工作区。

CANN SHMEM 提供两种完成确认方式:

  • aclshmemx_sdma_qp_quiet:在 AIV 内轮询 flag,直到指定 QP 上的 SQE 全部完成。缺点是该 AIV 被阻塞在轮询循环中,无法及时释放。
  • NotifyWait(本示例的主题):在数据搬运后追加一条 record 类型的 SQE,由 Host 侧aclrtWaitAndResetNotify等待通知;等待完成后 AIV 已提前释放,后续 kernel 可直接使用搬运结果。

其核心思路可用三步概括:Kernel 1(stream 1)搬运数据并记录通知 → Host 等待并复位 notify → Kernel 2(stream 2)消费数据。相比quiet的 AIV 轮询,NotifyWait 让 Host 承担等待职责,从而"及时释放 AIV 资源"(见 README_en.md)。

二、环境要求与软件准备

SDMA 功能是较新的能力,有明确的软件版本门槛:

  • CANN 版本:SDMA 功能需要 CANN 9.0.0 及以上版本(trial 版)支持(中文版 README.md 中标注为 CANN 9.0.0-beta.2 及以上)。需要安装Toolkit 包ops-legacy 包两类软件包,ops 包需根据硬件平台(A2/A3、x86_64/aarch64)选择与 toolkit 版本匹配的版本。
  • 平台限制:当前暂不支持 Ascend950平台配套编译运行(SDMA 写操作在 Ascend950 上不受支持,见 shmem_device_sdma.h 中相关接口注释)。
  • 运行环境:PES 仅支持 2、4、8 卡,且限定在单台机器内;数据通过 TCP 环回地址(默认tcp://127.0.0.1:8766)进行初始化通信。

三、编译与运行示例

3.1 三步编译运行流程

按照 README_en.md 的操作顺序:

  1. 在仓库根目录shmem/下编译软件包并安装:
bash scripts/build.sh -package ./install/*/SHMEM_1.0.0_linux-*.run --install
  1. 在仓库根目录shmem/下编译所有 examples:
bash scripts/build.sh -examples
  1. 进入shmem/examples/notifywait目录运行 demo:
bash run.sh -pes ${PES} -type ${TYPES}

参数说明(来自文档):

  • PES:用于运行 demo 的设备(NPU)数量,仅支持 2、4、8 卡,限定单台机器内。
  • TYPES:传输的数据类型,当前支持intuint8int64fp32

3.2 运行脚本支持的全部参数

run.sh 实际还支持更多命令行选项,可通过-键 值的方式传入:

参数含义默认值
-pes进程/PE 数量2(若-gnpus大于该值会自动收敛为-pes值)
-type数据类型:int / uint8 / int64 / fp32int
-ipport初始化通信 IP 端口tcp://127.0.0.1:8766
-fpe起始 PE 编号0
-gnpus使用的 NPU 数量(对应每卡一个进程)8
-fnpu起始 NPU 编号0
-pe_tablePE 映射表

脚本内部会导出SHMEM_UID_SESSION_ID=127.0.0.1:8899作为 UID 会话标识,并将${PROJECT_ROOT}/build/lib${ASCEND_HOME_PATH}/lib64加入LD_LIBRARY_PATH,随后为每个 NPU 拉起一个后台进程,等待全部进程结束后统一返回退出码。

四、容量与 AIV 限制(运行前必读)

文档明确给出了本示例的资源预算,运行前需要据此评估硬件是否满足条件:

  • 对称内存容量:示例申请128M * sizeof(T)字节的对称空间,其中输入区与结果区各需PES * 8M * sizeof(T)字节。以 8 卡、int(4 字节)为例,即每个 PE 需约128M * 4B = 512MB对称空间。文档支持矩阵为 2、4、8 卡,实际可用卡数还需满足对称空间与运行环境的容量条件。在 main.cpp 中可看到symmetric_elements = 128 * 1024 * 1024trans_size = 8 * 1024 * 1024的定义。
  • SDMA 共享 workspace:为 28 KiB。按 A5 平台最大 72 个 AIV/QP 计算,notify ID 区域与三组 flag 区域共需14 KiB + 72 * 4 B + 3 * 72 * 64 B = 28,448 B,恰好剩余 224 B,空间足够。
  • AIV/QP 数量:当前 kernel 启动 20 个 block,每个 block 含 2 个 subblock(AIV),实际使用 40 个 AIV/QP;底层基础设施和 notify 数组已按最多72 个 AIV/QP预留。上报 vector core 数超过 72 的设备当前返回"不支持"

对应的常量定义位于 main.cpp:SDMA_AIVS_PER_BLOCK = 2SDMA_BLOCK_NUM = 20SDMA_QP_NUM = SDMA_BLOCK_NUM * SDMA_AIVS_PER_BLOCK = 40,并在初始化时通过aclshmemx_set_qp_num(ACLSHMEM_DATA_OP_SDMA, SDMA_QP_NUM)配置 SDMA 通道数(main.cpp),同时将attributes.option_attr.data_op_engine_type置为ACLSHMEM_DATA_OP_SDMA以启用 SDMA 数据通路。

五、NotifyWait 三步用法详解

5.1 用法示例

notifywait 机制三步流程示意

整个机制分为三个步骤(对应 README_en.md 中的伪代码):

// 步骤 1: // stream 1 上的 kernel 1:调用显式 QP 的 SDMA 接口搬运数据,并追加 aclshmemx_sdma_qp_notify_record // 步骤 2: // Host:aclrtWaitAndResetNotify(notify_id, stream2, 0) // 步骤 3: // stream 2 上的 kernel 2:使用 SDMA 搬运好的数据

5.2 原理:record 类型 SQE 与 Host 侧等待

aclshmemx_sdma_qp_notify_record中,会向选定的 STARS QP下发一条 record 类型的 SQE。由于 SQE 在 QP 内保序,这条 record 通知会排在之前提交的所有搬运 SQE 之后,因此 Host 等待到该通知时,即可确认此前该 QP 上的搬运全部完成。随后 Host 再调度后续 kernel,天然形成了"搬运完成 → 数据可用"的依赖关系。

相比aclshmemx_sdma_qp_quiet依赖AIV 轮询 flag的方式,NotifyWait 将等待从 AIV 转移到 Host,从而及时释放 AIV 资源,让 AIV 可以立即投入其他计算任务。

设备侧实现可在 shmem_device_sdma.hpp 中看到:aclshmemi_stars_submit_notify_record通过notify_addr[qp_idx]定位到对应 QP 的通知地址并填充 record SQE;无 QP 变体则固定向 QP 0 追加通知(aclshmemi_stars_submit_notify_record(ub_tensor, sync_id, 0))。

Host 侧的 notify 对象由 SDMA 传输管理模块在初始化阶段创建:通过aclrtCreateNotify为每个 QP 创建 notify、aclrtGetNotifyId获取 notify ID,并在结束时用aclrtDestroyNotify销毁(见 device_sdma_transport_manager.cpp)。示例中 Host 通过g_state_host.notify_arr[i]数组按 QP 索引一一对应等待(main.cpp)。

六、显式 QP 的 SDMA 接口:多核 AllGather 实现

6.1 接口形态

显式 QP 接口相比无 QP 接口多一个qp_idx参数,且提供__ubuf__指针与AscendC::GlobalTensor/LocalTensor两套重载。核心接口签名(完整声明见 shmem_device_sdma.h):

template <typename T> void aclshmemx_sdma_qp_put_nbi(__gm__ T* dst, __gm__ T* src, __ubuf__ T* buf, uint32_t ub_size, uint32_t elem_size, int pe, uint32_t qp_idx, uint32_t sync_id); template <typename T> void aclshmemx_sdma_qp_get_nbi(__gm__ T* dst, __gm__ T* src, __ubuf__ T* buf, uint32_t ub_size, uint32_t elem_size, int pe, uint32_t qp_idx, uint32_t sync_id); template <typename T> void aclshmemx_sdma_qp_notify_record(__ubuf__ T* buf, uint32_t ub_size, uint32_t qp_idx, uint32_t sync_id);
  • dst/src:对称地址(会在指定 PE 上做地址翻译)或本设备 GM 地址;dst/src需落在同一个对称分配块内。
  • buf/ub_size:UB 工作区,地址必须64 字节对齐,大小至少 64 字节
  • elem_size:搬运的元素个数,elem_size * sizeof(T)不得超过UINT32_MAX字节。
  • pe:对端 PE,必须处于已初始化的 PE 范围内。
  • qp_idx:SDMA QP 索引,必须小于已配置的 SDMA 通道数;QP 索引与 block 索引相互独立
  • sync_id:流水线同步使用的硬件事件 ID。

6.2 多核 allgather_sdma 内核

main.cpp 中的allgather_sdma内核演示了显式 QP 的标准用法:

  1. 每个 AIV 根据GetBlockIdx()计算出自己负责的连续数据区间(base_per_core/extra_bytes按元素均摊切分,保证各 AIV 负载均衡);
  2. 循环向除自身外的每个 PE 调用aclshmemx_sdma_qp_put_nbi(或get_nbi),每个 AIV 使用与自身编号相同的 QP收发数据;
  3. 全部搬运提交后,调用aclshmemx_sdma_qp_notify_record在本 QP 上追加通知。

Host 侧对 40 个 QP 逐个等待:

for (int i = 0; i < total_block_num * sub_block_num; i++) { CHECK_RET(aclrtWaitAndResetNotify(g_state_host.notify_arr[i], g_state_host.default_stream, 0)); }

需要说明的是,接口的完成语义是:函数正常返回仅代表请求已提交,不代表搬运完成。在显式 QP 场景下,不能用aclshmemx_sdma_quiet(它只排空 QP 0)来等待qp_idx > 0的请求,而应使用同 QP 的aclshmemx_sdma_qp_quiet,或如本例一样追加aclshmemx_sdma_qp_notify_record并在 Host 等待(shmem_device_sdma.h 的接口注释对此有明确说明)。

示例还提供了allgather_sdma_tensor内核(main.cpp),展示GlobalTensor/LocalTensor重载的等价用法。

七、不带 QP 的 SDMA 接口:单核 AllGather 实现

除显式 QP 接口外,示例还演示了不带 QP 的 SDMA 接口。二者接口形态接近,区别是不带 QP 的接口固定使用 QP 0、无需传入qp_idx,属于单核(单 AIV)接口

// 异步搬运(固定使用 QP 0) template <typename T> void aclshmemx_sdma_put_nbi(__gm__ T* dst, __gm__ T* src, __ubuf__ T* buf, uint32_t ub_size, uint32_t elem_size, int pe, uint32_t sync_id); // 在 QP 0 上追加 notify record,Host 侧等待 notify_arr[0] 即可 template <typename T> void aclshmemx_sdma_notify_record(__ubuf__ T* buf, uint32_t ub_size, uint32_t sync_id);

对应实现为 main.cpp 中的allgather_sdma_noqp内核:仅由 0 号 AIV 执行(其余 AIV 直接返回),对本 PE 的数据做整块搬运(无需按 AIV 切分),并在 QP 0 上记录 notify:

// kernel 内(仅 0 号 AIV 执行) aclshmemx_sdma_put_nbi(dst, src, tmp_buff, ub_size, size, pe, EVENT_ID0); aclshmemx_sdma_notify_record(tmp_buff, ub_size, EVENT_ID0); // host 侧:只等待 1 个 notify(QP 0 对应 notify_arr[0]) aclrtWaitAndResetNotify(g_state_host.notify_arr[0], stream, 0);

与显式 QP 接口的对比

对比项不带 QP 接口显式 QP 接口
使用的 QP固定 QP 0通过qp_idx指定,可用满已创建的全部 QP
执行方式单 AIV 执行多 AIV 并发,每个 AIV 使用独立 QP
数据切分无需切分,整块搬运需按 AIV 切分数据
Host 等待notify_arr[0]每个 QP 各等待一次 notify
适用场景单核简单收发、快速验证多核并发、带宽敏感场景

八、运行验证与结果解读

运行run.sh时,demo 的执行顺序是固定的(见 main.cpp):

  1. 显式 QP 多核 AllGatherallgather_sdma内核搬运 → Host 等待 40 个 notify →aclshmem_barrier_all()同步 → 用 MTE 将结果拷入结果区 → Host 校验,控制台打印after notify_wait段的结果;
  2. 无 QP 单核 AllGatherallgather_sdma_noqp内核整块搬运 → Host 等待notify_arr[0]aclshmem_barrier_all()同步 → 拷贝并校验,控制台打印after sdma_put_nbi (no QP)段的结果。

两段校验逻辑相同:逐元素比对结果区中每个 PE 贡献的数据是否等于num10 + inum10 = 10),并统计异常值个数;若异常值均为 0,则说明对应阶段的搬运与通知机制工作正常。最终每个 PE 打印[SUCCESS] demo run success in pe N表示整体通过。

九、总结

NotifyWait 机制为 CANN SHMEM 设备侧 SDMA 异步搬运提供了一条"搬运即通知、Host 等待、流间接力"的同步链路:aclshmemx_sdma_qp_notify_record在指定 QP 追加 record SQE,Host 以aclrtWaitAndResetNotify等待,避免了quiet方案中 AIV 轮询 flag 的资源占用。notifywait示例同时给出了两种工程范式——显式 QP 的多核并发 AllGather(每 AIV 一 QP、按 AIV 切分数据)与无 QP 的单核快速验证 AllGather(固定 QP 0、整块搬运),配合本文给出的容量评估方法,可直接迁移到其他基于 SDMA 的集合通信或流水线算子设计中。

进一步阅读:完整的中文说明见 README.md,接口头文件见 shmem_device_sdma.h,设备侧实现见 shmem_device_sdma.hpp,Host 侧 notify 生命周期管理见 device_sdma_transport_manager.cpp。

【免费下载链接】shmemCANN SHMEM 是面向昇腾平台的多机多卡内存通信库,基于OpenSHMEM 标准协议,实现跨设备的高效内存访问与数据同步。项目地址: https://gitcode.com/cann/shmem

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

基于深度强化学习的智能停车系统设计与优化

1. 项目背景与核心价值停车难问题已经成为现代城市管理的痛点。我在北京某商业区实地调研时发现&#xff0c;高峰时段平均每辆车需要绕行15分钟才能找到空位&#xff0c;这不仅造成时间浪费&#xff0c;更导致周边道路拥堵指数上升37%。传统停车引导系统往往只显示剩余车位数量…

作者头像 李华
网站建设 2026/9/19 22:46:13

C语言标准库避坑指南:从字符处理到内存管理的边界问题

简介&#xff1a;这是一份面向C语言学习者的常用函数与头文件整理文档&#xff0c;适合正在系统复习C语言基础、备战考试或从事嵌入式开发的读者。内容以函数库为主线&#xff0c;覆盖ctype.h字符处理、math.h数学运算、stdio.h输入输出、stdlib.h实用工具、string.h字符串操作…

作者头像 李华
网站建设 2026/9/19 22:46:03

Wails v3 集成 Gin 实现自定义路由:Gin-Routing 示例深度解析

Wails v3 集成 Gin 实现自定义路由&#xff1a;Gin-Routing 示例深度解析 【免费下载链接】wails Create beautiful applications using Go 项目地址: https://gitcode.com/gh_mirrors/wa/wails 本指南以仓库中 gin-routing 示例 为骨架&#xff0c;完整讲解如何在 Wail…

作者头像 李华
网站建设 2026/9/19 22:45:05

Linux资源大全awesome-linux:社区筛选的优质工具与学习指南

1. 为什么“资源大全”这件事值得认真做刚接触Linux那会儿&#xff0c;我最大的困扰不是命令记不住&#xff0c;而是不知道该去哪里找靠谱的资料。搜索引擎一搜&#xff0c;前几页全是内容农场拼凑的“Linux常用命令大全”&#xff0c;复制粘贴的痕迹比我的笔记还明显&#xff…

作者头像 李华