已合并
perf(npu-inductor): permute-gather — 三态分派 + K-tile + 动态 H 兜底 + H>4096 trans #44037
perf(npu-inductor): permute-gather — 三态分派 + K-tile + 动态 H 兜底 + H>4096 trans #44037
已合并
huyuchao创建于 8月7日
huyuchao成员
8月7日

【合入来源】

如有社区issue,请关联issue链接
请勿携带内部流程信息(需求链接、问题单、内部issue等)

【修改方案】

请描述修改内容的具体实现,涉及哪些组件之间进行交互,可以用1、2、3、...进行罗列
如果是需求或者重构类的PR,需要补充详细设计文档(说明上下游组件关系、时序图、类图、DFX能力等内容)

T5 形状的 permute+归约(out[b,k,i] = sum_j arg1[b,k,i,j] + sum_j arg0[i,j,k])在默认路径下 permute 物化为独立 HBM 全量转置 + 归约分 kernel,且归约轴非单位 stride 的 strided tl.load 在 Ascend 落标量 gather,DMA 效率低。本 PR 新增 enable_permute_gather(opt-in,默认关)将归约轴 strided load 改写为连续 DMA + 寄存器级置换。组件交互:

  1. config.pypermute_gather_mode(stride_r, elemsize) 三态分派——stride 字节 <256 → "gather"(H≤63 fp32);≥256 → "trans"(H≥64);gather 分支受 permute_gather_max_xblock(4096) 上限约束(gather pin XBLOCK=stride_r,受 TRITON max_block 限制),超限返回 None 回退 realize。阈值 flags 全部 opt-in(permute_gather_stride_gate_bytes / permute_gather_ktile / permute_gather_dynamic_trans / permute_gather_max_xblock

  2. lowering.pyenable_permute_gathernpu_permute 直接构造 ir.PermuteView 零拷贝逻辑 view(绕开 realize 物化);关闭时走既有 realize 逻辑(基线行为)

  3. codegen/triton.pyNPUTritonKernel.load 拦截 eligible 归约 load:

    • gather:整行连续 DMA(L = stride_r·R0_BLOCK 平铺)+ tl.gather 寄存器置换
    • trans:tl.make_block_ptr + tl.trans + boundary_check=[0,1,2] + padding_option="zero";静态 trans pin XBLOCK=min(stride_r, ktile=64),int 轴跨程序 chunk(x1_blocks=ceil(H/64));trans 分支不受 max_xblock 上限约束(XBLOCK 与 stride_r 解耦),H>4096 同样走 trans+K-tile(H=8192 → 128 个满 chunk,整除无 tail 浪费),realize 仅保留给 gather 超限场景;R0 公式 eff=min(stride_r, ktile) 解耦 R0/H
    • slot 检查放宽为互异且 0<=s<ndim(ndim=max(slots)+1),消除「归约轴在最后」的布局假设(DEFAULT scheduler patch 对大 H kernel 产生 R 树 slot 0 布局时不再误拒)
  4. npu_triton_heuristics.py:rewrite 下固定 XBLOCK/R0_BLOCK(real_block_row==1 才合法),autotune 不扫其它配置

  5. 评审修复(d308b142b5 / bc0df673bb / 562d97e39f,响应 @rmch 与 robot 评审;其中 562d97e39f 的 autotune winner 缓存键修复后移至独立 PR):删 kernel 内非法 raise AssertionError(P1,Python raise 非 Triton 合法语句);slot 预检先类型检查再 max(slot 缺失正常回退 strided,不再 TypeError);gather pidx/reshape 与 slot 顺序解耦(R-first 布局不再静默重排);enable_permute_gatherpin_xr 同开时 PG marker 优先(避免 body 已 rewrite 而 config 被 pin_xr 决定);加载缓存 autotune winner 时剥离非 kwarg 键(issue 4367);新增 4 个回归用例(UT 9→13)

  6. 字符串手术 AST 化(925b5372e1,评审遗留整改):codegen 内三处对生成代码的正则/eval 改写全部替换为结构化 AST 操作(复用本文件既有 _npu_parse_tl_load_assignment / _npu_assignment_parts 基建,与 _maybe_rewrite_select_lane_load 同款模式),删除 PR 自身引入的 import re

    • _npu_pg_rewrite_body:load 行定位由行文本正则改为按赋值目标名匹配——可穿透 .to(tl.float32) 提升链(贪婪 (.*)\) 无法定界,fp16/bf16 候选原实现会静默错配);非 str 行(DeferredLine)透传;emit 拒绝时保留原行回退(原实现返回空列表会丢行且仍下发 config pin)
    • _npu_pg_emit:ptr/other/eviction_policy 由 argtext 正则(P3:other=([^)]*)float('-inf') 等嵌套括号即截断)改为 load AST 节点 args[0]/keywords 结构化读取;末行经节点拼接把 tl.where(...) 换回原 value_ast 再 unparse,保留 .to() dtype 提升
    • _npu_pg_eval_real_blocks:正则+eval 改为 AST 解析(name : tl.constexpr = ... AnnAssign 归一化)+ 新增 _npu_pg_eval_expr 小型求值器(int 算术/比较/IfExp tile 三元式/min-max;不支持的节点照旧跳过,保持「对发射文本求值、免疫排序逻辑漂移」性质)
  7. 评审二轮(ff49a90cde / 73f2144299 / 0c785d501a):为满足 PR 总行数 ≤1000(现 967),autotune 缓存键修复移至独立 PR(本 PR revert);_npu_pg_eval_expr 求值器表驱动化、三处重复注释去重、测试 prose 压缩(13 用例与断言不动,232→197 行)——13 个 PG kernel 生成源码 sha256 与整改前逐字节一致;config.py 阈值分节 banner + 三个阈值取值依据导语(256=实测 gather/trans 交叉点+UB 预算而非 DMA 对齐边界、64=256B DMA 效率单元、4096=TRITON max_block)。

  8. 评审二轮补齐(89ea3c481c):全部 _npu_pg_* 实现提取到独立模块 codegen/permute_gather_rewrite.py(section:eligibility/geometry/validation + gather/trans 发射分区;NPUTritonKernel 留薄委托,调用点与测试 monkeypatch 不变);统一回退 debug 日志(模块 logger:geometry/mode 门、slot 缺失/非互异、tiling 校验失败、load 定位失败、rewrite 成功,均带原因与变量名);no_raise 断言并入 gather/trans 用例族;PR 总量 996 行。验证:12/12 + 13/13(clean);11 个唯一 PG kernel 源码 sha256 与迁移前逐字节一致。

设计文档:NPUPermuteGather_upstream_design.md / NPUPermuteGather_v2_design.md(随本 PR 提供,详细设计思路另见 commit message「修复报告」节)

【资料变更】

请确认是否涉及资料变更。如涉及,需要在PR中体现,并简要说明修改内容。如不涉及,需填写“不涉及”

不涉及

【接口变更】

请确认是否涉及跨代码仓或者客户面可见的接口变更。如涉及,需要详细说明接口以及对应的变更内容,同时需要在资料中体现。如不涉及,需填写“不涉及”

不涉及(enable_permute_gather 为 Inductor 后端内部配置项;对外 API 不变,生成 kernel 文本属内部产物)

【功能验证】

说明测试场景,测试方法。如果本次测试方式与常规单元测试不同,请详细说明您的测试步骤
新增/变更内容是否已新增/适配UT测试用例看护,并补充测试自验证截图

UT(新增 pytorch/test/_inductor/test_permute_gather.py,12 用例整批通过;no_raise 断言已并入 gather/trans 用例族)

image.png

数值精度:assert_close(atol=1e-4, rtol=1e-4) 全过(H=1024 max_err 3.05e-05、H=8192 max_err 1.53e-05、J=96 bit-exact)。

AST 整改回归(925b5372e1)test_permute_gather.py 13/13 + test_triton_experimental_enable.py 13/13(source env.sh clean 冷缓存);13 个 PG kernel 生成源码与整改前 sha256 逐字节一致(纯重构、零行为变化,上表性能数据不受影响);fp16 端到端补充验证——生成行 tmp1 = tl.where(r0_mask, tmp1_pg_g, 0.0).to(tl.float32) 保留 dtype 提升,compiled 与 fp32 金标 max|diff|=0.0000(eager fp16 自身 3.12e-02,即保 cast 后走 fp32 累加、比 eager 更准;旧正则实现在该场景会丢 cast)。

性能(CANN profiler 设备时长 ground truth,per-launch 各 kernel 均值求和;对比 eager / 基线 enable_permute_gather=False

分组 场景 eager (us) v2 当前 (us) v2 vs eager 加速比 max_err
静态 H=12(gather) 37.25 10.95 3.40× 1.5e-05
静态 H=32(gather) 27.75 15.58 1.78× 0.0
静态 H=64(trans+K-tile,chunk=1) 35.19 21.00 1.68× 1.5e-05
静态 H=72(trans+K-tile,chunk=2) 35.20 47.87 0.74×(倒退) 1.5e-05
静态 H=1024(trans+K-tile) 391.09 275.77 1.42× 3.1e-05
静态 H=8192(trans+K-tile) 4249.05 2662.03 1.60× 1.5e-05
动态 H=72(trans+K-tile,运行时 block_shape) 35.22 33.80 1.04× 1.5e-05
动态 H=100(trans+K-tile,运行时 block_shape) 54.26 45.19 1.20× 1.5e-05
泛化一 非对齐 H=65(260B,260%32=4) 47.95 59.82 0.80×(倒退) 1.53e-05
泛化一 非对齐 H=71(284B,284%32=12) 49.94 58.19 0.86×(倒退) 1.53e-05
泛化一 非对齐 H=73(292B,292%32=4) 47.77 58.63 0.81×(倒退) 1.53e-05
泛化一 非对齐 H=129(516B,516%32=4) 57.43 72.34 0.79×(倒退) 1.53e-05
泛化一 I=96(row 轴,H=72) 48.27 86.30 0.56×(倒退) 1.53e-05
泛化一 J=96(r 轴,H=72) 24.07 19.56 1.23× 0.0(bit-exact)
泛化一 B=4(batch,H=72) 45.39 88.96 0.51×(倒退) 1.53e-05
泛化二 H=12 29.1 12.9 2.26× 0.0
泛化二 H=32 43.1 38.5 1.12× 0.0
泛化二 H=64 64.1 54.0 1.19× 1.14e-05
泛化二 H=72 66.1 144.7 0.46×(倒退) 1.53e-05
泛化二 H=1024 1944.4 1468.4 1.32× 1.53e-05
泛化二 H=8192 18821.4 10697.8 1.76× 1.53e-05
泛化二 H=65 105.7 190.8 0.55×(倒退) 1.53e-05
泛化二 H=71 107.4 189.4 0.57×(倒退) 1.53e-05
泛化二 H=73 110.6 188.4 0.59×(倒退) 1.53e-05
泛化二 H=129 196.7 216.9 0.91×(倒退) 1.53e-05

已知倒退(如实):H=72 trans+K-tile 2.0× 慢于基线(ceil(72/64)=2 程序拆分,tail chunk 仅 8 行按 64 行 tile 读取读放大);非 32B 对齐 H(65/71/73/129)系统性 1.7–2.0× 慢(block_ptr 非对齐行宽读放大,后续优化方向 H%8≠0 gate 回退);H=32 gather 14% 慢;墙钟口径下短 kernel 场景(H≤128)被 ~130us/launch host 开销吞掉收益(设备时长收益 ≠ 端到端收益)。

CI:未跑仓 CI 流水线(本环境仅本地 NPU 单机验证)。

【CheckList】

PR提交人对以下CheckList自检项进行全量自检,自检通过或不涉及,均修改 [ ] 为 [x]

likedislike
Pull Request已成功合入, 合并人@ascend-robot
(感谢 huyuchao 的贡献)
Hhuyuchao成员
8月7日 创建了 pull request,commit d35568f3
atomgit-bot
atomgit-bot
8月7日 评论:

变更摘要

本 PR 为 NPU Inductor 后端新增 enable_permute_gather(opt-in,默认关闭)优化路径,针对 T5 形状的 permute+归约场景(如 out[b,k,i] = sum_j arg1[b,k,i,j] + sum_j arg0[i,j,k]),将归约轴上的非单位 stride tl.load(在 Ascend 上退化为标量 gather)改写为连续 DMA + 寄存器级置换。核心机制是「三态分派」:内部 stride 字节 < 256(H≤63 fp32)走 gather 模式(整行连续 DMA + tl.gather),≥ 256(H≥64 fp32)走 trans 模式(tl.make_block_ptr + tl.trans + K-tile 分块),动态 H 兜底走 trans 分支;gather 受 permute_gather_max_xblock(4096)上限约束,trans 则通过 permute_gather_ktile(64)将 XBLOCK 与 stride_r 解耦,使 H>4096 也能走 trans+K-tile 路径。此外,slot 检查放宽为互异且在 [0, ndim) 范围,消除了对「归约轴在最后」的布局假设。

主要改动

  • config.py 新增三态分派函数与 opt-in 门控配置:新增 permute_gather_mode(stride_r, elemsize) 三态分派函数,按 stride 字节 <256 返回 "gather"、≥256 返回 "trans"、超 permute_gather_max_xblock 返回 None;同时新增 enable_permute_gatherpermute_gather_stride_gate_bytespermute_gather_ktilepermute_gather_dynamic_transpermute_gather_max_xblock 等全部 opt-in 阈值开关。

  • lowering.pynpu_permute 新增零拷贝视图路径:当 enable_permute_gather 开启时,npu_permute 直接构造 ir.PermuteView 零拷贝逻辑视图,绕过 realize 物化;新增 _elemsize_of() 辅助函数用于门控判断;动态 stride 场景通过 permute_gather_dynamic_trans 开关兜底。

  • codegen/triton.pyNPUTritonKernel 新增 permute-gather 归约 rewrite 全链路:新增 _npu_pg_record / _npu_pg_geometryload 时拦截符合条件的 strided 归约 load 并计算几何信息;新增 _npu_pg_rewrite_bodycodegen_body 后验证 tiling 不变式并替换 load 行;新增 _npu_pg_emit(gather 模式:平坦 DMA + tl.gather)和 _npu_pg_emit_trans(trans 模式:tl.make_block_ptr + tl.trans + boundary_check=[0,1,2] + padding_option="zero")两种发射路径;slot 校验放宽为 0<=s<ndim 且互异,消除对归约轴末位的布局假设。

  • npu_triton_heuristics.pyreduction() 新增 permute-gather 固定配置 pin:当 triton_meta 中存在 npu_permute_gather 标记时,固定 XBLOCK 和 R0_BLOCK 为 rewrite 验证通过的配置(real_block_row==1),通过 cached_autotune 单配置返回,禁止 autotune 扫其他配置。

  • 新增 test_permute_gather.py 测试文件:包含 9 个用例,覆盖 gather 模式(H=12,32)、trans+K-tile 模式(H=64,72,1024)、动态 H 兜底(H=72,100)、超大 H trans(H=8192)及禁用路径回退,通过内核源码标记(tl.gather / tl.make_block_ptr / boundary_check)验证 rewrite 是否生效,并 assert_close(atol=1e-4, rtol=1e-4) 校验数值精度。

likedislike
不准确?
atomgit-bot
atomgit-bot
8月7日 评论:

代码审查

现在我已审查完所有 5 个文件。以下是最终总结。


审查总结

发现的问题

优先级 数量
P0 0
P1 1
P2 0
P3 1

各文件审查结果

  • test/_inductor/test_permute_gather.py:无问题。测试结构正确(setUp/tearDown 隔离、compile_threads=1 避免 fork 问题)、模式标记合理、数值容差适当。
  • torch_npu/_inductor/triton_experimental/codegen/triton.py:发现 2 个问题(P1 + P3),详见上述报告。核心逻辑(geometry 匹配、UB 预算计算、tiling 不变量验证、body 重写、trans 模式 perm 推导)经审查均正确。
  • torch_npu/_inductor/triton_experimental/config.py:无问题。permute_gather_mode 的三态分派逻辑正确,配置项均有合理默认值,阈值注释与实测数据一致。
  • torch_npu/_inductor/triton_experimental/lowering.py:无问题。npu_permuteenable_permute_gatherrealize_permute_gather 的控制流正确,benefit gate 逻辑经审查与 permute_gather_mode 一致。
  • torch_npu/_inductor/triton_experimental/npu_triton_heuristics.py:无问题。npu_permute_gather config pin 正确地从 triton_meta 读取并传递给 cached_autotune

整体风险评估

中等风险。P1 问题(raise AssertionError 在生成的 Triton kernel 代码中)在编译期守卫失效时会导致 kernel 编译硬失败,但由于 _npu_pg_rewrite_body 中的编译期检查充分,触发概率较低。P3 问题(正则解析 other 值的脆弱性)在当前 T5 sum 模式下不会触发,但若未来扩展到 max/min 归约则需修复。由于 enable_permute_gather 默认为 False(opt-in),现有用户不受影响。建议在合入前修复 P1 问题(移除无效的运行时断言行)。

类型 数量
🔴 阻塞 1
🟡 建议 0

⛔ 需要修改

likedislike
不准确?
ascend-robotascend-robot成员
8月7日 添加了label:ascend-cla/yes
此处折叠了129条消息 查看更多
Hhuyuchao成员
16 小时前 解决了最后一个问题
ascend-robotascend-robot成员
16 小时前 关闭了关联的issue
ascend-robotascend-robot成员
16 小时前 合入了pull request
ascend-robot
ascend-robot成员
16 小时前 评论:

Pull Request 已合并或已关闭。

If you want to solve this problem, you can click here to do it in the FAQs.

likedislike
AtlasAccount
AtlasAccount成员
16 小时前 评论:
流水线 pytorch_gitcode_PR_multiVersion#15020 [ commitID:0b28567e ] 已完成
likedislike