ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PTO 多卡 GEMM + AllReduce 融合算子实战:双流计算通信重叠与 subtile 粒度 RS/AG 流水线设计(A2/A3)

2026/9/18 3:56:37 拓冰建站 浏览量
PTO 多卡 GEMM + AllReduce 融合算子实战:双流计算通信重叠与 subtile 粒度 RS/AG 流水线设计(A2/A3) PTO 多卡 GEMM AllReduce 融合算子实战双流计算通信重叠与 subtile 粒度 RS/AG 流水线设计A2/A3【免费下载链接】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导读本文以 CANN pto-isa 仓库中 kernels/manual/a2a3/gemm_ar 示例为对象完整剖析如何在 Ascend A2/A3910B上基于 PTO 虚拟指令集实现多卡GEMM AllReduce融合算子。该示例采用 Compute Stream24 AIC Comm Stream24 AIV双流架构通过逐 tile 的无锁 Ready Queue 与 subtile 粒度的 ready counter 同步实现了RSReduceScatter AGAllGather的流水化重叠。读完本文你将掌握双流内核的调度与信号机制、HCCL RDMA 窗口的分配方法、L1/L0 两级双缓冲 GEMM 流水线的代码级实现以及如何通过run.sh一键构建、运行并在 8 卡环境上复现 1.44x 的计算通信重叠收益。概览本示例演示如何使用 PTO 实现多卡 GEMM AllReduce 融合算子。计算侧每个 rank 独立完成本地 GEMM通信侧通过 PTO 通信指令集TLOAD/TSTORE/TTEST/TWAIT/TNOTIFY/TPUTAtomicAdd等在 HCCL RDMA 窗口上直接完成跨 rank 的归约与广播省去了传统框架中算子完成后统一走 HCCL 集合通信 API的整机级同步开销。核心设计要点双流计算通信重叠计算 kernel 运行在 Compute Stream24 AIC通信 kernel 运行在 Comm Stream24 AIV二者物理独立、可完全并行逻辑上仍是 RS AG执行上改成单循环 overlapRS 把数据归约到 owner rankAG 把 owner-local 结果广播到其它 rank两者通过 ready counter 在一个 subtile 粒度的混合循环里衔接不再依赖整机级 barrier无锁 Ready QueueAIC 与 AIV 之间通过 64 字节对齐的单生产者单消费者队列传递 tile 索引无需原子操作两级双缓冲流水线L1 缓存stepK4 批量 TLOAD L0 双缓冲ping/pong让 DMA 搬运与 Cube 计算尽可能重叠。支持的 AI 处理器A2 / A3性能数据以 8 卡 Ascend 910B 为参考平台目录结构kernels/manual/a2a3/gemm_ar/ ├── CMakeLists.txt # 构建配置3 个 targetcube kernel, vec kernel, host exe ├── run.sh # 一键构建运行脚本自动计算 HCCL_BUFFSIZE、发现 MPI 路径 ├── gemm_ar_config.h # 全局参数配置矩阵维度、tile 大小、block 数量 ├── main.cpp # 入口MPI 初始化、数据生成、HCCL 初始化、窗口分配、性能测量、验证 ├── gemm_compute_kernel.cpp # GEMM 计算内核Cube 架构L0C FP32→GM FP16 自动 cast ├── comm_kernel.cpp # 通信内核Vector 架构单 kernel 的 RS/AG overlap AllReduce ├── kernel_launchers.h # Host 侧 kernel launcher 声明 ├── common.hpp # HcclRemotePtr 设备端包装RDMA 窗口地址转换 ├── comm_context.h # HcclDeviceContext 结构体每 rank 的 RDMA 窗口地址 ├── ready_queue.hpp # 多 block 无锁 tile 队列compute→comm 信号机制 └── comm_mpi.h # MPI 动态加载包装dlopen/dlsym免硬链接依赖各文件职责清晰分层gemm_ar_config.h是全局参数的唯一事实来源所有源文件通过 include 共享gemm_compute_kernel.cpp与comm_kernel.cpp分别对应 Cube 与 Vector 两个设备侧内核main.cpp承担 Host 侧的初始化、benchmark 与验证ready_queue.hpp提供跨 AIC/AIV 的调度原语。算子说明计算功能本示例实现多卡 GEMM AllReduce$$ C_{final} \sum_{i0}^{nranks-1} A_i \times B $$其中A_i为M×K每 rank 独立B为K×N所有 rank 共享C_i为M×N每 rank 本地 GEMM 结果C_final为M×NAllReduce 归约后的最终输出gemm_ar_config.h 中默认的参考矩阵配置为M5416, K6144, N1408。从配置头文件可以看到所有宏都带有#ifndef保护允许通过编译期-DCONFIG_G_M...覆盖。下文性能数据使用 8 卡 Ascend 910B 作为参考平台。规格项目值OpTypeGEMM AllReduce输入A_i:M×K,float16,ND每 rank 独立;B:K×N,float16,DN共享输出C_final:M×N,float16,NDAllReduce 归约结果计算 Kernel 名称GemmComputeKernelCube 架构dav-c220-cube通信 Kernel 名称GemmCommAllKernelVector 架构dav-c220-vec优化说明本示例以 8 卡 Ascend 910BA2/A3平台作为性能验证平台。910B 采用分离模式架构24 个 AICCube Core负责矩阵计算24 个 AIVVector Core负责通信传输AIC 与 AIV 物理独立、可完全并行。双流计算通信重叠计算 kernel 运行在 Compute Stream24 AIC通信 kernel 运行在 Comm Stream24 AIV通过逐 tile 信号机制实现计算与通信并行。逻辑上仍是 RS AG执行上改成单循环 overlapRS 负责把数据归约到 owner rankAG 负责把 owner-local 结果广播到其它 rank两者通过 ready counter 在一个 subtile 粒度的混合循环里衔接。Block Swizzle计算 kernel 采用 zigzag tile 遍历顺序奇数行反向改善相邻 tile 间 B 矩阵的 L1 缓存复用。两级双缓冲流水线L1 缓存stepK4 批量 TLOAD L0 双缓冲ping/pong让 DMA 搬运与 Cube 计算尽可能重叠。无锁 Ready Queue每个 AIC 负责一条队列AIV block 静态接管队列子集{block_idx, block_idx num_comm_blocks, ...}。无数据时先TTEST轮询必要时再TWAIT避免空转。RS 双缓冲流水线RS 生产路径按 subtile 粒度用 ping/pong tile 重叠当前 subtile 的TLOAD与上一个 subtile 的TSTOREAtomicAdd。owner-local subtile 执行器每个 owner-local tile 会沿 M 维切成固定高度的 subtile默认G_COMM_SUB_M64行AG block 以 reversed-stripe 方式分配 subtile尽量把 RS 重块和 AG 重块错开。publish / consume fenceRS 只有在pipe_barrier(PIPE_ALL) dsb(DSB_DDR)之后才发布subtile-ready/ag-summary门铃AG 在一次 drain pass 的第一次命中时做 acquire fence然后批量消费 ready subtile。其中逻辑 RS AG、执行单循环 overlap的演进在 comm_kernel.cpp 的GemmCommAllImpl()中体现得最为直接主循环同时推进 RS 生产GemmCommTryRs与 AG 消费GemmCommTryAg只有当两个方向都无进展时才进入GemmCommWaitForWork阻塞等待。Tiling 参数参数值M原始5416K6144N原始1408M对齐后5504N对齐后1536baseM128baseK64baseN256stepKa4stepKb4commSubM64subtilesPerTile2tile 数25843×6COMPUTE_BLOCK_NUM24COMM_BLOCK_NUM24从 gemm_ar_config.h 可以看到 M/N 的对齐是编译期完成的G_M AlignUp(G_ORIG_M, G_BASE_M)、G_N AlignUp(G_ORIG_N, G_BASE_N)而 K 不做 padG_K G_ORIG_K因为 K 必须能被G_BASE_K × G_STEP_KA 256整除由static_assert(G_K_LOOP % G_STEP_KA 0)在编译期强制检查。tile 总数由G_M_TILES × G_N_TILES 43 × 6 258得到。整体架构┌──────────────────────────────────────────────────────────────────────────────┐ │ Compute Stream (24 AIC) Comm Stream (24 AIV) │ │ │ │ GemmComputeKernel: GemmCommAllKernel: │ │ ┌─────────────────────────┐ ┌──────────────────────────────┐ │ │ │ for each tile: │ │ RS/AG overlap loop │ │ │ │ K-loop (L1→L0→Cube) │ │ 轮询 Ready Queue │ │ │ │ TSTORE → gemm_output │──Ready──→ │ TLOAD tile from gemm_output│ │ │ │ pipe_barrier(ALL) │ Queue │ TSTOREAtomicAdd → owner │ │ │ │ Enqueue tile_idx │ │ subtile-ready / summary │ │ │ └─────────────────────────┘ │ 消费 ready subtile 做 AG │ │ │ │ TLOAD → TSTORE 到远端 rank │ │ │ │ ready 驱动的 AG 衔接 │ │ │ │ subtile 粒度 overlap │ │ │ │ │ │ │ └──────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────┘从 Host 侧视角main.cpp两个内核分别通过launchGemmCompute与launchGemmCommAll发射到独立的computeStream/commStream。Pipelined 模式下launchCommAsync不阻塞 Host两端最终以aclrtSynchronizeStream收口测得的墙钟时间即max(compute_end, comm_end)。计算内核详解计算内核的三级流水线时序如下时间 → L1 (MTE2): [TLOAD A0,B0] [TLOAD A1,B1] ... L0 (MTE1): [TEXTRACT k0] [k1] [k2] [k3] [TEXTRACT k0] ... Cube (M): [TMATMUL k0] [ACC k1] [ACC k2] [ACC k3] [TMATMUL k0] ... ↑ 三级流水线完全并行 ↑每个 AIC 负责一组 tile按block_idx × tiles_per_block分配对每个 tileBlock Swizzle 映射SwizzleTileIndex()gemm_compute_kernel.cpp将线性 tile 索引重映射为 zigzag 遍历顺序以SWIZZLE_GROUP G_N_TILES为组宽奇数行内ni反向ni SWIZZLE_GROUP - 1 - local使连续 tile 共享 B 矩阵列提升 L1 复用。K-loopProcessKIteration()gemm_compute_kernel.cpp每stepKa4次迭代做一次TLOAD一次搬入 4 个 K-slice 到 L1把 GM→L1 的 DMA 频率降为 1/4每次迭代TEXTRACT提取单个 K-slice 到 L0再TMATMUL/TMATMUL_ACC累积。这里TLOAD使用 ping/pong 双缓冲mte2DBFlag翻转TEXTRACT同样双缓冲mte1DBFlag翻转中间通过wait_flag/set_flag建立 MTE2→MTE1→Cube 的依赖链。TSTOREComputeAndStoreTile()gemm_compute_kernel.cpp中 L0C FP32 结果经 FixPipePIPE_FIX自动 cast 为 FP16写入gemm_output。pipe_barrier(PIPE_ALL)确保 GM 写入完成。MultiBlockEnqueueFast入队tile_idx通知通信 kernel。值得注意的是 L1 面板的内存排布gemm_compute_kernel.cppaMatTile[0]/[1]与bMatTile[0]/[1]通过TASSIGN显式落在不同 L1 偏移0x0、l1ASize、2*l1ASize、2*l1ASizel1BSize避免互相覆盖。通信内核详解当前真正被 launch 的通信路径是GemmCommAllImpl()里的 mixed subtile pipelinecomm_kernel.cppRS 生产和 AG 消费在一个循环里交错执行同步点是每个 subtile 的 ready counter而不是整机级 barrier。RS 生产路径每个通信 block 接管如下队列子集queues(block b) { b, b num_comm_blocks, b 2*num_comm_blocks, ... }当默认COMPUTE_BLOCK_NUM COMM_BLOCK_NUM 24时会退化成最熟悉的 1:1如果通信 block 更少一个 block 会通过RsPollQueues()/RsWaitOnQueue()轮询多条队列comm_kernel.cpp。对于每个出队 tile先沿M维切成G_COMM_SUBTILES_PER_TILE G_BASE_M / G_COMM_SUB_M个固定高度 subtile。RsPipelineStep()comm_kernel.cpp用 ping/pong UB tile 重叠当前 subtile 的TLOAD和上一个 subtile 的TSTOREAtomicAdd——TSTORE_IMPL..., AtomicType::AtomicAdd直接把数据累加到远端 owner 的reduced_output从而消除了传统 RS 中的独立 Reduce 阶段。RS 目标是owner tile_idx % nranks对应 rank 的reduced_output归约直接在 owner rank 完成远端地址由CommRemotePtr()common.hpp基于windowsIn[]换算localPtr - localBase windowsIn[pe]。RS/AG 重叠同步当前协议在 owner rank 的signal_matrix上维护两类计数器subtile-ready[local_subtile_id]记录这个 owner-local subtile 已经被多少个 rank 完成 RS。ag-summary[summary_block]给负责该 subtile 的 AG block 提供较粗粒度的唤醒门铃。signal_matrix的槽位布局在 gemm_ar_config.h 中由编译期常量精确刻画前MAX_RANKS2个槽保留给 legacy barrier 计数随后是G_SIGNAL_MAX_LOCAL_SUBTILES个 subtile-ready 计数器最后是COMM_BLOCK_NUM × 16个 AG summary 槽位每个槽位跨 64 字节独占一个 cache line避免伪共享。发布路径由RsPublishSubtileReady()comm_kernel.cpp完成先执行pipe_barrier(PIPE_ALL)刷本地流水。再执行dsb(DSB_DDR)确保reduced_output中的数据对其它 block / rank 可见。RsNotifySubtileReady()通过TNOTIFY(..., NotifyOp::AtomicAdd)递增 subtile-ready counter远端时经CommRemotePtr换算地址。RsNotifyAgSummary()递增AgSummaryBlockForSubtile()选中的 summary counter。消费路径由AgDrainReadyAssignedSubtiles()comm_kernel.cpp完成先用TTEST(..., nranks, GE)轮询自己负责的subtile-readycounter。一次 drain pass 第一次命中时执行一组 acquire fencepipe_barrier dsb后续本轮 ready subtile 共享这次 acquire。对每个 ready subtile 执行广播。如果本轮没有任何进展则由AgWaitAssignedSummary()在summary_ack_count 1上阻塞等待下一次属于本 block 的唤醒。AgSummaryBlockForSubtile()使用 reversed-stripe 映射comm_kernel.cppnum_comm_blocks - 1 - (local_subtile_id % num_comm_blocks)。这样做的原因在源码注释中写得很清楚RS 侧前remainder个 block 承担更重的ceil负载reversed 之后 AG 的重块恰好落在 RS 的轻块上从而拉平(rsNagN)的总工作量。AgInitAssignedState()中 AG block 的起始 id 同样从num_comm_blocks - 1 - comm_block_idx开始、步长为num_comm_blocks与上述映射保持一致。AG 执行路径AG 的分工是在 owner-local subtile 空间里完成的total_local_subtiles my_tile_count * G_COMM_SUBTILES_PER_TILE assigned_ids(block b) { num_comm_blocks - 1 - b k*num_comm_blocks }对每个已 ready 的 assigned subtileAgDecodeLocalSubtile()comm_kernel.cpp把 owner-local subtile id 还原成reduced_output中的全局行偏移先反解owner_local_tile id / SUBTILES_PER_TILE与stripe_id id % SUBTILES_PER_TILE再经global_tile my_rank owner_local_tile * nranks与(mi, ni)换算出行偏移。AgTransferSubtileToAll()comm_kernel.cpp把固定G_COMM_SUB_M行广播给所有远端 rank。第一个远端 peer 会按local_subtile_id % (nranks - 1)做轮转start_step 1 local_subtile_id % (nranks-1)避免所有 block 先打同一个目标 rank。现在 AG 只要看到某个 owner-local subtile 已被所有 rank 完成 RSTTEST命中nranks就可以立刻启动这一段广播无需等待整机 RS 结束。Ready Queue 机制┌─────────────┐ ┌─────────────┐ │ AIC 0 │ │ AIV 0 │ │ (Compute) │──Queue──│ (Comm) │ │ block_idx0│ 0 │ block_idx0│ └─────────────┘ └─────────────┘ ┌─────────────┐ ┌─────────────┐ │ AIC 1 │ │ AIV 1 │ │ (Compute) │──Queue──│ (Comm) │ │ block_idx1│ 1 │ block_idx1│ └─────────────┘ └─────────────┘ ... ... ┌─────────────┐ ┌─────────────┐ │ AIC 23 │ │ AIV 23 │ │ (Compute) │──Queue──│ (Comm) │ │ block_idx23│ 23 │ block_idx23│ └─────────────┘ └─────────────┘每个队列为 64 字节对齐的PerBlockQueue结构体ready_queue.hpp包含count生产者递增和data[]tile 索引数组。当前实现通过GetQueueSlot()对 slot 做显式寻址不再依赖隐含的data[idx]布局假设。生产者AICPerBlockQueueEnqueueFastready_queue.hpp通过GetQueueSlot()写目标 slot再递增count并用dcci刷新缓存保证 AIV 可见。注释指出 caller 自行跟踪 slot 位置把原本 5 次dcci降为 2 次。消费者AIVPerBlockQueueTryDequeueready_queue.hpp用TTEST检查count head1随后通过GetQueueSlot()刷新并读取目标 slot无数据时返回 -1长时间无数据时用TWAIT硬件等待。当COMM_BLOCK_NUM COMPUTE_BLOCK_NUM时一个通信 block 会按固定 round-robin 次序接管多条队列因此无需跨 block 原子争抢。单生产者单消费者设计无需原子操作。MultiBlockQueueSet头部的queue_offsets[]记录了每条队列在整块内存中的偏移Host 侧通过MultiBlockQueueSetInit初始化后一次性aclrtMemcpy到设备main.cpp设备侧GetMyBlockQueue()先dcci再读偏移。内存布局与 HCCL 窗口只有被远端 TPUT/TNOTIFY 写入的 buffer 需要放在 HCCL RDMA 窗口中本地读写的 buffer 使用普通aclrtMalloc。缓冲区大小位置原因reduced_outputM × N × 2BHCCL 窗口RS AtomicAdd AG 远端 TPUT 写入FP16signal_matrixG_SIGNAL_TOTAL_SLOTS× 4B对齐 64BHCCL 窗口subtile-ready / AG summary 计数器含保留 legacy barrier 槽位gemm_outputM × N × 2BaclrtMalloc仅本地读写FP16src0_dev,src1_dev输入矩阵FP16aclrtMalloc仅本地读写窗口内 buffer 的分配在 main.cpp 中通过WindowAlloc()完成以hostCtx.windowsIn[rankId]为窗口基址、按偏移依次分配reduced_output与signal_matrix并校验winOffset winSize超出即报HCCL window too small。窗口内 buffer 必须在每个 rank 上分配在相同偏移处——这是CommRemotePtr()能直接做地址平移的前提。窗口大小由HCCL_BUFFSIZE环境变量控制。run.sh 会基于 pad 后的reduced_output大小估算窗口需求并额外预留较大的安全边界pad(M, G_BASE_M) × pad(N, G_BASE_N) × 2 / 1MB 64MB若当前HCCL_BUFFSIZE小于该估算值脚本自动抬高并打印 INFO 日志。signal_matrix也在同一个窗口里但相对这 64MB margin 很小。HCCL 上下文初始化main.cpp区分两条路径MESH 拓扑A2直接复用HcclAllocComResourceByTiling返回的CommDeviceContextRING 拓扑A3则从CommOpResParam的remoteRes数组手工提取各远端 rank 的windowsIn地址重建 Host 侧上下文再拷回设备InitRingPath。实测性能参考以下数据基于当前subtile-ready / AG-summary overlap实现在8卡 Ascend 910B 上测得参数M5416, K6144, N1408padded5504×1536258 tiles (43×6)compute_blocks24comm_blocks24。每 rank 计算完整 GEMMC_i A_i × BAllReduce 对8个C_i求和。指标值Compute-only368.1 us254546 GFLOPSSequential808.9 uscompute371.6 us comm437.3 us 63.6 GB/sPipelined560.6 uscompute done367.2 uscomm done560.6 us 49.7 GB/sSpeedup1.443xTime saved248.4 us30.7%Overlap eff66.8%Throughput1337307 GFLOPStotal性能指标的定义可以在 main.cpp 的PrintTimingDetails()中找到对应实现GFLOPS 按2·M·K·N / time计算通信带宽按(rs_bytes ag_bytes) / time计算overlap_eff定义为(seq_comp seq_comm - pipe) / min(seq_comp, seq_comm)。这些数字意味着什么Compute-only纯 GEMM 时间无通信反映单卡 Cube 利用率上限。当前纯算时间为368.1 us对应254546 GFLOPS。Sequential计算→通信串行执行无重叠。当前串行总时间为808.9 us其中 compute371.6 us、comm437.3 us。Pipelined计算与通信双流并行。当前Pipelined 560.6 us相比Sequential 808.9 us加速1.443xcompute done 367.2 us基本与纯算持平。SpeedupSequential / Pipelined越高说明计算通信重叠越有效。Time saved相对串行路径节省的总时长。当前节省248.4 us约占30.7%。Overlap eff重叠带来的时间节省占较短阶段时间的百分比。66.8%表明当前大约三分之二的较短阶段时间已被成功隐藏。优化历程下表前几行是历史优化检查点最后一行是当前subtile-ready / AG-summary overlap实现的最新端到端结果。不要把这些历史检查点误解成当前 live path 的逐项开关。优化Pipelined (us)增益结论基线808——Block Swizzle793-1.8%保留RS AtomicAdd 消除 Reduce 阶段736-6.6%保留AG 行级展平分解623-15.4%历史检查点48 AIVRS skip AG 参与639RS 仅 24 AIV、AG 48 AIV回退AIC 干扰48 AIV 双队列1 AIC : 2 AIV667RS/AG 均 48 AIV回退AIC 干扰当前 subtile-ready / AG-summary overlap560.6相比623 us历史检查点再降约10.0%当前结果性能优化指南如何调这个 kernel1) 优先做多核切分每个 AIC 按block_idx × tiles_per_block分配 tile 子集block 之间互不干涉。tile 分配的均衡性由 gemm_ar_config.h 中的GEMM_AR_BLOCK_TILE_COUNT/GEMM_AR_BLOCK_START_TILE宏保证当总 tile 数不能被 block 数整除时把余数分摊给前remainder个 block使每个 block 要么拿floor(T/N)要么拿ceil(T/N)避免出现一个 dwarf block的经典负载失衡。检查清单调整COMPUTE_BLOCK_NUM使每个 block 承担接近相等的 tile 数。对于不同形状的矩阵重新计算 tile 总数G_NUM_TILES (M_padded/128) × (N_padded/256)。2) 选择合适的 base tileL0A 与 L0B 使用双缓冲ping/pong每个 buffer 上限 32 KiB。对于 FP16 输入2 bytes/elemL0A tile bytes ≈baseM × baseK × 2128 × 64 × 2 16 KiBL0B tile bytes ≈baseK × baseN × 264 × 256 × 2 32 KiB通信 tile 大小为baseM × baseN × sizeof(FP16) 128 × 256 × 2 64 KB。3) 用 L1 的 stepK 缓存提升复用stepKastepKb4一次 TLOAD 搬入 4 个 K-slice 到 L1后续 TEXTRACT 逐个提取到 L0。L1 使用量2×64KB(A) 2×128KB(B) 384KB ≤ 1024KBL1 总容量。增大stepK可减少 DMA 启动开销但须确保不超过 L1 容量。4) 保持流水线重叠计算 kernel 内部的双缓冲L1/L0A/L0B 计算与通信之间的双流重叠是性能核心。当你观察到通信时间远大于计算时间→ 计算侧已充分优化重点优化通信效率或增大重叠。计算时间远大于通信时间→ 通信已被完全隐藏重点优化计算侧。5) 调整通信 block 数COMM_BLOCK_NUM控制通信 kernel 的 AIV 并行度。通过--comm-blocks参数调整。注意在 910B 上实测发现将COMM_BLOCK_NUM从 24 提升到 48使用全部 AIV会导致 AIC 计算时间显著增加24%原因是 HBM 带宽争用和 TSCH 调度开销。当前最优配置为 24 AIV。这一结论与上文优化历程中48 AIV 两条路径均回退的实测相互印证。6) 约束条件K 必须能被G_BASE_K × G_STEP_KA默认 64×4256整除。M 自动 pad 到 128 对齐N 自动 pad 到 256 对齐。所有窗口内 buffer 必须在每个 rank 上分配在相同偏移处。signal_matrix在每轮迭代开始前通过aclrtMemset清零见 main.cpp 的ResetDeviceState()。构建与运行配置 Ascend CANN 环境export ASCEND_CANN_PATH/usr/local/Ascend/cann-version/set_env.sh source ${ASCEND_CANN_PATH}激活 conda 环境需含 Python NumPyconda activate your-conda-env运行示例8 卡cd ${git_clone_path}/kernels/manual/a2a3/gemm_ar ./run.sh --nranks 8 --soc-version Ascend910B1指定起始设备编号FIRST_DEVICE0 ./run.sh --nranks 8 --soc-version Ascend910B1自定义 block 分配./run.sh --nranks 8 --compute-blocks 20 --comm-blocks 4成功时输出GEMM AllReduce demo completed successfully.run.sh内部的关键流程run.sh若未设置ASCEND_CANN_PATH自动 glob/usr/local/Ascend/cann-*/set_env.sh取最新版本在MPI_SEARCH_DIRS指定的目录中寻找mpirun并据此导出MPI_LIB_PATH供运行时dlopen每次运行前清理/dev/shm/sem.hccl*与共享内存ipcrm -a避免上次异常退出残留脏状态计算 pad 后的 M/N自动抬高HCCL_BUFFSIZE将--compute-blocks/--comm-blocks转换为-DCOMPUTE_BLOCKS/-DCOMM_BLOCKS编译宏传入 CMake最后mpirun -n ${NRANKS} ./gemm_allreduce --first-device ${FIRST_DEVICE}拉起多进程。环境变量说明环境变量用途默认行为ASCEND_CANN_PATHCANNset_env.sh的完整路径自动 glob/usr/local/Ascend/cann-*/set_env.sh取最新版MPI_SEARCH_DIRSMPIbin/目录搜索路径空格分隔搜索/usr/local/mpich/bin、/home/mpich/bin等常见路径ASCEND_DRIVER_PATHAscend driver 路径CMake 使用默认/usr/local/Ascend/driverMPI_LIB_PATHlibmpi.so绝对路径运行时动态加载由run.sh根据找到的 MPI 自动设置HCCL_BUFFSIZEHCCL RDMA 窗口大小MB由run.sh根据 M/N 自动计算FIRST_DEVICE起始 NPU 设备编号默认 0修改矩阵维度修改 gemm_ar_config.h 中的CONFIG_G_M、CONFIG_G_K、CONFIG_G_N即可所有源文件通过 include 共享配置。也可通过 CMake 参数传入cmake -DCONFIG_G_M8192 -DCONFIG_G_K8192 -DCONFIG_G_N2048 ..约束K 必须能被G_BASE_K × G_STEP_KA默认 64×4256整除。HCCL_BUFFSIZE由run.sh自动计算。从 CMakeLists.txt 可以看到CONFIG_G_M/G_K/G_N与CONFIG_G_BASE_M/G_BASE_K/G_BASE_N均通过add_compile_definitions注入进而被gemm_ar_config.h的#ifndef守卫捕获COMPUTE_BLOCKS/COMM_BLOCKS则分别注入 cube kernel 与两个 kernel 的编译定义。常见问题问题原因与解决HCCL window too small窗口需要覆盖 pad 后的reduced_output和signal_matrix。先检查是否手工覆盖了HCCL_BUFFSIZErun.sh默认会按pad(M) × pad(N) × 2 / 1MB 64MB自动抬高HcclGetRootInfo failed: 7上次运行残留脏状态。执行rm -rf /dev/shm/sem.hccl*; ipcrm -a或等待 ~30s 重试HCCL 初始化后挂死rank 同步问题检查所有 rank 是否到达CommMpiBarrier通信 kernel 段错误通常是窗口地址无效验证windowsIn[]值非零signal wait 卡死signal_matrix 未在迭代间清零或 subtile-ready / AG summary 计数映射有误先检查resetState是否 memset 了 signal_matrix验证失败 max_diff 较大FP16 精度有限验证容差为 atol1.0, rtol0.01若 diff 异常大检查 subtile-ready / AG summary 同步与 owner 映射aclInit repeat init(100002)无害代码已做保护同一进程只调用一次aclInit--allow-run-as-root失败本项目使用 MPICH此选项是 OpenMPI 专用验证逻辑main.cpp以 CPU 上的 FP32 GEMM 结果为 golden按atol rtol·|exp|容差逐元素比较max_diff/err_count会打印在[VERIFY]日志中。构建系统编译器bishengCANN 内置 clang 15.0.5Cube kernel--cce-aicore-archdav-c220-cube -DMEMORY_BASEVec kernel--cce-aicore-archdav-c220-vec -DMEMORY_BASEHost 可执行文件-xc标准编译链接库runtime、ascendcl、hcomm、tiling_apipto-comm-isa 的 include 路径必须放在首位以覆盖 CANN 自带的pto_tile.hpp从 CMakeLists.txt 可以看到include_directories的第一项是../../../../include即仓库根目录的 include 目录这正是 pto-comm-isa 头文件所在位置。此外CMAKE_CCE_COMPILE_OPTIONS中的--cce-pto-enable开关启用 PTO 指令支持两个内核 target 分别通过--cce-fatobj-link产出 fatbin 供 Host 侧动态加载。三个 targetgemm_compute_kernel、comm_kernel、gemm_allreduce由 CMakeLists.txt 定义Host 可执行文件同时链接两个 kernel 库与ascendcl/hcomm/tiling_api等运行时库。变更记录日期变更2025-12-15初始版本实现 GEMM AllReduce 双流融合算子2026-04-01兼容 CANN 9.0.0移除已废弃的 hccl/hccl.h 依赖2026-04-02RS AtomicAdd 消除 Reduce 阶段AG 行级展平分解优化负载均衡更新架构文档2026-04-21RS - DeviceBarrier - AG通信模式改成subtile-ready / AG-summary overlap模式如需进一步深入建议对照阅读以下仓库文件示例目录的 README_zh.md本文档、README.md英文版、通信指令集说明 docs/isa/comm、PTO 编程模型 ProgrammingModel_zh.md以及通信指令封装头文件 include/pto/comm。【免费下载链接】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),仅供参考