
人工智能编译器模型编译高性能计算深度学习CANN【免费下载链接】pyptoPyPTO发音: pai p-t-oParallel Tensor/Tile Operation编程范式。项目地址https://gitcode.com/cann/pypto点击查看免费下载pypto_pro.language.move是 CANN PyPTO 编程范式中最核心的片上数据搬运动作负责在 L1 Buffer、L0A/L0B Buffer、L0C Buffer、UBUnified Buffer等各级片上内存之间搬运 Tile 数据并可在搬运过程中随路完成格式转换、量化scale与激活ReLU等融合操作。本文以该接口的官方文档为主体结合 move 接口声明源码 与仓库中的测试用例完整讲解其函数原型、参数语义、数据类型与分形约束、offset 子块搬运语义以及三类量化参数传参方式与 phase 硬件握手机制帮助开发者直接写出可运行、可对齐精度的搬运代码。产品支持情况该接口在不同硬件平台上支持情况不同接口文档通过注释块明确标注见 move.mdAscend 950PR / Ascend 950DT支持。Atlas A3 训练系列产品 / Atlas A3 推理系列产品不支持。Atlas A2 训练系列产品 / Atlas A2 推理系列产品不支持。也就是说当前move接口属于 Ascend 950 系列特有能力。配套的 AccToVecMode、ReluPreMode 以及 phase 机制phase 使用约束均只在 950 系列上可用。功能说明move用于片上 Tile 与 Tile 之间的数据搬运tile↔tile不涉及 GMGlobal Memory。从 move 接口源码注释 可以看到其支持的存储空间路径与对应硬件流水源src目的dst硬件流水AccL0CVecUBfixFixpipeMatL1LeftL0Amte1MTE1MatL1RightL0Bmte1MTE1MatL1VecUBvVectorVecUBMatL1mte3MTE3其他组合—vVector同时move支持三类随路融合操作side operationsacc_to_vec_modeL0C→UB 双目标搬运模式、relu_pre_mode随路 ReLU 激活、scaleFixpipe 量化比例支持整块或按列粒度。这些融合能力使move不只是搬数据而是搬运即计算减少中间步骤与流水同步开销。函数原型pypto_pro.language.move( dst_tile: Tile, src_tile: Tile, offset: Optional[Offset] None, *, acc_to_vec_mode: Optional[AccToVecMode] None, relu_pre_mode: Optional[ReluPreMode] None, scale: Optional[Union[float, Scalar, Tile]] None, phase: Optional[STPhase] None, ) - None函数返回None。offset之后的所有参数均为关键字参数*分隔且均带默认值仅dst_tile、src_tile为必填。从 move 声明 可以确认scale支持float、Scalar、Tile三种类型这也决定了三种不同的量化粒度。参数说明参数输入/输出说明dst_tile输出目的操作数Tile类型支持的数据类型与分形详见约束说明。src_tile输入源操作数Tile类型支持的数据类型与分形详见约束说明。offset输入可选小 Tile 在大 Tile 中的相对位置格式为[offset_m, offset_n]单位为元素个数。见下方语义详解。acc_to_vec_mode输入可选L0C Buffer→UB 搬运时是否开启双目标搬运模式AccToVecMode 类型。relu_pre_mode输入可选L0C Buffer→UB 搬运时是否开启随路 ReLU 操作ReluPreMode 类型。scale输入可选量化参数。数据搬出 L0C Buffer 时由 Fixpipe 乘以该比例并转换到目的数据类型。详见量化参数的使用。phase输入可选phase 使用约束。offset 的子块搬运语义offset表示小 Tile 在大 Tile 中的相对位置方向取决于源、目的 Tile 的 shape 大小关系当源操作数的 shape ≥ 目的操作数的 shape 时表示从源操作数的第offset_m行、offset_n列开始读数据搬运量取目的操作数的valid_shape。典型场景是大 Tile 取子块。当源操作数的 shape 目的操作数的 shape 时表示从目的操作数的第offset_m行、offset_n列开始写数据搬运量取源操作数的valid_shape。典型场景是分块写入大 Tile。注意 move 源码注释 中 offset 被描述为从更宽源 Tile 中提取子块的[offset_m, offset_k]其本质含义与文档一致以元素为单位的二维偏移。另外在 Acc→Vec 场景下phase与offset不能同时使用move 源码注释 明确 phase 仅用于 Acc→Vec 路径且不可与 offset 组合。acc_to_vec_mode双目标搬运模式acc_to_vec_mode仅在 L0C Buffer→UB 场景生效枚举定义见 AccToVecMode枚举值说明SingleModeVec0单目标模式将整个矩阵写入 Vec0 的目标 UB。SingleModeVec1单目标模式将整个矩阵写入 Vec1 的目标 UB。DualModeSplitM双目标模式按 M 维度拆分M/2×N 个元素写入每个 UB。尾块时框架自动将 valid_M 向上对齐到 2 的倍数得到 aligned_MVec0sub_id0得到前 aligned_M/2 行Vec1sub_id1得到剩余 valid_M − aligned_M/2 行。注意valid_M 为 1 时仅切分给 Vec0。DualModeSplitN双目标模式按 N 维度拆分M×N/2 个元素写入每个 UB。尾块时框架自动将 valid_N 向上对齐到 32 的倍数得到 aligned_NVec0 得到前 aligned_N/2 列Vec1 得到剩余 valid_N − aligned_N/2 列。注意valid_N 不超过 16 时仅切分给 Vec0。双目标模式的核心价值在于将 L0C 中的矩阵结果一分为二写入两个 UB对应两个 vector 核提升后续 Vector 计算的并行度这是 Flash Attention 等长序列注意力实现的重要手法。DualModeSplitN 的卡死风险接口文档特别指出DualModeSplitN与phase同时使用时若 N 未 32 对齐可能出现卡死现象。建议在矩阵乘运算前对 L0B Buffer 的 N 设置 valid shape使其向上对齐到 32 的倍数。relu_pre_mode随路 ReLUrelu_pre_mode使能 L0C Buffer→UB 搬运过程中的随路 ReLU枚举定义见 ReluPreModePYPTO_DECLARE_ENUM(ReluPreMode, NormalRelu # 使能随路ReLU操作 )随路 ReLU 逐元素将负值置零、正值保持不变$$dst \max(src, 0) \begin{cases} src src 0 \ 0 src \leq 0 \end{cases}$$该操作由 Fixpipe 硬件在搬运数据离开 L0C 时完成无需先将数据搬到 UB 再调用 Vector 的激活指令省去一次数据往返和一次流水同步。scale量化参数scale使能量化功能并设置量化模式下的量化参数数据在搬出 L0C Buffer 时由 Fixpipe 乘以该比例并转换到目的数据类型。不支持与双目标搬运DualModeSplitM/DualModeSplitN同时使用。不同传入形式影响量化粒度详见量化参数的使用一节。phaseFixpipe 硬件握手phase参数pypto_pro.language.STPhase类型取Partial或Final用于 L0C→UB 搬运与矩阵乘之间建立 unit_flag 硬件握手详细机制见 phase 使用约束。在仓库的自动化测试中也有专门覆盖例如 test_move_phase.py 与 test_scale_phase_atomic_order.py。数据类型与分形约束接口文档给出了完整的源 → 目的约束矩阵覆盖分形ND/NZ/ZN/ZZ/NN/DN与数据类型两方面要求源 → 目的分形要求数据类型要求L1 Buffer → L0A Buffer源支持 ND、NZ、ZN、ZZ目的固定为 NZ源与目的必须相同支持 DT_INT8、DT_FP8E4M3FN、DT_FP8E5M2、DT_HF8、DT_FP16、DT_BF16、DT_FP32、DT_FP4E2M1、DT_FP4E1M2、DT_FP8E8M0L1 Buffer → L0B Buffer源支持 ND、NZ、ZN、ZZ目的固定为 ZN源与目的必须相同支持的数据类型同上UB → UB目的 shape ≤ 源 shape不校验分形源与目的必须相同非 ND → NZ 与 ND → NZ 支持集合略有差异详见 move.mdUB → UB目的 shape 源 shapeND → ND、NZ → NZ源与目的必须相同支持 DT_INT8、DT_INT32、DT_FP16、DT_BF16、DT_FP32、DT_FP8E4M3FN、DT_FP8E5M2、DT_FP8E8M0、DT_HF8、DT_FP4E2M1、DT_FP4E1M2UB → L1 Buffer目的 shape ≤ 源 shape源支持 ND、NZ目的不校验分形源与目的必须相同支持 DT_INT8、DT_FP8E4M3FN、DT_FP8E5M2、DT_HF8、DT_FP16、DT_BF16、DT_FP32、DT_FP4E2M1、DT_FP4E1M2、DT_FP8E8M0UB → L1 Buffer目的 shape 源 shape源支持 ND、NZ目的不校验分形源与目的必须相同在上一行基础上额外支持 DT_INT32L1 Buffer → BiasTable Buffer目的 layout 固定为 ND支持 DT_INT32 → DT_INT32、DT_FP32 → DT_FP32、DT_FP16 → DT_FP32、DT_BF16 → DT_FP32L1 Buffer → Fixpipe Buffer不校验分形目的必须为 DT_INT64 或 DT_UINT64源数据类型不做限制L1 Buffer → L0A_MX Buffer源与目的分形均为 ZZ源与目的必须相同仅支持 DT_FP8E8M0L1 Buffer → L0B_MX Buffer源与目的分形均为 NN源与目的必须相同仅支持 DT_FP8E8M0L0C Buffer → UB不配置 scaleNZ → ND、NZ → DN、NZ → NZ支持 DT_FP32 → DT_FP32/DT_FP16/DT_BF16以及 DT_INT32 → DT_INT32L0C Buffer → UB配置 scaleNZ → ND、NZ → DN、NZ → NZ支持 DT_FP32 → DT_INT8/DT_UINT8/DT_HF8/DT_FP16/DT_BF16/DT_FP8E4M3FN/DT_FP32以及 DT_INT32 → DT_INT8/DT_UINT8/DT_FP16/DT_BF16L0C Buffer → L1 Buffer仅支持目的 shape 源 shapeNZ → NZ支持 DT_FP32 → DT_FP32/DT_FP16/DT_BF16以及 DT_INT32 → DT_INT32从源码可以印证这些存储单元与硬件流水的对应关系codegen_common.h 中列出了各缓冲区类型的地址修饰符如__cc__对应 L0C、__fbuf__对应 Fixpipe Buffer、__ca__/__cb__对应 L0A/L0Bcodegen_common.h 中给出了各流水PIPE_MTE1/PIPE_MTE3/PIPE_V/PIPE_FIX 等的标识。L0C→UB 场景实际上由 FixpipePIPE_FIX承载这正是scale、relu_pre_mode能在搬运过程中随路完成的硬件基础。尾块与 L0A_MX/L0B_MX 的补充约束尾块场景需要搭配pypto_pro.language.set_validshape与 TileType 中的compact参数使用否则可能出现精度失败或卡死现象。L0A_MX / L0B_MX 地址约束L1 Buffer → L0A_MX/L0B_MX 要求目的 Tile 必须满足L0A_MX Buffer 地址 L0A Buffer 地址 4或L0B_MX Buffer 地址 L0B Buffer 地址 4否则 MX 矩阵乘时会读取错误的量化系数。调用示例示例一L1 → L0A / L0B 喂数给矩阵乘这是最典型的矩阵乘Matmul前处理流程先用pl.load把 GM 数据搬到 L1 Buffer再用pl.move把 L1 数据分别送入 L0A左矩阵NZ与 L0B右矩阵ZN最后pl.matmul在 L0C 中完成计算import os import pypto_pro.language as pl import torch pl.jit(auto_mutexTrue) def kernel( a: pl.Tensor[[64, 128], pl.DT_FP16], b: pl.Tensor[[128, 32], pl.DT_FP16], out: pl.Tensor[[64, 32], pl.DT_FP32], ): a_l1 pl.make_tile_group( typepl.TileType(shape[64, 128], dtypepl.DT_FP16, target_memorypl.MemorySpace.Mat, layoutpl.NZ), addrs0x00000, mutex_ids[0]) b_l1 pl.make_tile_group( typepl.TileType(shape[128, 32], dtypepl.DT_FP16, target_memorypl.MemorySpace.Mat, layoutpl.NZ), addrs0x10000, mutex_ids[1]) a_l0a pl.make_tile_group( typepl.TileType(shape[64, 128], dtypepl.DT_FP16, target_memorypl.MemorySpace.Left, layoutpl.NZ), addrs0x0, mutex_ids[2]) b_l0b pl.make_tile_group( typepl.TileType(shape[128, 32], dtypepl.DT_FP16, target_memorypl.MemorySpace.Right, layoutpl.ZN), addrs0x0, mutex_ids[3]) c_l0c pl.make_tile_group( typepl.TileType(shape[64, 32], dtypepl.DT_FP32, target_memorypl.MemorySpace.Acc, layoutpl.NZ), addrs0x0, mutex_ids[4]) with pl.section_cube(): cur_a a_l1.current() cur_b b_l1.current() al a_l0a.current() br b_l0b.current() ac c_l0c.current() pl.load(cur_a, a, [0, 0]) pl.load(cur_b, b, [0, 0]) pl.move(al, cur_a) # L1 - L0A pl.move(br, cur_b) # L1 - L0B pl.matmul(ac, al, br) pl.store(out, ac, [0, 0]) if __name__ __main__: device fnpu:{int(os.environ.get(TILE_FWK_DEVICE_ID, 0))} torch.npu.set_device(device) torch.manual_seed(42) a torch.randn([64, 128], devicedevice, dtypetorch.float16) b torch.randn([128, 32], devicedevice, dtypetorch.float16) out torch.zeros([64, 32], devicedevice, dtypetorch.float32) kernel(a, b, out) torch.npu.synchronize() ref torch.matmul(a.float(), b.float()) torch.testing.assert_close(out, ref, rtol2e-2, atol2e-2) print(fmax diff {(out - ref).abs().max().item()})要点说明L1 → L0A 时目的固定 NZL1 → L0B 时目的固定 ZN与本例 TileType 中的 layout 一致。矩阵乘对右矩阵采用 ZN 布局示例中b在 L1 中是 NZ由 load 写入move 到 L0B 时目的声明为 ZN。使用make_tile_groupcurrent()在 cube section 内取当前 Tile配合mutex_ids管理缓冲区互斥。示例二UB 数据转置写入 L1 Buffer当左矩阵或右矩阵由 Vector 计算在 UB 中产生时可先通过pl.move将 ND 转换为 NZ再将结果写入 L1 Buffer不转置时L1 Buffer Tile 使用 NZshape 与 UB 中的 NZ Tile 相同。转置时L1 Buffer Tile 使用 ZNshape 的两个维度与 UB 中的 NZ Tile 互换。源 Tile 的 NZ [R, C] 与目的 Tile 的 ZN [C, R] 表示相同的物理分形因此 ZN 写入本质是零拷贝转置。矩阵是否转置UB 中的 NZ TileL1 Buffer Tile左矩阵 A[M, K]否shape[M, K]shape[M, K]NZ左矩阵 A[M, K]是shape[K, M]shape[M, K]ZN右矩阵 B[K, N]否shape[K, N]shape[K, N]NZ右矩阵 B[K, N]是shape[N, K]shape[K, N]ZN分块写入 L1 Buffer 时可通过offset指定写入位置源 Tile 的有效区域必须完整落入目的 Tile 范围内。以下示例中vector_result表示 Vector 计算产生的 [K, M] 数据第一次 move 将其转换为 NZ第二次 move 将其转置写入 L1 Buffer得到供左矩阵使用的 [M, K]、ZN 数据import pypto_pro.language as pl M, K 64, 128 # 前序Vector计算的输出UB中的ND [K, M] vector_result pl.make_tile( pl.TileType( shape[K, M], dtypepl.DT_FP16, target_memorypl.MemorySpace.Vec, layoutpl.ND, ), addr0x0000, ) # ND转换为NZ时使用的UB Tile vector_nz pl.make_tile( pl.TileType( shape[K, M], dtypepl.DT_FP16, target_memorypl.MemorySpace.Vec, layoutpl.NZ, ), addr0x4000, ) # 转置后的数据写入L1 Buffer逻辑shape为[M, K] lhs_l1 pl.make_tile( pl.TileType( shape[M, K], dtypepl.DT_FP16, target_memorypl.MemorySpace.Mat, layoutpl.ZN, ), addr0x20000, ) with pl.section_vector(): pl.move(vector_nz, vector_result) # ND [K, M] → NZ [K, M] pl.move(lhs_l1, vector_nz) # NZ [K, M] → ZN [M, K]仓库中 test_move_nd_to_nz_vec.py 对该场景有直接覆盖测试中dst与flat_view共享同一地址但属于不同 IR 值auto_mutex无法识别该依赖因此测试显式插入pl.system.sync_src(set_pipepl.PipeType.V, wait_pipepl.PipeType.MTE3, event_id3)来保证 Vector 与 MTE3 之间的执行顺序——这提示我们在真实工程中当搬运目的地址与源存在别名关系时需要手动管理流水同步。量化参数的使用scale支持三种传参形式对应三种量化粒度方式一scale 为编译期常量float# acc: L0C Buffer中的Tile, DT_FP32 # vec_tile: UB Tile, DT_INT8 pl.matmul(acc, q_left, k_right) pl.move(vec_tile, acc, scale0.5) # L0C Buffer - UB随路按 0.5 量化为 INT8直接传固定值适用于整块 Tile 使用同一比例。从 move 源码注释 可知该路径走 deqScalarper-tensor 量化。方式二scale 为运行时标量Scalarimport struct pl.jit() def kernel(..., scale_bits: pl.DT_INT32): # ... pl.move(vec_tile, acc, scalescale_bits) scale_bits struct.unpack(!I, struct.pack(!f, 0.5))[0] kernel(..., scale_bitsscale_bits)量化比例在运行时确定需按数据类型传值DT_FP32直接传原始比例值如0.5。源码注释说明运行时 FP32 标量会被自动按 IEEE-754 位模式重解释codegen bitcast因此传原始 float 数值即可。DT_INT32、DT_INT64传预编码的 float32 位模式转成的整数即struct.pack(!f, 0.5)拆包后的整数。其他运行时标量 dtypeFP16/BF16/无符号/窄整数会在解析期被拒绝。方式三scale 为 Tile 类型逐列量化每列使用独立比例适用于 per-channel 量化场景前提条件较多目标存储区域必须为Fixpipe Bufferpl.MemorySpace.Scaling。shape 为[1, N]列量化N 必须是 16 的倍数且 N ≤ 512[N, 1]的行量化不支持move 源码注释 明确说明硬件只支持每列缩放。dtype 为 DT_INT64。目的操作数的 Tile 数据类型为DT_INT8时Fixpipe Buffer 中每个 DT_INT64 元素的bit46 需置 1用于选择有符号量化未置位时 L0C Buffer 中的负值会被按无符号解读。用户需先把比例数据从 GM 搬到 L1 Buffer再搬到 Fixpipe Buffer并完成 MTE1→FIX 同步。框架不会自动分配缓冲或插入同步数据流完全由用户负责move 源码注释。# fp_mat: L1 Buffer中的Tile, shape [1, 64], DT_INT64 # fp_tile: Fixpipe Buffer中的Tile, shape [1, 64], DT_INT64 # acc: L0C Buffer中的Tile, DT_INT32 # vec_tile: UB Tile, DT_FP16 pl.jit() def kernel(..., fp_params: pl.Tensor[[1, 64], pl.DT_INT64]): # ... pl.load(fp_mat, fp_params, [0, 0]) # GM - L1 pl.move(fp_tile, fp_mat) # L1 Buffer - Fixpipe Buffer pl.system.sync_src(set_pipepl.PipeType.MTE1, wait_pipepl.PipeType.FIX, event_id1) pl.system.sync_dst(set_pipepl.PipeType.MTE1, wait_pipepl.PipeType.FIX, event_id1) pl.matmul(acc, q_left, k_right) pl.move(vec_tile, acc, scalefp_tile) # L0C Buffer - UB按列独立比例反量化为DT_FP16Host 侧如何准备量化比例张量分两种情况# 情况一、Kernel需要DT_INT64若使用float32的比例张量需要先进行转换 import torch_npu scale_value 2.0 scale_fp32 torch.ones(1, 64, dtypetorch.float32, devicedevice) * scale_value fp_params torch_npu.npu_trans_quant_param(scale_fp32) # 情况二、目的Tile数据类型为DT_INT8时需要将Fixpipe Buffer中的Tile每个INT64元素的bit46需置1 def _make_scale_tensor(device: str, scale_values: list) - torch.Tensor: scale_bits_list [] for scale_value in scale_values: scale_bits struct.unpack(!I, struct.pack(!f, scale_value))[0] scale_bits | 1 46 # signed INT8 flag scale_bits_list.append(scale_bits) return torch.tensor(scale_bits_list, dtypetorch.int64, devicedevice).reshape(1, 64) scale_values [2.0] * 64 fp_params _make_scale_tensor(device, scale_values)仓库对量化参数有大量针对性测试可作为实现参考test_scale_param.py、test_scale_per_channel_advanced.py、test_scale_move_dtype.py、test_scale_dtype_validation.py、test_scale_value_range.py 以及 test_scale_relu_fusion.pyscale 与 ReLU 融合。phase 与双目标搬运的配合phaseSTPhase与acc_to_vec_mode是 L0C→UB 场景下最常用的两个可选参数二者配合可实现双目标搬运 硬件握手的高效流水。需要注意DualModeSplitN 与 phase 同时使用时若 N 未 32 对齐可能出现卡死现象详见参数说明建议对 L0B 的 N 预先对齐到 32 的倍数。尾块 双目标框架自动对齐 valid_M/valid_NUB 侧用户需自行计算两个 Vec 的 valid shape。完整示例见 AccToVecMode 文档DualModeSplitM 下aligned_M34时v0(valid_M1)//2*2//2、v1valid_M-v0DualModeSplitN 下aligned_N64时v0(valid_N31)//32*32//2、v1valid_N-v0。仓库测试 test_dual_mode_tail.py 专门验证尾块场景下的双目标搬运切分行为。Flash Attention 的 QK matmul 是 phase move 配合的经典模式matmul 结果需要 vector 核做 softmax 后处理store只能直接写 GM、无法在 UB 上继续计算因此必须通过move配合DualModeSplitN与STPhase.Final将累加器数据搬到 UB。完整正确用法与三个错误案例matmul 无 Final 导致卡死、store 未配 phase 导致精度问题、循环内 store(Final) 后 matmul 卡死参见 phase 使用约束。总结pypto_pro.language.move是 Ascend 950 系列上连接 Cube矩阵乘与 VectorUB 计算两套流水、贯通 L1/L0A/L0B/L0C/UB 多级存储的核心动作。使用时需要重点关注四类信息路径合法性根据约束说明确认源/目的存储空间组合及其数据类型、分形是否受支持尤其注意 L0C→UB 的 Fixpipe 路径与 L1→L0A/L0B 的 MTE1 路径差异。offset 语义根据源/目的 shape 大小关系确定是从源读子块还是向目的写子块并确保有效区域完整落入目的范围。量化粒度scale的 float/Scalar/Tile 三种形式分别对应整块、运行时标量、逐列三种量化粒度逐列量化时需自行完成 GM→L1→Fixpipe 的数据流与 MTE1→FIX 同步INT8 场景还需置位 bit46。同步与握手尾块场景需配合set_validshape与compact需要省去软件同步时可配置phase走 unit_flag 硬件握手但必须保证 matmul 与 move 的 phase 配对使用并以 Final 收尾。赞分享人工智能编译器模型编译高性能计算深度学习CANN【免费下载链接】pyptoPyPTO发音: pai p-t-oParallel Tensor/Tile Operation编程范式。项目地址https://gitcode.com/cann/pypto点击查看免费下载相关推荐PyPTO ReluPreMode 枚举详解L0C Buffer 数据搬运中的随路 ReLU 融合编程PyPTO ReluPreMode 枚举详解L0C Buffer 数据搬运中的随路 ReLU 融合编程 导读 本文围绕 PyPTOParallel Tens人工智能编译器模型编译高性能计算深度学习CANNCANN SHMEM SIMT RMA over UB Staging以 simt_rma_ub2gm 为例解析 UB↔GM 数据搬运接口CANN SHMEM SIMT RMA over UB Staging以 simt_rma_ub2gm 为例解析 UB↔GM 数据搬运接口 本指南以 exam通信高性能计算人工智能CANNAscendCANN pyasc 数据搬运接口 asc.data_copy_pad 详解非对齐搬运与填充实战CANN pyasc 数据搬运接口 asc.data_copy_pad 详解非对齐搬运与填充实战 导读 asc.data_copy_pad 是 CANN py编译器编程语言人工智能CANN创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考