PTO TPARTADD 指令全解析:Ascend CANN 部分有效区域逐元素加法的语义、约束与多平台实现
【免费下载链接】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
TPARTADD 是 Ascend CANN Parallel Tile Operation(PTO)虚拟指令集中用于在目标有效区域(valid region)内执行逐元素加法的核心指令。与普通 TADD 不同,TPARTADD 允许两个输入 Tile 的有效区域与目标不一致,从而高效支撑 padding、掩码、动态形状等场景下的部分数据叠加。本文将围绕 TPARTADD 指令文档 展开,完整讲解其数学语义、汇编语法(AS Level 1/2)、C++ 内建接口、各平台实现约束,并结合仓库内 CPU 仿真、Atlas A2/A3 与 Ascend 950 系列(A5)的源码实现与测试用例,帮助读者掌握该指令的精确行为边界与实战用法。
指令示意图
指令概述与核心语义
TPARTADD 在目标有效区域上执行逐元素加法,其特殊性在于对输入有效区域不做"完全一致"的强约束:当某个元素位置(i, j)上src0与src1同时有效时,结果取二者之和;当只有其中一个输入有效时,结果直接拷贝该输入的值;其余有效区域不匹配的组合行为由具体实现定义。
对目标有效区域内的每个元素(i, j),其数学语义可以精确表述为如下分段函数:
$$ \mathrm{dst}{i,j} = \begin{cases} \mathrm{src0}{i,j} + \mathrm{src1}{i,j} & \text{if both inputs are defined at } (i,j) \ \mathrm{src0}{i,j} & \text{if only src0 is defined at } (i,j) \ \mathrm{src1}_{i,j} & \text{if only src1 is defined at } (i,j) \end{cases} $$
理解"有效区域"(valid region)
在 PTO 的 Tile 模型中,每个 Tile 除了声明逻辑形状(如16x16)外,还通过GetValidRow()/GetValidCol()描述当前实际参与计算的有效行数与有效列数。TPARTADD 的计算域完全由dst的有效区域决定("The destination valid region defines the result domain"),而非由 Tile 的物理形状决定。这一设计使得同一份 Tile 形状可以承载动态的有效范围,是算子侧实现动态形状、padding、部分写入等逻辑的基础。
支持的"部分有效区域"模式
指令文档给出的通用约束明确限定了合法使用模式:
dst、src0、src1的元素类型必须一致;- 目标有效区域定义结果的计算范围;
- 对目标有效区域内的每个元素:两个输入都有效则执行逐元素加法;仅一个输入有效则结果取该输入值;
- 若
dst的有效区域为零(有效行或有效列为 0),指令直接返回,不产生任何计算; - 支持的部分有效区域模式要求:至少有一个源 Tile 的有效区域与
dst完全一致,另一个源 Tile 的有效区域在两个维度上都不能超过dst; - 上述范围之外的有效区域组合,其行为均由具体实现定义(即用户不应依赖未列举模式的结果)。
这条"一源对齐、一源不越界"的约束在 Atlas A2/A3 实现 中有对应的运行时断言佐证:
bool condSrc0EqDst = (src0ValidRow == dstValidRow && src0ValidCol == dstValidCol); bool condSrc1EqDst = (src1ValidRow == dstValidRow && src1ValidCol == dstValidCol); PTO_ASSERT( condSrc0EqDst || condSrc1EqDst, "Fix: TPARTADD At most one entry in the valid-rows and valid-cols of src0 and src1 is smaller than dst.");即 src0 与 src1 中至多一个的(有效行,有效列)可以比 dst 更小,另一个必须与 dst 完全相等,这与文档描述完全一致。
汇编语法
TPARTADD 在 PTO 汇编体系中有三种表达形式,覆盖从同步伪指令到 SSA、再到 DPS(Destination-Operand-Source)风格的完整层次。
同步形式(PTO Assembly Form):
%dst = tpartadd %src0, %src1 : !pto.tile<...> -> !pto.tile<...>AS Level 1(SSA 形式):使用pto.前缀与多操作数类型签名,显式声明两个输入与一个输出的 Tile 类型:
%dst = pto.tpartadd %src0, %src1 : (!pto.tile<...>, !pto.tile<...>) -> !pto.tile<...>AS Level 2(DPS 形式):采用ins(...)/outs(...)操作数分组,输入与输出从 SSA 值细化为具体的!pto.tile_buf<...>缓冲区:
pto.tpartadd ins(%src0, %src1 : !pto.tile_buf<...>, !pto.tile_buf<...>) outs(%dst : !pto.tile_buf<...>)C++ 内建接口
TPARTADD 的 C++ 内建接口声明于 include/pto/common/pto_instr.hpp,公共包含头为<pto/pto-inst.hpp>:
template <typename TileDataDst, typename TileDataSrc0, typename TileDataSrc1, typename... WaitEvents> PTO_INST RecordEvent TPARTADD(TileDataDst &dst, TileDataSrc0 &src0, TileDataSrc1 &src1, WaitEvents &... events);从源码实现看,该接口的行为链是:先通过detail::PtoWaitEvents(events...)等待传入的异步事件(WaitEvents 变参用于依赖同步),再经MAP_INSTR_IMPL(TPARTADD, dst, src0, src1)宏分发到各平台对应的TPARTADD_IMPL实现,最终返回一个RecordEvent记录本次发射。在 CPU 仿真模式下,MAP_INSTR_IMPL还会通过PTO_INSTR_SCOPE将指令名与操作数写入 trace,便于仿真排障(参见 include/pto/common/pto_instr.hpp)。
返回值RecordEvent表示指令发射记录,可继续作为后续指令的 WaitEvents 依赖传入,从而在用户代码层面构建指令间的流水依赖关系。
各平台约束与实现检查
通用约束(所有平台)
dst、src0、src1元素类型必须一致(各平台实现均以static_assert强制,例如 A2/A3 实现中的"Fix: TPARTADD src and dst data type is different!");- 结果计算范围由
dst有效区域决定; dst有效区域为零时指令空转返回;- 支持的部分有效模式见上文。
Atlas A2/A3 训练系列产品 / Atlas A2/A3 推理系列产品实现检查
- 支持的元素类型:
int32_t、int16_t、half、float; dst、src0、src1必须全部为行主序(isRowMajor),列主序(BLayout)不被支持,源码中以static_assert(TileDataDst::isRowMajor && ...)强制。
Ascend 950PR / Ascend 950DT(A5)实现检查
- 支持的元素类型显著更广:
uint8_t、int8_t、uint16_t、int16_t、uint32_t、int32_t、int64_t、uint64_t、half、float、bfloat16_t; - 从源码看,64 位整数类型走专门的
Int64Part<Int64Op::Add, ...>路径(见 A5 TPartAdd 实现),其余类型走统一的掩码寄存器路径。
使用示例
Auto(自动)模式
自动模式下 Tile 的放置与调度由编译器/运行时统一管理,用户只需声明 Tile 并调用接口:
#include <pto/pto-inst.hpp> using namespace pto; void example_auto() { using TileT = Tile<TileType::Vec, float, 16, 16>; TileT src0, src1, dst; TPARTADD(dst, src0, src1); }Manual(手动)模式
手动模式下需先用TASSIGN将各 Tile 显式绑定到目标地址(如统一缓冲区偏移),再发射 TPARTADD:
#include <pto/pto-inst.hpp> using namespace pto; void example_manual() { using TileT = Tile<TileType::Vec, float, 16, 16>; TileT src0, src1, dst; TASSIGN(src0, 0x1000); TASSIGN(src1, 0x2000); TASSIGN(dst, 0x3000); TPARTADD(dst, src0, src1); }对应汇编形式
自动模式(资源放置与调度交给编译器/运行时):
# Auto mode: compiler/runtime-managed placement and scheduling. %dst = pto.tpartadd %src0, %src1 : (!pto.tile<...>, !pto.tile<...>) -> !pto.tile<...>手动模式(先显式绑定资源,再发射指令,Tile 操作数可选用pto.tassign绑定地址):
# Manual mode: resources must be bound explicitly before issuing the instruction. # Optional for tile operands: # pto.tassign %arg0, @tile(0x1000) # pto.tassign %arg1, @tile(0x2000) %dst = pto.tpartadd %src0, %src1 : (!pto.tile<...>, !pto.tile<...>) -> !pto.tile<...>PTO 汇编完整形式(同步 + AS Level 2 DPS):
%dst = tpartadd %src0, %src1 : !pto.tile<...> -> !pto.tile<...> # AS Level 2 (DPS) pto.tpartadd ins(%src0, %src1 : !pto.tile_buf<...>, !pto.tile_buf<...>) outs(%dst : !pto.tile_buf<...>)源码级纵深:三个平台的实现原理
CPU 仿真实现(指令语义的黄金参照)
CPU 仿真实现位于 include/pto/cpu/TPartAdd.hpp 与 include/pto/cpu/TPartOp.hpp,其双层循环精确刻画了文档语义:
for (int i = 0; i < DstValidRow; i++) { for (int j = 0; j < DstValidCol; j++) { const size_t DstOffset = GetTileElementOffset<TileDataDst>(i, j); bool InSrc0 = i < Src0ValidRow && j < Src0ValidCol; bool InSrc1 = i < Src1ValidRow && j < Src1ValidCol; ... if (InSrc0 && InSrc1) { InstrOp::PartInstr(dst, src0, src1, DstOffset, Src0Offset, Src1Offset); // dst = src0 + src1 } else if (InSrc0 && !InSrc1) { dst[DstOffset] = src0[Src0Offset]; // 仅 src0 有效 → 拷贝 } else if (!InSrc0 && InSrc1) { dst[DstOffset] = src1[Src1Offset]; // 仅 src1 有效 → 拷贝 } else { dst[DstOffset] = 0; // 双方均无效 → 清零(实现定义行为) } } }其中PartAddOp::PartInstr仅执行dst[DstOffset] = src0[Src0Offset] + src1[Src1Offset]这一行加法。值得注意的实现细节:文档声明"双方都无效"等未列举模式行为由实现定义,而 CPU 仿真选择写入 0;TPartCheck则用static_assert校验数据类型集合,并在有效行或有效列为 0 时直接返回("TPARTMAX: Invalid data type." 为模板复用遗留的断言文案)。CPU 实现支持的数据类型集合与 A5 一致,覆盖 8/16/32/64 位整型、half、float、bfloat16。
Atlas A2/A3 实现(向量 vadd + repeat 展开)
A2/A3 实现位于 include/pto/npu/a2a3/TPartAdd.hpp。其核心思路是将逐元素加法映射到底层向量指令vadd,并按 block/repeat 结构展开:
struct PartAddOp { PTO_INTERNAL static void PartInstr(__ubuf__ T* dst, __ubuf__ T* src0, __ubuf__ T* src1, uint8_t repeats) { vadd(dst, src0, src1, repeats, 1, 1, 1, 8, 8, 8); } };elementsPerRepeat与blockSizeElem由元素类型换算得出:BLOCK_BYTE_SIZE / sizeof(T)与REPEAT_BYTE / sizeof(T)(见 A2/A3 实现),因此不同数据类型会自动适配不同的向量展开参数;- 模板参数
dstRowStride / src0RowStride / src1RowStride允许三个 Tile 拥有不同的行跨度,支持非连续布局下的部分加法; - 实现会先判断哪个源与 dst 有效区域相等,再决定将谁作为"全量"侧传入
TPartInstr,从而保证部分模式只出现在一个输入上,与文档约束一一对应。
Ascend 950PR/950DT(A5)实现(掩码寄存器 + zeroing 模式)
A5 实现位于 include/pto/npu/a5/TPartAdd.hpp,基于 RegTensor 与掩码寄存器(MaskReg)实现:
struct PartAddOp { PTO_INTERNAL static void BinInstr(RegTensor<T>& dst, RegTensor<T>& src0, RegTensor<T>& src1, MaskReg preg) { vadd(dst, src0, src1, preg, MODE_ZEROING); } };- 采用
MODE_ZEROING模式,配合 predication 掩码一次性完成"有效区相加、无效区清零"的合并语义,效率高于逐元素分支判断; - 对
int64_t/uint64_t两类 64 位类型,走Int64Part<Int64Op::Add, ...>专用路径,其余类型进入统一的TPARTOP_IMPL,这解释了 A5 支持类型清单明显长于 A2/A3 的原因; - 通用类型校验以
static_assert在编译期完成(见 A5 实现),非法类型直接编译失败而非运行期报错。
测试与验证
仓库为 TPARTADD 提供了跨平台、跨架构的完整测试用例,可作为指令行为的可执行规范:
- CPU 仿真:tests/cpu/st/testcase/tpartadd/main.cpp 使用 64x64 的 Tile、src1 有效区域为 32x32(
kValidRows1 = 32、kValidCols1 = 32)的典型部分有效场景,从input1.bin/input2.bin读取输入并经设备执行后写出output.bin对照;配套 gen_data.py 负责生成 golden 输入数据; - Atlas A2/A3:tests/npu/a2a3/src/st/testcase/tpartadd/main.cpp;
- Ascend 950(A5):tests/npu/a5/src/st/testcase/tpartadd/main.cpp 以模板参数
dstVR/dstVC/src0VR/src0VC/src1VR/src1VC显式枚举不同有效区域组合,并额外声明了LaunchTPartAddInplace用于覆盖**原地(in-place)**场景——即输出复用某个输入缓冲的情形; - 此外 kirin9030 / kirinDev0000 等架构目录下同样存在 tpartadd 测试集,说明该指令在多代 Ascend 架构上均有落地验证。
测试框架层面,NPU 用例经 ACL(aclrtMalloc/aclrtMemcpy/aclrtSynchronizeStream)完成主机与设备侧数据搬运与流同步,整个用例可经 tests/run_st.sh 脚本统一调度运行。
总结
TPARTADD 是 PTO 指令集中"部分有效区域二元运算"族(TPARTADD/TPARTMUL 等,参见 include/pto/common/pto_instr.hpp 中相邻的TPARTMUL声明)的代表指令。它通过"目标有效区域定义计算域 + 至少一个输入与目标区域对齐 + 另一输入不得越界"的约束模型,在保证硬件可实现性的同时,为算子开发者提供了处理动态形状与掩码叠加的原子能力。无论是理解 PTO-Virtual-ISA-Manual 中的指令体系,还是直接在算子内核中落地 partial 数据通路,掌握 TPARTADD 的语义边界、汇编三种形态与各平台实现差异都是必要的一步。
【免费下载链接】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),仅供参考