ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PyPTO-Gym 算子 Tile 合法性校验:从形状契约、动态尾块到 auto_mutex 硬上限的完整实践指南

PyPTO-Gym 算子 Tile 合法性校验:从形状契约、动态尾块到 auto_mutex 硬上限的完整实践指南 PyPTO-Gym 算子 Tile 合法性校验从形状契约、动态尾块到 auto_mutex 硬上限的完整实践指南【免费下载链接】pypto-gymPyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库项目地址: https://gitcode.com/cann/pypto-gym本篇技术指南以 PyPTO-Gym 仓库中 pypto-pro-op-kb 约束页 为骨架系统讲解在 PyPTO-Propltile DSL下做算子 Tiling 设计时必须遵守的合法性契约每个 tile 都要从所选 API 文档化的 shape、dtype、memory-space、layout、对齐与 valid-shape 契约推导而非从其他平台或 SDK 版本搬运限制。读完本文你将掌握一套可落地的 tile 合法性检查清单、两个无法靠目标检测推导出的 codegen 结构天花板Tile 至多二维、mutex_ids落在[0, 31]、动态尾块dynamic tail的合法物理 tile 运行时有效窗口valid window处理方式以及 Cube 侧按 dtype 获取收缩几何与 L0A/L0B/L0C 布局要求的方法。1. 核心规则一切 tile 都从 API 契约推导PyPTO-Pro 的 tile 合法性第一原则可以浓缩为一句强制规则Derive every tile from the selected APIs documented shape, dtype, memory-space, layout, alignment, and valid-shape contract. Do not carry limits from another platform or SDK version.即每一个 tile 都必须从所选 API 文档化的 shape、dtype、内存空间、布局、对齐和 valid-shape 契约推导而来不得从其他平台或 SDK 版本搬运限制。这条规则在 arch-a5.md 中被进一步强化为“fail closed”原则如果架构或匹配的平台源码无法确认不得凭记忆套用 A5 的数值。平台配置值是 roofline 输入不是实测的 kernel 性能任何 UB 容量、L0A/L0B/L0C 容量都必须以检测到的目标版本对应平台文件为准。1.1 为什么要“按版本、按 SKU”推导PyPTO-Pro 的 Tiling 合法性高度依赖安装版本与目标 SKU。以 UB 容量之争为例arch-a5.md 记录了一次真实的争议A5 的 UB 容量曾有两个说法——教程里的248 KB与兄弟 DSL 设备表中的256 KB / 216 KB-with-SIMT。最终裁决者是平台文件framework/src/platform/parser/simulation_platform/platform_config/950PR_957x.ini中ub_size253952即 248 KB 整950DT_957x、950PR_958x、950DT_958x同理同一文件还承载l0_a_size、l0_b_size、l0_c_size、l1_size、bt_size、cube_core_cnt、vector_core_cnt、ubblock_size32 字节 UB 量子等全部容量键。结论是搬运数值到文档里是危险的正确做法是“carry the key, not the value”——在规划期解析平台文件中的键而不是把248写进任何页面。这正是 tiling.md 第 4 步“与匹配的平台 source/configuration 对比”的底层含义。1.2 平台与版本记录arch-a5.md 给出标准的平台发现流程用 get_npu_arch.py 或构建配置检测目标记录设备型号、SKU、CANN 版本、PyPTO 版本与解析出的pypto_pro.__file__选择与安装版本、SKU 精确匹配的平台文件只提取当前设计所需常量并在EXPLORE_REPORT.md/DESIGN.md中引用文件与键名。主源路径在记录了版本的安装树下解析framework/src/platform/parser/simulation_platform/platform_config/950DT_957x.ini及匹配的950DT_958x.ini、950PR_957x.ini、950PR_958x.iniframework/src/platform/parser/platforminfo.inipython/pypto_pro/runtime/compile_config.pypython/pypto_pro/runtime/platform.py2. 七步检查清单从版本检测到多尾块回归tiling.md 给出了 7 条可直接执行的检查步骤是设计评审与自测的骨架检测目标平台记录已安装的 PyPTO/CANN 版本。这是后续一切推导的前提见 1.2。对每个存活livetile 记录 shape、dtype 字节数、内存空间、layout、地址、slot 数量与生命周期。生命周期分三类memory-layout.mdRotating多 slot 经.next()跨迭代选择、Resident单 slot 跨内层循环存活、Scratch临时生命周期结束即可复用地址。按内存空间分别累加同时分配simultaneous allocation的字节。Vec、Mat、Left、Right、Acc 是五个独立地址空间必须分开求和与对应平台源/配置对比。将每个和与匹配的平台 source/configuration 对比。数值以安装版本平台.ini为准不引用任何 KB 页面里的数字。独立确认pl.load、pl.load_tile、store 以及向量函数 API 的 offset 单位。仓库记录了一个经典陷阱load是目标优先load(dst_tile, src_tensor, offsets)store是张量优先store(dst_tensor, src_tile, offsets)这种不对称会可靠地制造编译期完全正常、运行期错误的参数顺序 bug见 pypto-pro-framework-findings.md 第 5 条。load_tile使用 tile-index offset 而非元素 offset。动态尾块保持一个合法的物理 tile并在每个读/写它的操作上设置运行时 valid window。这是第 4 节的完整主题。对对齐aligned、单尾块single-tail、多尾块multi-tail、多 tilemulti-tile四类用例重跑正确性。tail-validshape.md 的清单进一步要求测试“小于一个 tile”“恰好一个 tile”“多个整 tile”“多个 tile 带余数”四档形状。2.1 与内存布局表结合把地址空间写成表格memory-layout.md 建议为每个内存空间维护一张地址审查表tile | layout | start_byte | size_bytes_per_slot | slots | lifetime | end_byte_exclusive字节区间一律记作半开区间[start_byte, end_byte_exclusive)。一个 slot 的end_byte_exclusive start_byte size_bytes_per_slot连续聚合的 slot 用start_byte slots * size_bytes_per_slot。设计在以下任一情况直接否决存活区间重叠任一 exclusive end 超过该空间容量地址违反对齐布局转换是“推断”出来的而非文档化的。mutex_ids标识同步槽不能证明两个字节区间可以安全别名。仓库在 pypto-pro-framework-findings.md 第 16 条记录了一次真实事故同一个地址的两个make_tile_group视图使用不同mutex_idsauto_mutex按mutex_id追踪依赖而对别名一无所知DMA 写一个视图、向量操作读另一个视图时 MTE2→V 屏障永远不会被发出约八分之一输出元素被破坏、出现 1e7 相对量级的 scale 误差。规则是永远不要 DMA 进入一个经由不同 tile-group 视图读取的地址——让传输落在自己拥有独立mutex_id的 tile 上再用向量操作复制过去。3. 两个检查清单推导不出的结构天花板tiling.md 特别指出下面两条是codegen 的属性不是平台文件的属性任何数量的目标检测都不会暴露它们——只能靠“撞上”才能发现。3.1 天花板一Tile 至多二维一个 Tile 最多只有两个维度对应TileType.md。秩为 3 的视图必须在宿主编排host side的 tiling 数学里被展平为[outer, inner]。这与 pypto-pro-framework-findings.md 中的记录一致codegen 最多支持二维 Tile。仓库内所有已验证样例都是二维结构例如 matmul_float_mmad_impl.py 中的[M, K]、[K, N]、[M, N]全是二维 tile 组。当 M/K/N 之外还需要第三维时必须由宿主编排层把维度折叠进[outer, inner]两维。3.2 天花板二mutex_ids必须落在[0, 31]mutex_ids必须位于[0, 31]。依赖auto_mutex时要给独立存活 buffer 或旋转 slot 分配互不相同的标识跨存活组复用同一标识必须由同步设计加上一个保留的、通过的目标结果来证明合理。因此32 是同时互异的auto_mutex标识数量的硬上限而不是物理 buffer 或 tile 数量的上限。这条约束要在第 3 步的字节预算byte budget旁一并规划一个内存上放得下的设计仍可能因为标识数放不下而需要重构或显式同步——两个上限由不同的设计分别触达。框架发现页在“No allocator, and overlap is unchecked”一节补充了配套规则每个旋转 tile 族要有自己的 slot 计数器——共享计数器若以错误步长前进会把两个逻辑 slot 别名到同一个物理 buffer而事件机制仍发放两个 credit产生看起来像精度 bug 的损坏。tiling.md 明确指出slot identity 正是auto_mutex排序的对象步长错误的 slot 计数器会把两个逻辑 buffer 别名到一个物理 buffer 上。实测环境以上两条天花板在Ascend950PR / CANN 9.2.0上测得详见 pypto-pro-framework-findings.md “A5 probe session findings”一节。该页还记录了关联风险slot identity 是auto_mutex排序的依据步长错误的 slot 计数器会让两个逻辑 buffer 别名到同一物理 buffer而事件机制仍发放两个 credit。3.3 样例中的 mutex 规划在 bf16_matmul_operand_reuse_impl.py 中可以看到规范的 mutex 分配a_l1 pl.make_tile_group(type..., addrs0x00000, mutex_ids[0, 1, 10, 11]) a_l0a pl.make_tile_group(type..., addrs0x0, mutex_ids[2, 5]) b_l1 pl.make_tile_group(type..., addrs0x20000, mutex_ids[3, 4, 12, 13]) b_l0b pl.make_tile_group(type..., addrs0x0, mutex_ids[6, 7]) acc pl.make_tile_group(type..., addrs0x0, mutex_ids[8, 9, 14, 15])每个 tile 族拥有互异的标识区间为旋转 slot 预留编号全部落在[0, 31]内。4. 动态尾块合法物理 tile 运行时 valid window第 2 节清单的第 6 步值得单独成节因为它直接决定 tail 形状的算子正确性。规则见 tail-validshape.md保持一个编译期合法的物理 tile并为每个部分工作项设置运行时 valid window把 valid window 应用到每一个观察尾块的输入、输出、归约结果和工作区。检查清单用向上取整除法ceiling division计算工作项数量从真实张量维度计算每个剩余 extent将 extent钳制到物理 tile 尺寸按安装 API 要求在 load、compute、reduction、store 之前设置 valid shape当仅靠 valid shape 无法定义归约恒等元时为归约中和无效 lane测试小于一个 tile、恰好一个 tile、多个整 tile、多个 tile 带余数。对于 streaming softmax最后一个 score chunk 必须同时从 max 与 sum 中排除无效 lane参见 online-softmax-tail.md。4.1 完整可运行示例动态行列 softmax仓库中的 softmax_impl.py 是动态行/列 尾块处理的教科书实现VALIDATED on Ascend a5rows64 cols64 时max_abs_diff 2.98e-08多核block_dim4pl.jit(auto_mutexTrue) def softmax_kernel( x: pl.Tensor[[pl.DYNAMIC, pl.DYNAMIC], pl.DT_FP32], y: pl.Tensor[[pl.DYNAMIC, pl.DYNAMIC], pl.DT_FP32], ): tile_type pl.TileType( shape[TILE_ROWS, MAX_N], dtypepl.DT_FP32, target_memorypl.MemorySpace.Vec, valid_shape[-1, -1] ) red_type pl.TileType( shape[TILE_ROWS, 1], dtypepl.DT_FP32, target_memorypl.MemorySpace.Vec, layoutpl.DN, valid_shape[-1, -1] ) in_group pl.make_tile_group(typetile_type, addrs[VA_IN0, VA_IN1], mutex_ids[0, 1]) out_group pl.make_tile_group(typetile_type, addrs[VA_OUT0, VA_OUT1], mutex_ids[2, 3]) tmp_group pl.make_tile_group(typetile_type, addrs[VA_TMP0, VA_TMP1], mutex_ids[4, 5]) red_group pl.make_tile_group(typered_type, addrs[VA_RED0, VA_RED1], mutex_ids[6, 7]) with pl.section_vector(): rows x.shape[0] cols x.shape[1] num_cores pl.get_block_num() core_id pl.get_block_idx() num_tiles (rows TILE_ROWS - 1) // TILE_ROWS # 向上取整 for tile_id in pl.range(core_id, num_tiles, num_cores): # 行 tile 跨向量核 row_off tile_id * TILE_ROWS valid_rows pl.min(TILE_ROWS, rows - row_off) # 尾块部分行 in_slot in_group.next() pl.set_validshape(in_slot, [valid_rows, cols]) pl.load(in_slot, x, [row_off, 0]) out_slot out_group.next() tmp_slot tmp_group.next() red_slot red_group.next() pl.set_validshape(out_slot, [valid_rows, cols]) pl.set_validshape(tmp_slot, [valid_rows, cols]) pl.set_validshape(red_slot, [valid_rows, 1]) pl.row_max(red_slot, in_slot, tmp_slot) # m max over N pl.row_expand_sub(out_slot, in_slot, red_slot) # x - m pl.exp(out_slot, out_slot) pl.row_sum(red_slot, out_slot, tmp_slot) # s sum over N pl.row_expand_div(out_slot, out_slot, red_slot) # / s pl.store(y, out_slot, [row_off, 0])关键点全部对应第 4 节清单(rows TILE_ROWS - 1) // TILE_ROWS是向上取整pl.min(TILE_ROWS, rows - row_off)是剩余 extent 钳制每个 load/compute/store 前都有pl.set_validshape行归约载体red_type显式声明layoutpl.DNdim0广播需要。独立数学参考是 softmax_golden.py。4.2 例外compact1tile 的窗口属于布局的一部分清单第 4 步对任何声明compact1的 tile 都是错的——照做会静默损坏数据。在compact1下布局是按当前 valid window 解释的CompactMode.md在写入与读取之间收窄窗口会让写者与读者解码同一段字节的两种不同 fractal 布局。仓库实测的两个案例均发生在compact1的 Acc 上某累加器在matmul前设为[tile_m, TN] [128, 256]8 个行块在pl.store前重设为[valid_m, valid_n]1 个行块。只有列块j 0地址重合于是每行 256 个单元里恰有 16 个存活、240 个错误——仅凭不匹配计数就能锁定机制。另一算子恰好在matmul之后、extract 之前调用set_validshape(acc, …)产生 4 个写行块对 3 个读行块只在奇数 M 尾块上复现因而被误读为形状特例。compact1累加器的契约每个输出 tile 只在第一次matmul之前设置一次窗口窗口钉在完整物理 tile[tile_m, TN]而不是 valid extentmatmul与store之间不得再对该 tile 调用set_validshapeK 轴始终为真。代价是尾块 tile 会多写(tile_m − valid_m) × TN × 4B的填充凡 M、N 整除 tile 的场景该代价为零。注意把窗口向上取整到 fractal 边界是 no-op 而非修复——ceil(ceil(vm/16)·16/16) ceil(vm/16)照此开方子会逐字节复现故障。5. Cube 侧专项检查按 dtype 获取收缩几何与布局Cube矩阵乘侧不能假设所有 dtype 共用一种 K 对齐或一种 fractal 形状。规则从安装的 API 和官方 matmul 示例获取 dtype 依赖的收缩几何与 Left/Right/Acc 布局要求。不要假设所有 dtype 共享同一种 K 对齐或同一种 fractal 形状。arch-a5.md 的 Design Checks 印证要从匹配平台文件读取 dtype 依赖的收缩几何不从假设出发launch 宽度从 runtime platform 信息读取而非硬编码 SKU 核心数。框架发现页还给出一个必须避开的坑A5 上不存在f322s8直接转换vconv表只有f162s8fp32 → int8必须经由 fp16fp32 → fp16 → int8这是量化算子的硬约束而非可选优化。5.1 单块 FP32 matmul 样例matmul_float_mmad_impl.py 是 Cube 侧最简参考VALIDATED on Ascend a5FP32 位精确max_abs_diff 0.000e00完整展示了五类内存空间的 tile 组声明pl.jit(auto_mutexTrue) def matmul_float_mmad_kernel( a: pl.Tensor[[M, K], pl.DT_FP32], b: pl.Tensor[[K, N], pl.DT_FP32], out: pl.Tensor[[M, N], pl.DT_FP32], ): a_l1 pl.make_tile_group(typepl.TileType(shape[M, K], dtypepl.DT_FP32, target_memorypl.MemorySpace.Mat, layoutpl.NZ), addrs0x00000, mutex_ids[0]) b_l1 pl.make_tile_group(typepl.TileType(shape[K, N], dtypepl.DT_FP32, target_memorypl.MemorySpace.Mat, layoutpl.NZ), addrs0x10000, mutex_ids[1]) a_l0a pl.make_tile_group(typepl.TileType(shape[M, K], dtypepl.DT_FP32, target_memorypl.MemorySpace.Left, layoutpl.NZ), addrs0x0, mutex_ids[2]) b_l0b pl.make_tile_group(typepl.TileType(shape[K, N], dtypepl.DT_FP32, target_memorypl.MemorySpace.Right, layoutpl.ZN), addrs0x0, mutex_ids[3]) c_l0c pl.make_tile_group(typepl.TileType(shape[M, N], dtypepl.DT_FP32, target_memorypl.MemorySpace.Acc, layoutpl.NZ, fractal1024), addrs0x0, mutex_ids[4]) with pl.section_cube(): ca, cb a_l1.current(), b_l1.current() al, br, ac a_l0a.current(), b_l0b.current(), c_l0c.current() pl.load(ca, a, [0, 0]) pl.load(cb, b, [0, 0]) pl.move(al, ca) # L1 - L0A pl.move(br, cb) # L1 - L0B pl.matmul(ac, al, br) # a b - L0C pl.store(out, ac, [0, 0])值得注意的数据流形态GM→L1Mat→L0A/L0B→matmul→L0CAcc→GMLeft 用pl.NZ、Right 用pl.ZN、Acc 带fractal1024。样例注释特别声明其*_wrapper只是该样例的自驱动harness交付 wrapper 只能调用torch.empty详见 wrapper-boundary.md。5.2 Cube 侧“存储复用”与尾块无关的另一条注意Cube 侧还有一条与“K 对齐因 dtype 而异”同族的隐患框架发现页第 18 条K 循环最后一个 matmul 必须是AccPhase.Final。全用AccPhase.Partial时即使是单块循环也会以device error type 0xFFFF死掉。同时“slot 数 ≥ 块数”是被证伪的旧结论——实测 56 块 7168 深度的收缩用两 slot 也能通过auto_mutex会正确地对 slot 复用与仍在该读的 matmul 排序。6. 证据与进一步阅读6.1 本页证据链tiling.md 声明的四条证据全部可在仓库内直接验证单块 matmulmatmul_float_mmad_impl.pyFP32 位精确BF16 操作数复用bf16_matmul_operand_reuse_impl.pyresidency 研究参考性能需在目标设备上重测动态行 softmaxsoftmax_impl.py动态尾块 valid window平台发现arch-a5.md显式 A5 或工作流默认 A5数值限制仍要求精确目标确认安装的$PYPTO_DEVKIT_DIR/docs/pypto_pro/api/页面与官方示例是当前版本的首要来源本文及 KB 中的一切数值约束都不得与其冲突。6.2 相关 KB 页面速查memory-layout.md五个内存空间的地址表、生命周期类、线性 extent 公式与 overlap 审查tail-validshape.md动态尾块六步清单与compact1契约arch-a5.md平台门控、平台文件主源路径、UB 容量裁决pypto-pro-framework-findings.md与 tiling 直接相关的框架缺陷——load/store参数顺序不对称第 5 条、auto_mutex按mutex_id而非地址重叠排序第 16 条、K 循环收尾AccPhase.Final第 18 条、无分配器且 overlap 不检查第 10 条vec-alignment-and-rotation.md32 字节 UB 量子与向量侧对齐细则7. 实战收尾建议把 tiling.md 变成日常检查表时建议固化以下动作每次设计开始前先锁版本记录目标 SKU、CANN、PyPTO 版本并解析平台.ini键禁止引用任何写死数值的二手页面。地址表 标识预算同步做五个内存空间各一张半开区间表mutex_ids预算与字节预算并列两个天花板二维 Tile、32 个auto_mutex标识在架构评审时就检查而不是等设备报错。尾块一律“合法物理 tile runtime valid window”向上取整、extent 钳制、load/compute/reduce/store 前逐个set_validshapecompact1的 Acc 窗口钉死在物理 tile 且中途不重设。Cube 侧按 dtype 查表K 对齐、fractal、L0A/L0B/Acc 布局从安装 API 与官方 matmul 样例取不跨 dtype 假设K 循环末块用AccPhase.Final。回归覆盖四档形状aligned、single-tail、multi-tail、multi-tile外加 tail-validshape 的“小于/等于/多整/带余”四档缺一不可。【免费下载链接】pypto-gymPyPTO-Gym 是基于 PyPTO 编程框架构建的算子与模型样例仓库项目地址: https://gitcode.com/cann/pypto-gym创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表