PTO 通信指令测试实战:A5 平台基于 RDMA(HNS1825)的 TPUT_ASYNC_NOTIFY 异步写与 Set 信号验证
【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa
导读
tput_async_notify_rdma是 CANN pto-isa 仓库中专门用于验证「RDMA 远程写 + Set 信号通知」组合语义的 A5 通信 ST(System Test)用例。它复用 RDMA 异步 ST 的共享实现,通过TPUT_ASYNC_NOTIFY指令将根 rank 的发送缓冲以单边远程写(one-sided remote write)方式写入对端接收缓冲,并在对端内存中写入一个 int32 Set 信号;对端通过轮询信号、维护数据缓存后校验载荷与信号两侧的 canary 哨兵值。读完本文,你将掌握该用例的构建运行方式、PTO_RDMA_BACKEND等关键环境变量语义、端点发现机制、内核级指令调用链以及常见故障的定位方法。
一、用例定位:RDMA 异步 ST 三件套中的「写 + 通知」
tput_async_notify_rdma并不是一个独立实现的用例,其入口文档明确指出它复用 RDMA 异步 ST 的共享实现,前置条件、配置、运行命令和问题定位均以 RDMA 异步 ST 说明 为准(中文版见 README_zh.md)。共享实现位于tests/npu/a5/comm/st/testcase/tput_async_rdma/目录,其 CMake 配置可以直接佐证这种复用关系:
pto_comm_st(tput_async_notify_rdma ../tput_async_rdma/tput_async_rdma_kernel.cpp)即tput_async_notify_rdmatarget 直接编译共享的 tput_async_rdma_kernel.cpp,三个 target 共用同一套 RDMA 测试内核实现,职责划分如下:
| Target | 验证的语义 | 数据面操作 |
|---|---|---|
tput_async_rdma | 远程写 | TPUT_ASYNC(RDMA 引擎) |
tget_async_rdma | 远程读 | TGET_ASYNC(RDMA 引擎) |
tput_async_notify_rdma | 远程写 +Set通知 | TPUT_ASYNC_NOTIFY(RDMA 引擎) |
三者都运行在HNS1825 网卡(目标平台)之上,并通过 rdma_test_backend.hpp 与 hns_1825_bootstrap.hpp 完成后端适配与端点引导。
二、被测核心语义:TPUT_ASYNC_NOTIFY
2.1 指令语义
TPUT_ASYNC_NOTIFY是 PTO 通信指令集中「异步远程写 + 信号更新」的组合操作。其声明位于 include/pto/comm/pto_comm_inst.hpp,签名如下(简化):
AsyncEvent TPUT_ASYNC_NOTIFY( GlobalDstData& dstGlobalData, GlobalSrcData& srcGlobalData, GlobalSignalData& dstSignalData, int32_t signalValue, NotifyOp notifyOp, const AsyncSession& session, uint32_t peer, WaitEvents&... events)参数要点:
dstGlobalData/srcGlobalData:远端目标张量与本地源张量,二者元素类型必须一致、布局必须一致;dstSignalData:远端信号地址,必须是 int32 类型且 4 字节对齐;signalValue:写入远端信号的整数值;notifyOp:信号更新方式,目前支持NotifyOp::Set与NotifyOp::AtomicAdd两种(见 TPutAsyncCommonDetail.hpp 中的校验逻辑);peer:目标 peer 编号,用于 RDMA/URMA 选择对端队列与注册内存元数据;- 返回
AsyncEvent,供后续Wait/Test等待异步操作完成。
2.2 不同架构的实现差异
从 pto_comm_inst.hpp 的注释可以清楚看到该指令在不同架构上的落地方式:
- A2/A3(SDMA):载荷与信号由一条 SQ 统一提交;
- A5 SDMA 命名路径:使用同步 MTE 之后紧跟 Scalar
SET/AtomicAdd,返回一个已完成的 event(handle 为 0); - A5 URMA / RDMA:通过
peer选择目标队列与注册内存; - CPU 桩实现:载荷委托给
TPUT_ASYNC,信号通过TNOTIFY更新。
本用例聚焦的是 A5 平台上DmaEngine::RDMA的实现分支。
2.3 载荷前置校验
无论底层引擎如何,TPUT_ASYNC/TPUT_ASYNC_NOTIFY在提交前都会执行一组静态断言与运行期校验(见 TPutAsyncCommonDetail.hpp):
- 源、目标元素类型一致且布局一致;
- 源、目标指针非空;
- 源与目标张量必须满足**扁平连续一维(flat contiguous 1D)**布局:各维度 stride 与 shape 严格按顺序相乘,且只有最后一维有实际长度;
- 目标缓冲区元素数不少于源缓冲区;
- 信号类型必须是
int32_t,地址 4 字节对齐,notifyOp必须是Set或AtomicAdd。
三、前置条件
运行tput_async_notify_rdma需要满足以下条件(来源:共享 README):
- A5 环境,配备HNS1825 网卡,并安装匹配的驱动 / HCOMM 软件栈;
- MPI 与 HCCL环境已按 tests/README.md 中通信测试的说明配置(MPICH 或 OpenMPI,运行时需要
mpirun与libmpi.so,可配置MPI_HOME/MPI_LIB_PATH环境变量); - 参与测试的所有 rank 具备可达的 RDMA NIC IPv4 地址。
此外,tput_async_notify_rdma的用例对进程数有硬性约束:从 main.cpp 可以看到,Int32SetAndCanaries用例在CommMpiSize() != 2时直接GTEST_SKIP(),即必须恰好使用 2 个 MPI rank才实际执行。
四、构建与运行
4.1 选择 RDMA 后端并编译
RDMA 后端必须在CMake 配置阶段通过环境变量选定,这是整个流程最关键的约束:
export PTO_RDMA_BACKEND=HNS_1825PTO_RDMA_BACKEND只在 CMake 配置该 ST 构建时被读取;未设置、为空或不支持的值会导致测试不带 RDMA 支持编译。共享 README 中的完整命令如下:
export PTO_RDMA_BACKEND=HNS_1825 python3 tests/script/run_st.py -r npu -v a5 -t comm/tput_async_rdma -d -n 2 python3 tests/script/run_st.py -r npu -v a5 -t comm/tget_async_rdma -d -n 2 python3 tests/script/run_st.py -r npu -v a5 -t comm/tput_async_notify_rdma -d -n 2run_st.py参数说明(依据 tests/script/run_st.py):
| 参数 | 含义 |
|---|---|
-r/--run-mode | 运行模式,此处为npu |
-v/--soc-version | SOC 版本,此处为a5 |
-t/--testcase | 用例名,通信用例写作comm/<用例名>形式 |
-g/--gtest_filter | 可选,指定具体 gtest 用例名 |
-d/--debug-enable | 开启 debug 检查 |
-a/--auto-mode-enable | 开启 auto 模式 |
-w/--without-build | 跳过编译(需要预先编译好的产物) |
-n/--nranks | comm 测试的最大 MPI rank 数量(默认 8,按 2/4/8 分轮执行) |
4.2 首次验证:聚焦两 rank 用例
首次验证TPUT_ASYNC_NOTIFY时,建议只运行聚焦的两 rank 用例,以便快速定位问题:
export PTO_RDMA_BACKEND=HNS_1825 python3 tests/script/run_st.py -r npu -v a5 -t comm/tput_async_notify_rdma \ -g TPutAsyncNotifyRdma.Int32SetAndCanaries -d -n 24.3 关于重新编译的注意事项
run_st.py默认会重新构建。因此修改PTO_RDMA_BACKEND之后,不要使用-w/--without-build复用旧二进制,否则 RDMA 支持可能不会生效,导致用例被跳过或行为与预期不符。
五、用例行为剖析(源码级)
5.1 整体流程
Int32SetAndCanaries用例调用共享入口RunPutAsyncNotifyRdmaSet(2, 2, 0, 0)(2 个 rank、2 个设备,见 main.cpp)。共享内核实现位于 tput_async_rdma_kernel.cpp:
- 运行前检查(
PrepareRdmaLaunch):校验mpiSize == nRanks、设备数量充足、各 rank 通过 MPIAllgather对齐 RDMA 后端选择结果(全部READY才继续,全部DISABLED则整体 SKIP); - 环境准备(
RdmaTestContext::Setup):aclrtSetDevice+ 创建 stream +aclrtMalloc通信缓冲 + bootstrap 端点引导 +RdmaWorkspaceManager::Init建立 RDMA 通道; - 缓冲初始化:向
devBuf写入数据偏移之前的头部(设备状态、canary 前哨、信号初值、canary 后哨)以及 send/recv 缓冲; - 启动内核:根 rank 发帖
TPUT_ASYNC_NOTIFY,非根 rank 轮询信号; - 结果校验:流同步后回读设备状态、载荷、信号值与两侧 canary,全部通过才 PASS。
5.2 通信缓冲布局
缓冲区布局与 URMA 测试保持一致(见 tput_async_rdma_kernel.cpp 的常量定义):
[64 × int32 头部][sendBuf: count × T][recvBuf: count × T]其中头部各字段偏移:
| 偏移 | 字段 | 初值 |
|---|---|---|
| 0 | 设备状态deviceStatus | 0 |
| 4(1 × uint32) | canary 前哨canaryBefore | 0x13572468 |
| 8(2 × uint32) | 信号signal | 0 |
| 12(3 × uint32) | canary 后哨canaryAfter | 0x24681357 |
256(64 * sizeof(int32_t)) | sendBuf 起始 | 载荷数据 |
用例的数据长度为kCount = 256个int32_t元素,载荷值规律为index + rootRank * 10000。
5.3 根 rank:发帖远程写 + Set 通知
根 rank(myRank == rootRank)在内核中执行(tput_async_rdma_kernel.cpp):
const uint64_t peerBase = pto::comm::rdma::PeerMrBaseAddr(rdmaWorkspace, targetPeer); // ... pto::comm::Signal remoteSignal(reinterpret_cast<__gm__ int32_t*>(peerBase + kRdmaNotifySignalOffset)); pto::comm::AsyncEvent event = pto::comm::TPUT_ASYNC_NOTIFY<pto::comm::DmaEngine::RDMA>( destination, source, remoteSignal, kRdmaNotifySignalValue, pto::comm::NotifyOp::Set, session, targetPeer); *deviceStatus = CompleteRdmaEvent(event, session, RdmaCompletionMode::PUBLIC_EVENT_WAIT_TEST);要点:
targetPeer = myPeer + 1,即根 rank 只写相邻的下一个 peer;- 远端目标地址 = 对端注册内存基址
peerBase+ 数据偏移(256B)+count(落到对端 recvBuf 区域); - 信号值为
kRdmaNotifySignalValue = 37,notifyOp为Set; - 完成模式采用
PUBLIC_EVENT_WAIT_TEST:先Test(首次可能合法地为 false),再Wait确保事件完成,最后再次Test验证已消费的目标索引被识别为完成(CompleteRdmaEvent)。
5.4 非根 rank:轮询信号 + 维护数据缓存 + 校验载荷
非根 rank 不参与发帖,而是(tput_async_rdma_kernel.cpp):
pto::comm::Signal signal(localSignal); for (uint32_t poll = 0U; poll < kRdmaNotifyPollLimit; ++poll) { if (pto::comm::TTEST(signal, kRdmaNotifySignalValue, pto::comm::WaitCmp::EQ)) { signaled = true; break; } } // 观察信号后,先维护数据缓存,再校验载荷 __asm__ __volatile__(""); dcci(static_cast<__gm__ void*>(0), cache_line_t::ENTIRE_DATA_CACHE); __asm__ __volatile__(""); for (uint32_t index = 0U; index < count; ++index) { if (recvBuf[index] != static_cast<int32_t>(index + rootRank * 10000U)) { /* 失败 */ } }这里体现了 RDMA 通知语义中一个容易被忽略的细节:观察(poll)到信号不代表数据已从缓存可见。因此用例在信号就绪后显式执行dcci(数据缓存一致性刷新),然后才读取载荷,这正是共享 README 中「After observing the signal, the receiver maintains its data cache before checking the payload」的源码对应。
轮询上限kRdmaNotifyPollLimit = 10000000(1000 万次),超时则置deviceStatus = kRdmaPublicEventWaitError。
5.5 主机侧附加校验
内核执行完毕后,主机侧还会对头部做三连读校验(tput_async_rdma_kernel.cpp):
- 信号值:根 rank 应为初值 0,非根 rank 应为
37(kRdmaNotifySignalValue); - canary 前哨仍为
0x13572468、canary 后哨仍为0x24681357——确认Set写信号没有越界污染相邻内存。
这就是用例名Int32SetAndCanaries中 canaries(金丝雀哨兵)的由来。
5.6 为什么没有 AtomicAdd 的 RDMA 用例
共享 README 明确说明:当前 RDMA 后端不支持AtomicAdd,因此没有对应的 RDMA 用例(NotifyOp::Set之外的路径未在 RDMA 引擎下展开测试)。
六、端点发现与配置参数
6.1 端点发现流程
Bootstrap(hns_1825_bootstrap.hpp)先解析每个 rank 的物理设备 id与RDMA IPv4,再通过 MPI 交换端点和注册内存信息。本地 IPv4 的查找顺序为:
- 固定
/etc/hccl_rootinfo.json中与物理设备匹配的 CLOS IPv4; - 解析固定
/var/run/ascend-topologyd/virtualTopology.xml(HCOMM 拓扑)中的 RoCE IPv4; - 测试专用 IP 环境变量(见下表)兜底。
注意:ST不生成、不修改上述两个拓扑文件,也不提供路径覆盖选项。
6.2 环境变量汇总
| 变量 | 说明 |
|---|---|
PTO_RDMA_BACKEND | 配置期后端选择器,唯一支持值HNS_1825 |
PTO_ROCE_PHYIDS | 可选,按 MPI rank 索引、逗号分隔的物理设备 id 列表 |
PTO_ROCE_LOCAL_IP | 当前 MPI 进程使用的最终兜底 IPv4;需要时须为各 rank 分别设置 |
PTO_ROCE_IPS | 最终兜底列表,按 MPI rank 排序且 IPv4 数量必须恰好等于 rank 数 |
PTO_ROCE_BASE_PORT | 公共通道基端口,默认60032 |
PTO_ROCE_VERBOSE | 设为1时输出端点、MR、通道与清理进度的详细日志 |
HCCL_RDMA_TC | HCOMM 流量类别(traffic class),默认132 |
HCCL_RDMA_SL | HCOMM 服务等级(service level),默认4 |
优先级规则(源码与 README 双重印证):
PTO_ROCE_LOCAL_IP优先于PTO_ROCE_IPS;- 只要 root-info 或 virtual-topology 查找成功,两个 IP 兜底变量都会被忽略;
- 所有 rank 必须使用相同的基端口;使用
PTO_ROCE_IPS时,所有 rank 必须使用相同的按序列表(bootstrap 中还会通过 MPIAllgather校验各 rank 基端口一致,不一致直接报错)。
七、故障排查
共享 README 给出了四类典型问题的定位路径:
- CMake 报告 RDMA 被禁用:确认已设置
PTO_RDMA_BACKEND=HNS_1825,并且重新配置构建时不要带-w; - 端点发现失败:核对物理设备映射、root-info / virtual-topology 中的 RoCE IPv4;必要时使用测试专用 IP 兜底变量;
- HCOMM 无法从默认搜索路径加载 HNS1825 verbs provider:将
IBV_EXTEND_DRIVERS设置为驱动提供的libhrn5-rdmav34.so; - 区分失败阶段:设置
PTO_ROCE_VERBOSE=1,通过日志区分端点发现、MR 注册、通道建立与清理哪个环节失败。
此外,设备侧返回的状态码也有明确的语义映射(见 rdma_test_backend.hpp 与内核的PrintRdmaDeviceStatus):
| 状态值 | 含义 |
|---|---|
kRdmaSessionBuildError | 异步会话构建失败(session_build_fail) |
kRdmaPublicEventWaitError(0x30000) | 公开事件Wait失败(public_event_wait_failed) |
kRdmaPublicEventTestError(0x30001) | Wait之后Test仍失败(public_event_test_after_wait_failed) |
| 后端自定义值 | 由DescribeBackendCompletionStatus解析,如poll_cq_timeout、invalid_transfer、invalid_context、cqe_error_without_syndrome等 |
八、小结
tput_async_notify_rdma表面上只是一个指向共享 README 的短文档,但其背后是一套完整的「RDMA 远程写 + Set 通知」验证体系:构建期通过PTO_RDMA_BACKEND=HNS_1825选择后端,运行期通过 MPI 完成端点发现与通道建立,内核侧由根 rank 发帖TPUT_ASYNC_NOTIFY、对端以TTEST轮询信号并在dcci刷新缓存后校验载荷,主机侧再以 canary 哨兵确认信号写入没有越界。对开发者而言,这套用例既是 PTO 通信指令在 RDMA 引擎下语义正确性的回归保障,也是学习异步通信「信号可见性」与「数据缓存一致性」如何落地为可执行代码的最佳样例。若需深入,可继续阅读共享实现 tput_async_rdma_kernel.cpp、指令声明 pto_comm_inst.hpp 以及端点引导 hns_1825_bootstrap.hpp。
【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考