解读 PTO 通信指令集:昇腾多卡数据搬移的 N 种路径
PTOParallel Tile Operation是昇腾 CANN 定义的一套面向 tile 编程的虚拟 ISA。如果你还不了解 PTO 的 Tile 概念和整体设计思路推荐先阅读《浅谈昇腾虚拟指令集 PTO》系列。本文聚焦 PTO ISA 的通信扩展指令集——当计算从单卡走向多卡数据搬运的故事就从”核内物流”升级成了”城际运输”。一、单卡是工厂多卡就是工业园区前几篇文章里我们把昇腾芯片比作一座精密的工业园区矩阵单元是生产车间片上 Buffer 是临时仓库。在单卡内部MTE 就像车间和仓库之间的传送带。PTO 指令集就是这座园区的调度五线谱让各个加速器有节奏地运转。这个比喻在单卡内部很好用。但大模型训练从来不是一个人的事——8 张卡、64 张卡、上千张卡每张卡都在算自己的那一份然后把结果汇总。当数据需要跨卡时“核内传送带”就必须升级为“跨厂区物流”。问题来了工厂和工厂之间怎么运货想象一下你是一个工业园区的物流调度员。园区里有几十座工厂分布在不同的区域。有些工厂紧挨着走廊里推个小车就到了有些隔着几个街区得叫专门的货车有些在另一个城市得走高速。更头疼的是每一代园区扩建运输基础设施都在变。老园区只有电动三轮和小货车新园区修了高速公路最新的园区甚至建了专门的集散中心。如果每次扩建所有工厂的物流流程都要重写一遍那还了得你需要的是一套统一的运单格式不管底层走三轮还是走高速运单上写的东西不变。这就是 PTO 通信指令集要解决的问题。二、昇腾芯片上的”N种运输方式”在展开 PTO 通信指令之前先看看昇腾硬件到底提供了哪些跨卡运输通道。先看一眼昇腾的”仓库体系”数据在昇腾芯片里的存储是分层级的就像一座工厂里有不同级别的仓库昇腾芯片的仓库体系与搬运路线GMGlobal Memory片上内存容量最大所有 AICore 共享一般是跨卡通信的起点和终点。UBufferUnified Buffer核内 SRAM容量较小但速度极快AIVAI Vector单元的向量计算和细粒度数据搬运都围绕它进行。L0A/L0B/L0C、L1更靠近计算单元的缓存层级主要用于矩阵乘的流水线。本文探讨的跨卡通信主要聚焦于“本地 GM到远端 GM”这条主干道。注虽然昇腾系统也支持从本地 UBuffer 直接发货到远端 GM但这属于 MTE 引擎的专属路线。为统一步调下文主要围绕 GM 间的搬运展开。搬运引擎谁来搬、怎么搬昇腾跨卡通信的四种运输方式对比结合上图展示的“物流路线”我们可以把昇腾上多种搬运引擎的核心差异梳理成一张详细的“运力配置表”技术名称谁在搬怎么搬适合什么代际MTEAIV 亲自动手经 UBuffer 中转同步灵活支持 2D stridedtile 级精细控制Ascend 910/950 全系列SDMA委托专用 DMA 引擎GM 直传异步大块连续数据AIV 可以继续干别的Ascend 910/950 全系列URMA委托硬件 RDMA 引擎GM 直传异步高带宽用户态 RDMAAscend 950CCU委托专用协处理器片上 Reduce 传输集合通信硬件卸载Ascend 950打个比方MTE 就像你亲自推着小车送货——灵活、精确、随叫随到启动延迟极低但你在送货的时候就干不了别的这就是所谓的同步操作。SDMA/URMA 则像你把包裹交给快递员——你解放了双手可以继续干活这就是所谓的异步操作。但叫快递员需要填单子、等揽收存在较高的启动开销如果是极小块的数据和较短的传输距离可能还不如你自己送得快。CCU 是950代际新增的集合通信处理器单元更像一个专门的分拣中心你把货扔过去它在内部完成归拢和分发。注意最后一列——不同代际的芯片搬运能力各有侧重。910系列有 MTE 和 SDMA950 还新增了 URMA 和 CCU。这就引出了 PTO 通信 ISA 的核心设计哲学一套运单N种运法。你填运单按需选路。三、亲自送货TPUT / TGET最朴素的跨卡搬运就是用 MTE 引擎进行跨卡的GM到GM传输。数据的旅程是本地 GM → UBuffer tile中转仓库→ 远端 GM就像你亲自推车送货路线灵活、什么形状的 tile 都能搬、2D strided 布局也没问题。代价是你在搬货的时候没法干别的——MTE 是同步的AIV 必须等搬完才能继续。但它胜在哪儿都有——从 910 到 950MTE 引擎全系列标配是 PTO 通信的”保底路径”。为什么不是直接 GM到 GM因为MTE引擎的硬件通路天生就是为了给计算核心喂数据设计的。它的读写通路是分离的读指令MTE2只能把数据从 GM 搬进核内的 UBuffer写指令MTE3只能从 UBuffer 搬回 GM。注MTE1 通常用于 L1 到 L0 的核内搬运。虽然多了一道中转但这个过程 PTO 帮你全自动处理了TPUT 的三大自动化搬运特性货太多装不下自动分批。TPUT 会把超大 tensor 按行列切块2D sliding逐块搬运对你完全透明。想加快速度用两个中转仓库轮流装卸Ping-Pong 双缓冲当前这批在卸货的同时下一批已经开始装了。想在对面直接累加支持 AtomicAdd远端那边收到数据直接加上去不用先存再加。注以下接口示例以 PTOAS 风格伪代码展示底层同样提供 C DSL。# 最简单的远端写 pto.comm.tput(dst_gm, src_gm, staging_tile) # 双仓库轮流装卸Ping-Pong 双缓冲 pto.comm.tput(dst_gm, src_gm, ping_tile, pong_tile) # 远端直接累加硬件原子加在目标侧完成 pto.comm.tput(dst_gm, src_gm, staging_tile, atomicAtomicAdd)三行代码三种模式。底层调了多少个 MTE 指令、怎么切块、怎么流水线全部被TPUT这一条 PTO指令藏起来了。这就是 PTO 通信指令和裸 MTE 调用的本质区别你操作的不是底层搬运引擎而是tile级的语义——“把这块数据搬到那边去”。四、委托搬运TPUT_ASYNC / TGET_ASYNC同步路径的问题很明确搬运期间AIV干等着。你亲自推车去送货送货的时候产线就停了等你回来才能继续干活。对于追求高效率的大模型训推来说这不能接受。核心思想把包裹交给快递员异步路径的哲学很简单别亲自送了委托给专用的搬运引擎。发完搬运指令不用等搬运完成你接着干你的活。等活儿干得差不多了再去查一下”货到了没“。本地 GM → DMA 引擎后台搬运→ 远端 GM数据不再经过 UBuffer 中转而是在两端 GM 之间直传。“DMA 引擎”具体是哪个这取决于你在哪一代芯片上、用哪条指令。SDMA路径SDMASystem DMA是独立于 AICore 的专用 DMA 引擎。在PTO的设计里面AIV 通过 TPUT_AYSNC 指令把搬运任务转换成向 SDMA 硬件提交的传输描述符——相当于你把包裹和地址填好交给快递员然后回去继续工作。# 叫一个快递员构建会话 # scratch_type 定义了这块剪贴板的规格通常只需 256B 的极小 UBuffer 空间 scratch pto.alloc_tile(scratch_type) session pto.comm.build_async_session(scratch, workspace) # 下发搬运单立即返回不等完成 put_event pto.comm.tput_async(dst, src, session) # ... 你继续干活 ... # 最后确认货都到了吗 pto.wait_async_event(put_event)这里有个细节值得一提代码里的 scratch tile 并不是用来装货的“中转箱”而是 AIV 和快递员SDMA交接用的“调度剪贴板”。AIV 在这块极小的 UBuffer 空间里填写发车指令更新 Queue Tail、查收回执Polling Flags。货物本身完全由快递员从本地GM直接拉到远端 GM。URMA路径Ascend 950 新增到了昇腾 950新增了URMAUB远程内存访问UB Remote Memory Access——基于Unified Bus 灵衢互联能力的硬件用户态 RDMA。相当于园区升级了更高效的自动化物流系统。对用户来说URMA和 SDMA仍然是同一套异步接口先建会话再下发搬运单最后 wait 或 test 完成态。真正选走 SDMA 还是 URMA通常由更外层的引擎参数或目标平台决定。底层硬件机制的巨大差异被PTO的指令抽象完美地屏蔽掉了。开发者只需要关心“货送到了没”而不需要关心底层是怎么查收的。还有一个很优雅的设计叫Quiet语义你连续发了 10 张搬运单最后只需要查最后一张的状态。如果最后一张到了前面 9 张一定也到了。这就像是物流的“批量签收”只要这批货的最后一个包裹扫码入库了前面的包裹肯定已经在仓库里了。一次确认全部生效。把这些部件放到一张图里看会更容易把”后台直传 会话控制 Quiet 语义”三件事串起来TPUT_ASYNC 和 TGET_ASYNC 机制图核心设计同一张运单自动选路现在问题来了Ascend 910系列上该走 SDMAAscend 950上该走 URMA程序员需要写两份代码吗不需要。PTO 用了一套非常干净的编译期分派机制。以 TGET_ASYNC 的底层实现为例简化template DmaEngine engine DmaEngine::SDMA AsyncEvent TGET_ASYNC_IMPL(dst, src, session) { if constexpr (engine DmaEngine::SDMA) { return SDMA_IMPL(dst, src, ...); // 所有平台走 SDMA } else if constexpr (engine DmaEngine::URMA) { return URMA_IMPL(dst, src, ...); // Ascend 950 走 URMA } }if constexpr 是编译期分支——编译完成后不走的分支直接消失零运行时开销。TGET_ASYNC 在 Ascend 910/950 上都走 SDMA 后端950 上还可以选择走 URMA。这就是”一套运单灵活选路”的技术实现。同一行用户代码底层走哪条路、是否需要回退全部由编译宏和 if constexpr 自动决定。五、信号指令TNOTIFY / TWAIT / TTEST光有搬运还不够。工厂之间需要协调“我这批货发出去了你可以开始用了”“我在等你那边的原料到了通知我”。PTO 提供了三条信号指令就像工厂之间的信号灯系统TNOTIFY给对方亮绿灯“我搞定了”TWAIT盯着信号灯等“你搞定了我再动”TTEST瞄一眼信号灯“搞定没没搞定我先干别的”信号支持原子累加和直接设置两种模式比较条件支持 EQ/NE/GT/GE/LT/LE 六种。更精妙的是支持2D Signal Grid——不是一盏灯而是一整面信号墙适合多 rank 多 tile 的复杂协同场景。PTO 信号指令TNOTIFY / TWAIT / TTEST这三条指令和搬运指令组合起来就构成了完整的跨卡通信协议。后面的实操案例会展示它们如何像齿轮一样咬合在一起。六、集散中心走向 CCU硬件卸载前面说的 MTE、SDMA、URMA本质上都是点对点搬运——从 A 搬到 B。但大模型训练中有一类操作更复杂AllReduce——8 张卡各算了一份结果需要全部加起来再把总结果发给所有人。传统做法是让 AIV 一个个搬、一个个加拼装出 AllReduce 的效果。就像没有物流中心的年代工厂之间互相派货车送货然后各自手工汇总。昇腾 950 带来了新的选择CCUCollective Communication Unit。CCU是什么一句话它是一个建在IO-Die上的专用集散中心。它不是通用计算核心而是专门做集合通信的硬件协处理器。有自己的微码指令集Load/Ctrl/Trans/Reduce 四类有自己的片上缓冲区Memory Slice有硬件 Reduce 引擎直接在片上做加法/取最大值数据不用搬回 AICore在集散中心内部就处理完了关于 CCU 硬件架构的详细分析推荐阅读《集合通信处理器CCU技术解读文档》。PTO怎么和 CCU 对接PTO 把 CCU 的使用拆成两段就像你在物流平台上发快递Host侧平台下单编译 CCU kernel → 注册 → Launch产出一个”取件码”CcuDeviceSessionDevice侧工厂协作AIV 拿着取件码给 CCU 亮绿灯AIV_LAUNCHCCU 干活干完了亮回来CCU_DONEAIV不参与实际的搬运和归约——它只负责握手信号。真正的重活全部由 CCU 硬件完成。对于 GEMM_AR 这样的通算融合场景协议变成了逐 tile 级的流水线握手AIC 算完一个 tile → AIV 亮灯”这块 ready 了” → CCU 取走做 reduce → CCU 亮灯”这块搞定了” → AIV 继续下一块。这就像工厂的产线和集散中心之间实现了无缝对接边生产边发货。接口一致性在 PTO 指令层面CCU 路径和传统路径复用了相同的 API# 传统方式AIV 自己搬 pto.comm.tbroadcast(group, src_gm, staging_tile) # CCU 方式加一个 session 参数切换到硬件卸载 pto.comm.tbroadcast(group, src_gm, staging_tile, sessiondevice_session)多传一个参数搬运方式就从”自己动手”变成了”委托集散中心”。目前 CCU 路径已完成 CPU-SIM 功能仿真验证真实硬件平台的对接正在紧锣密鼓地进行中。七、实操GEMM AllReduce边算边搬说了这么多运输方式它们在实际场景中是怎么配合的以 pto-isa 仓库中的 GEMM AllReduce 融合算子为例。这是一个 8 卡场景每张卡算一部分矩阵乘然后把结果 AllReduce 汇总。传统做法的问题先算完 GEMM再做 AllReduce。两个阶段完全串行。就像工厂先把一整批货全部生产完堆在仓库里再统一发货。产线空闲的时候物流在跑物流空闲的时候产线在跑。两边互相等谁都没吃饱。PTO的做法边生产边发货910B 的架构天然支持这种并行——24 个 AIC计算核和 24 个 AIV通信核物理独立可以完全并行工作。AIC 产线: [GEMM tile 0] [GEMM tile 1] [GEMM tile 2] ... ↓ ready ↓ ready ↓ ready AIV 物流: [搬 tile 0] [搬 tile 1] [搬 tile 2] ...下图把这种”边算边搬”的流水线重叠关系画得更直观PTO 通算融合GEMM 计算与 AllReduce 通信重叠示意关键的PTO 通信指令各司其职TPUTAtomicAddReduceScatter 阶段的搬运工具。AIV 把本地 GEMM 结果直接原子累加到 owner rank 的地址——硬件原子加在目标侧完成省去了独立的Reduce步骤。TTESTAIV 用它非阻塞地”瞄一眼”Ready Queue——“AIC 那边有新 tile 算完了吗”有就搬没有就用 TWAIT 硬件等待避免空转。TNOTIFYTWAITDeviceBarrier 的实现——block 0 用 TNOTIFY 通知所有远端 rank “我这一轮搞定了”再用 TWAIT 等所有人都到齐。实测收益打破算子边界的红利在 8 卡 910B 环境下BF16 数据类型M5416, K6144, N1408我们简单对比了传统串行和 使用PTO Tile指令实现计算-通信流水线的表现模式耗时说明纯计算365 us257 TFLOPS98% 峰值利用率先算后通串行743 us计算 368us 通信 375us边算边搬流水线631 us加速 1.18x31%通信被掩盖可以看到高达 31% 的通信开销被成功“掩盖”在了计算过程中。这种级别的 Overlap 是传统的 Host 侧Kernel调度根本无法做到的。这就是引入 Device侧通信指令的核心价值它彻底打破了算子边界的黑盒。传统 Host 调度的最小单位是一整个算子——必须等矩阵乘全部算完才能启动通信算子。而 PTO 将调度的权力下放到了 Kernel 内部让开发者能以 Tile 为粒度精确编排“算完一块、搬走一块”的微观流水线。当调度的粒度细化了一个数量级性能优化的空间自然就豁然开朗了。八、一套运单跨三代硬件回到开头的比喻PTO 通信 ISA 就是那套跨越园区代际的“统一运单格式”。用户代码PyPTO 脚本 / PTOAS 代码/ C 算子代码填写运单 ↓ PTOAS 编译层 / pto_comm_inst.hpp运单受理窗口 ↓ pto_comm_instr_impl.hpp编译期选路 ↓ NPU: MTE / SDMA / URMA / CCU | CPU: 仿真桩在这套体系下十余条精简的通信指令覆盖了从点对点到集合通信的完整场景类别指令类比同步搬运TPUT, TGET亲自推车异步委托TPUT_ASYNC, TGET_ASYNC叫快递员信号协调TNOTIFY, TWAIT, TTEST信号灯集合操作TGATHER, TSCATTER, TBROADCAST, TREDUCE分拣中心而当这份“运单”下发到底层时PTO 编译器会自动根据目标芯片的代际将其映射到最优的硬件通路上。一张表看懂这种跨代兼容性平台同步亲自搬异步委托搬集合通信Ascend 910系列MTESDMAAIV 自己搬Ascend 950MTESDMA / URMACCU 硬件卸载这意味着你只需写一次代码编译到不同平台时PTO就会自动为你选择最合适的硬件路径。这就是 PTO 通信 ISA 的核心价值它并不是生硬地外挂了一套通信 API而是在 Tile 编程模型的土壤里原生地长出了通信能力。计算与通信共享同一套 Tile 抽象、同一套事件同步机制、同一套跨代兼容架构。从此计算与通信不再是割裂的两个孤岛而是统一编程模型下协同运转的精密齿轮——这正是大模型时代打破通信瓶颈、走向极致 Overlap 的必由之路。PTO 通信 ISA 的代码已开源在 pto-isa 仓库欢迎体验和反馈。PTO ISA开源仓https://gitcode.com/cann/pto-isa/tree/master/include/pto/comm加入我们以码会友微信社群飞书社群