PTO ISA 标量余数指令 TFMODS 详解:从 C++ Intrinsic 到 NPU/CPU 实现

发布时间:2026/9/19 13:13:39
PTO ISA 标量余数指令 TFMODS 详解:从 C++ Intrinsic 到 NPU/CPU 实现 PTO ISA 标量余数指令 TFMODS 详解从 C Intrinsic 到 NPU/CPU 实现【免费下载链接】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导读TFMODSTile Float Mod Scalar是 CANN PTO 虚拟指令集中用于逐元素取余数的标量二元指令其语义为dst[i][j] fmod(src[i][j], scalar)。它是 TREMSTile 间余数的标量形式广泛用于归一化、周期性计算、索引取模等算子场景。本文以 docs/isa/TFMODS.md 为核心结合仓库内 NPUA2A3/A5与 CPU 模拟器实现、单元测试用例完整讲解该指令的数学语义、汇编语法、C Intrinsic 接口、各平台约束与底层实现原理读完即可在 PTO 算子中正确使用 TFMODS 并理解其精度与性能权衡。指令总览TFMODS 属于 PTO ISA 中逐元素标量运算指令族与 TADDS、TMULS、TDIVS、TREMS 等同属一类操作对象是向量 TileVector Tile将源 Tile 的每一个元素与一个标量做fmod运算结果写入目标 Tile。指令示意图如下docs/figures/isa/TFMODS.svg数学语义TFMODS 对有效区域valid region内的每一个元素(i, j)执行$$\mathrm{dst}{i,j} \mathrm{fmod}(\mathrm{src}{i,j}, \mathrm{scalar})$$fmod为 C 标准库语义的浮点余数结果符号与被除数src一致而非取模运算mod。在仓库 CPU 模拟器实现中include/pto/cpu/ElementOp.h 的ElementOpCalDType, ElementOp::OP_FMODS给出了精确的参考语义template typename DType struct ElementOpCalDType, ElementOp::OP_FMODS { static void apply(DType dst, DType src, DType scalar, size_t) { if constexpr (std::is_integral_vDType) { dst src % scalar; // 整型走取余 } else { dst static_castDType(std::fmod(static_castdouble(src), static_castdouble(scalar))); } } };可以看到整型元素走src % scalar浮点元素按std::fmod语义计算NPU 上的实现也遵循同一数学公式fmod(a, b) a - trunc(a/b) * b见 include/pto/npu/a2a3/TFmodS.hpp 注释。汇编语法与三种表达层级PTO ISA 为 TFMODS 提供了从高层 SSA 到低层 DPSDestination-Pass-Style的三级表达形式同步形式Synchronous form统一为%dst tfmods %src, %scalar : !pto.tile..., f32AS Level 1SSA 形式MLIR 风格的 SSA 指令使用 tile 类型约束%dst pto.tfmods %src, %scalar : !pto.tile..., f32AS Level 2DPS 形式显式列出输入输出缓冲的 DPS 形式用于贴近硬件资源描述pto.tfmods ins(%src, %scalar : !pto.tile_buf..., f32) outs(%dst : !pto.tile_buf...)C Intrinsic 接口TFMODS 的 C 内建接口声明于公共头文件 include/pto/common/pto_instr.hpp对外通过pto/pto-inst.hpp统一暴露template auto PrecisionType FmodSAlgorithm::DEFAULT, typename TileDataDst, typename TileDataSrc, typename... WaitEvents PTO_INST RecordEvent TFMODS(TileDataDst dst, TileDataSrc src, typename TileDataSrc::DType scalar, WaitEvents ...events);接口要点模板参数PrecisionType控制算法精度选择默认FmodSAlgorithm::DEFAULTTileDataDst/TileDataSrc分别为目标、源 Tile 类型。函数参数dst输出 Tile、src输入 Tile、scalar标量类型与 Tile 元素类型一致、events可选的等待事件通过detail::PtoWaitEvents(events...)实现异步同步。返回值RecordEvent可配合 PTO 的异步事件机制使用。从源码可见调用链为TFMODS(...)→TFMODS_IMPLPrecisionType(dst, src, scalar)其中TFMODS_IMPL按目标平台CPU / A2A3 / A5 等分别实现。PrecisionType 精度选项FmodSAlgorithm枚举定义于 include/pto/common/type.hppenum class FmodSAlgorithm : uint8_t { DEFAULT, HIGH_PRECISION };选项说明适用平台FmodSAlgorithm::DEFAULT普通算法速度快但精度较低A2A3 / A5FmodSAlgorithm::HIGH_PRECISION高精度算法速度较慢仅支持float类型仅 A5A3 上忽略该选项平台约束与实现检查TFMODS 在不同平台上有不同的静态编译期与动态运行期检查约束文档与源码一一对应。实现检查A2A3对应 include/pto/npu/a2a3/TFmodS.hpp 中TFMODS_IMPL的static_assert与PTO_ASSERTdst和src必须使用相同的元素类型支持的元素类型为float和float32_t源码中static_assert明确拒绝其他类型dst和src必须是向量 TileTileType::Vecdst和src必须是行主序Row-Major运行时dst.GetValidRow() src.GetValidRow() 0且dst.GetValidCol() src.GetValidCol() 0。A2A3 的底层实现FmodSOp::FmodSF32Instrinclude/pto/npu/a2a3/TFmodS.hpp用向量指令流水线复现fmodvector_dup将标量广播 →vdiv求src/scalar→vconv_f322f32z做向零取整 →vmuls回乘 →vsub相减中间以pipe_barrier(PIPE_V)保证向量流水线同步。实现检查A5对应 include/pto/npu/a5/TFModS.hppdst和src必须使用相同的元素类型支持的元素类型为目标实现支持的 2 字节或 4 字节类型包括half和floatdst和src必须是向量 Tile两个 Tile 的静态有效边界都必须满足ValidRow Rows且ValidCol Cols编译期static_assert运行时dst.GetValidRow() src.GetValidRow()且dst.GetValidCol() src.GetValidCol()。A5 实现FModSOpPrecisionType, T::BinSInstrinclude/pto/npu/a5/TFModS.hpp支持三类路径高精度路径PrecisionType HIGH_PRECISION T float时调用TFmodRemHP专用高精度实现float 普通路径vdiv → vtrc(ROUND_Z 向零取整) → vmuls → vsubhalf 路径将 half 拆分为奇偶PART_EVEN/PART_ODD两个 float 通道分别计算后vcvt转回并vor合并以保证半精度下的计算精度。除零行为除零行为由目标平台定义CPU 模拟器在调试构建debug build中会断言TFMODS_IMPL在 include/pto/cpu/TBinSOps.hpp 中先检查scalar ! 0否则触发PTO_ASSERT(false, illegal src is zero)因此开发时建议在调用前显式保证标量非零或在业务层面处理零标量语义。有效区域Valid Region该操作使用dst.GetValidRow()/dst.GetValidCol()作为迭代域即实际计算范围由目标 Tile 的有效边界决定而非物理 Tile 大小从 include/pto/cpu/TBinSOps.hpp 的UnaryTileScalarOpImpl可见CPU 实现按rows dst.GetValidRow()、cols dst.GetValidCol()遍历并通过parallel_for_rows做行级并行向量化宏PTO_CPU_VECTORIZE_LOOP开启内层循环向量化。高精度算法HIGH_PRECISION仅在 A5 上有效PrecisionType选项在 A3 上将被忽略仅支持float类型。使用示例C 示例以下示例定义 16×16 的float向量 Tile将每个元素对3.0f取余数#include pto/pto-inst.hpp using namespace pto; void example() { using TileT TileTileType::Vec, float, 16, 16; TileT x, out; TFMODS(out, x, 3.0f); }端到端 Kernel 示例参考仓库测试用例仓库测试用例 tests/cpu/st/testcase/tfmods/tfmods_kernel.cpp 给出了 TFMODS 在完整 Kernel 中的数据流TASSIGN分配 Tile 地址 →TLOAD从全局内存加载 →TFMODS计算 →TSTORE写回template typename T, int kDRows_, int kDCols_, int kTRows_, int kTCols_ AICORE void runTFmods(__gm__ T __out__* out, __gm__ T __in__* src, __gm__ T __in__* scalar) { using TileDataDst TileTileType::Vec, T, kDRows_, kDCols_, BLayout::RowMajor, -1, -1; using TileDataSrc TileTileType::Vec, T, kTRows_, kTCols_, BLayout::RowMajor, -1, -1; TileDataSrc srcTile(kTRows_, kTCols_); TileDataDst dstTile(kTRows_, kTCols_); TASSIGN(srcTile, 0); TASSIGN(dstTile, kTRows_ * kTCols_ * sizeof(typename TileDataSrc::DType)); TLOAD(srcTile, srcGlobal); TFMODS(dstTile, srcTile, scalar[0]); TSTORE(dstGlobal, dstTile); }该用例还实例化了float、int32_t、int16_t、halfaclFloat16以及可选bfloat16_t的多种形状组合可同时作为类型覆盖与边界形状的参考。对应 NPU 侧测试见 tests/npu/a5/src/st/testcase/tfmods/tfmods_kernel.cpp 与 tests/npu/a2a3/src/st/testcase/tfmods/tfmods_kernel.cpp各测试目录下均配有main.cpp与gen_data.py用于生成对比数据。汇编示例ASM自动模式Auto Mode自动模式下由编译器/运行时负责 Tile 的资源放置与指令调度用户只需写 SSA 指令# 自动模式由编译器/运行时负责资源放置与调度。 %dst pto.tfmods %src, %scalar : !pto.tile..., f32手动模式Manual Mode手动模式下必须先显式绑定 Tile 资源通过pto.tassign将虚拟 Tile 绑定到物理地址再发射指令。pto.tassign对 tile 操作数是可选的示例中tile(0x1000)、tile(0x2000)为地址示意# 手动模式先显式绑定资源再发射指令。 # 可选当该指令包含 tile 操作数时 # pto.tassign %arg0, tile(0x1000) # pto.tassign %arg1, tile(0x2000) %dst pto.tfmods %src, %scalar : !pto.tile..., f32PTO 汇编形式AS Level 1 / Level 2 对照%dst tfmods %src, %scalar : !pto.tile..., f32 # AS Level 2 (DPS) pto.tfmods ins(%src, %scalar : !pto.tile_buf..., f32) outs(%dst : !pto.tile_buf...)与其他指令的关联TFMODS 与以下标量指令共享同一套Tile 对 标量的运算框架include/pto/cpu/TBinSOps.hpp 中通过CategoryBinSOps统一声明约束类别TREMSTile 与 Tile 的逐元素余数src % src需要临时 Tile 参数见 docs/isa/TREMS.mdTDIVS逐元素除以标量实现上同样检查零标量PTO_ASSERT(false, TDIVS: illegal scalar is zero)TADDS / TMULS / TPOWS / TMAXS / TMINS等同族的逐元素标量运算。其中OP_FMODS在约束上属于CONSTRAINT_VEC要求向量 Tile不强制行主序而OP_ADDS、OP_MULS等属于CONSTRAINT_VEC_ROWMAJOR同时要求向量与行主序这解释了为何 A2A3 上 TFMODS 会额外要求行主序而 CPU 通用实现不强制。需要取整除法余数场景时可依据 docs/isa/TREMS.md、docs/isa/TDIVS.md 选择最合适的指令。总结与使用建议TFMODS 是 PTO ISA 中实现Tile 元素对 标量 取余数的标准指令使用时注意以下关键点类型匹配dst与src元素类型必须一致A2A3 仅支持float/float32_tA5 支持 2/4 字节类型half、floatTile 属性两端必须是向量 TileA2A3 额外要求行主序有效区域迭代域以dst.GetValidRow()/GetValidCol()为准两个 Tile 的有效行列数必须一致除零防护标量不得为 0CPU 模拟器调试构建会断言NPU 行为由目标平台定义精度权衡默认FmodSAlgorithm::DEFAULT速度快、精度较低需要高精度且目标为 A5、类型为float时可选用FmodSAlgorithm::HIGH_PRECISION。如需查看 TFMODS 的完整规格可直接阅读 docs/isa/TFMODS.md 及中文版 docs/isa/TFMODS_zh.md并通过 docs/menu/arithmetic_zh.md 或 docs/isa/README.md 索引同族算术指令。【免费下载链接】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),仅供参考