已关闭
[RFC]: reduction autotune 方案 #3464
AllenGuan创建于 7月29日关闭于 8月13日
7月29日 添加了label:rfc
7月30日 关联了看板:@yao-xiaobai的看板 20260730
8月10日 添加了label:bot-triaged
TorchNPU-Bot
8月10日 评论:
8月10日 评论:
检测到当前 issue 已关联 PR,自动添加标签:bot-triaged


8月13日 关闭了 issue
8月13日 添加了label:resolved
Reduction Autotune 候选集拟合报告
状态(Status): Draft
作者(Authors): @Your_community
创建日期(Created): 2026-07-29
更新日期(Updated): 2026-07-29
相关 Issue/PR: #(待关联 Issue/PR 以便追踪背景)
1. 概述
1.1 简介
本报告给出 reduction kernel 的 autotune 候选集规律与公式驱动的候选生成算法
select_reduction_final,并以 dense 最优为参考,对比 npu-inductor 生产启发式与本文算法的性能与编译成本。每个 kernel 的流程为:候选集生成 → autotuner 编译并 benchmark 每个候选、取最快 → 与 dense 全局最优对比。本文算法实现 95% 无损 / 0% 严重,全面优于生产启发式(73% / 7%);真实编译数 20,比生产启发式 59 少 66%、比 dense 79 少 75%——候选更少、质量更高。机理:按 reduction 轴长
rnumel把搜索空间精确切三段(条件剪枝),每段只发命中带内的锚点,不再撒大网——故编译数下降而非膨胀。1.2 动机
使用场景 / 数据底座:面向 Ascend NPU(硬件:Ascend910B3,UB=192KB,40 个 AIV 向量核)上 reduction kernel 的 autotune。数据底座为 153 个 reduction kernel(存量 57 = generated 49 + torchbench 8;backward 96 = resnet18/50 bw bf16 66 + fp32 30),全部 dense mspti 实测(每个 kernel 在完整 tile 格点上 bench,winner = dense 全局最优 = min ms_mspti)。
out/generated_reduction{,_torchbench,_torchbench_bw_dense_bf16,_torchbench_bw_dense_fp32}.csv。当前痛点:npu-inductor 生产启发式(autotune_enhance)在 snap=2.0 口径下仅 73% 无损、7% 严重(snap=1.0 严格口径 63% / 11%),且真实编译数达 59;dense lattice 虽 100% 无损但需 79 候选、编译成本高。
必要性与用户价值:候选集是 autotune 编译耗时的近似线性决定因素(autotuner 逐候选编译 + mspti bench)。更少且更准的候选既降低 autotune 编译时间,又消除严重性能回退(0% severe)。不做此提案则维持 7% 严重回退 kernel 与约 3× 的冗余编译。
1.3 目标
目标:
select_reduction_final(drop-in 替换reduction()候选生成步骤)。非目标(边界):
2. 用例分析
场景:Ascend NPU reduction kernel 的 autotune 候选生成。
功能点:按 reduction 轴长
rnumel把搜索空间精确切三段(HOLE / SMALL/FILL / MID,条件剪枝),每段只发命中带内的锚点,不再撒大网。关键性能指标:
DFX(兼容性、可维护性、可测试性、可靠性)要求:兼容性(参数名与 npu_inductor autotune 源码一致,drop-in);可维护性(单调并集、优雅退化);可测试性(dense mspti 双 snap 口径可复现);可靠性(0% severe,HOLE/SMALL 阈值落宽死区中央、鲁棒)。
使用限制 / 约束:前置条件
xnumel/rnumel/num_load/num_reduction均为 ≥1 整数;硬件 Ascend910B3(UB=192KB、40 AIV)。3. 方案设计
3.1 总体方案
select_reduction_final.select_candidates是纯候选生成器——输入(xnumel, rnumel, dtype, num_load, num_reduction, min_ptr_elem_bytes),输出(XBLOCK, R0_BLOCK)元组列表。参数名与 npu_inductor autotune 源码一致——生产中每个值都从 kernel 的size_hints/inductor_meta/triton_meta签名读出,故与生产reduction()启发式产出的 Config 二元组同形,是候选生成步骤的 drop-in 替换。整体设计思路:按rnumel三分支(HOLE / SMALL / MID)精确切搜索空间,每段只发命中带内的锚点。reduction 真正被 autotuner 扫的旋钮只有
(XBLOCK, R0_BLOCK)(源码坐实,npu_inductor/npu_triton_heuristics.py)——输出轴 / reduction 轴的 tile 尺寸。其余全 pin 或派生:next_pow2((X·R0)//128)xnumelpow2_floor(xnumel)rnumeldtype_dt_bytes→ 元素字节d,决定 tile 乘积 P*(见下)num_load,num_reductionbuffers = num_load+num_reduction+1,决定 UB 预算min_ptr_elem_bytesdtype推导(混合 dtype kernel)理论依据(153 kernel dense 数据坐实):
① rnumel 的三段"死区"分割 —— 剪枝的预判条件。 reduction 轴长
rnumel把 153 个 winner 干净切成三段,两段空隙里 0 个 kernel:64 < rnumel < 12825088 < rnumel < 50176→ 阈值
rnumel=64、rnumel=50000落在宽真空带中央,不是过拟合。三段 winner 的 R0 行为完全不同:② tile 乘积 P=X·R0 是 2 的幂。 93% 的 winner P 是精确 pow2,88% 落在 {1024,2048,4096,8192},众数 4096(36%)。winner 贴 UB 墙(
P·d·buffers·_NPU_UB_OVERHEAD_FACTOR / _NPU_UB_CAPACITY_BYTES中位=1.0)。但 P 与 buffers 几乎无关(corr(log2 P, log2 buffers)=−0.04)、dtype 影响弱(bf16 仅 1.10× fp32)——故 P* 取 dtype 决定的常数:P* = 2048 if d≤2 else 4096(d= 元素字节)。③ X 轴与 regime 无关。 pow2 阶梯封顶
pow2_floor(xnumel)单独即覆盖 97%。wX>32 只在短 rnumel 出现(HOLE 强制 wX≤8,28/28)。→ X 侧不需 regime 分支,x_cap自动适配。候选数瓶颈全在 R0 侧。3.2 技术选型
考虑过但放弃的其他方案,优劣对比与不选择理由:
_full_r_range(8,16384),质量低且候选多select_reduction_finaldense 质量上限最高但编译成本不可承受;生产启发式候选多且 7% 严重;本文以定向发射兼顾质量与编译成本。
3.3 功能与性能设计
算法公式(
select_reduction_final.select_candidates):给定
(xnumel, rnumel, dtype, num_load, num_reduction, min_ptr_elem_bytes),按rnumel三分支输出(XBLOCK, R0_BLOCK)候选(变量名与源码完全一致):逐项通俗解释:
_full_r_range(8,16384)蹭到 4K-8K 故 HOLE 只 4% severe,但非定向;本文固定格精确命中 → HOLE 100% 无损)。SMALL fill 把搜索坍缩到 X。MID 预测锚点 (pX,pR) 再发定向小格。log2(X/R0)=−2.06+0.59·log2(xnumel/rnumel))。R²=0.62 偏松,故配小-R0 列 + hugex + FILL 列做安全网。源码(即
report/reduction final report/select_reduction_final.py的核心,贴近可用):"""Reduction autotune candidate GENERATOR (drop-in for npu-inductor reduction()).""" import math _NPU_UB_CAPACITY_BYTES = 192 * 1024 # :1711 Ascend910B3 Unified Buffer NPU_CU_COUNT = 40 # :218 AIV vector cores _NPU_UB_OVERHEAD_FACTOR = 2.0 # :1716 double-buffer + scratch # B1 HOLE 固定格:此段 winner R0 与 rnumel 无关(UB 饱和) HOLE_X = (1, 2, 4, 8) HOLE_R = (512, 1024, 2048, 4096) # rnumel 三段阈值——均落在宽死区中央(无 kernel 落在 64<rnumel<128 / 25088<rnumel<50176) RN_HOLE = 50000 # >= : UB 饱和段(语料 min 50176) RN_SMALL = 64 # <= : fill / 过 tile 段 POW2 = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576] def _p2floor(n): return None if (n is None or n < 1) else 1 << int(math.floor(math.log2(n))) # 最大 2 幂 ≤ n def _p2ceil(n): return None if (n is None or n < 1) else 1 << int(math.ceil(math.log2(max(1, n)))) # 最小 2 幂 ≥ n def _p2round(n): return 1 if (n is None or n < 1) else 1 << int(round(math.log2(max(1, n)))) # 最近 2 幂 def _dt_bytes(dtype, min_ptr_elem_bytes): if min_ptr_elem_bytes: return min_ptr_elem_bytes return 2 if dtype in ("bf16", "fp16", "f16") else 4 # i1 走父计算 dtype def select_candidates(xnumel, rnumel, dtype, num_load, num_reduction, min_ptr_elem_bytes=None): """生成 reduction kernel 的 autotune 候选 (XBLOCK, R0_BLOCK)。理论依据见 §3.1。 前提:xnumel/rnumel/num_load/num_reduction 均为 ≥1 的整数(故相关冗余守卫已去)。""" d = _dt_bytes(dtype, min_ptr_elem_bytes) x_cap = _p2floor(xnumel) # ① XBLOCK 上限 = 最大 2 幂 ≤ xnumel(唯一硬 X 约束) P = 2048 if d <= 2 else 4096 # ② tile 乘积目标 P*(dtype 决定,与 buffers 无关) buffers = num_load + num_reduction + 1 # :1749 ub_cap = _NPU_UB_CAPACITY_BYTES / (d * buffers * _NPU_UB_OVERHEAD_FACTOR) # ③ UB 软上限(loose,真 UB 由编译失败剪枝) rmax = _p2floor(min(rnumel, max(ub_cap, 1))) # UB-bounded R0 上限;max(.,1) 兜超大 buffers r_cap = _p2ceil(rnumel) # ④ R0 过-tile 上限(rnumel=49 → 64) cands = set() def add(X, R): if X > xnumel: return # XBLOCK ≤ xnumel(B1 固定格在 xnumel<8 时裁剪) if R > r_cap: return # 仅允许小过 tile cands.add((X, R)) # ---- B1 HOLE:UB 饱和、与 rnumel 无关 → 固定 4×4 格 ---- if rnumel >= RN_HOLE: for X in HOLE_X: for R in HOLE_R: add(X, R) return sorted(cands) # ---- B2 SMALL/FILL:R0=rnumel(含过 tile),搜索坍缩到 X ---- if rnumel <= RN_SMALL: rset = {rnumel, _p2ceil(rnumel)} pX = _p2round(P / rnumel) for X in {1, pX // 4, pX // 2, pX, pX * 2, pX * 4}: if X <= x_cap: for R in rset: add(X, R) return sorted(cands) # ---- B3 MID (64<rnumel<50000):预测锚点 (pX,pR) + 定向格 + 安全列 ---- ratio = xnumel / rnumel # ⑤ OLS split: log2(X/R0)~log2(xnumel/rnumel), R²=0.62 (degrades gracefully if off) logsplit = -2.06 + 0.59 * math.log2(ratio) split = 2.0 ** logsplit pX = min(_p2round(math.sqrt(P * split)), x_cap) pR = min(_p2round(math.sqrt(P / split)), rmax) xs = [x for x in sorted(set([1, max(1, pX // 2), pX, min(pX * 2, x_cap), x_cap])) if 1 <= x <= x_cap] # 核心 X 阶梯(恒含 1 与 cap) rs = [r for r in sorted(set([max(1, pR // 2), pR, min(pR * 2, rmax)])) if 1 <= r <= rmax] # 核心 R0 ±一档 for X in xs: for R in rs: add(X, R) for R in (pR // 4, pR // 8, 16, 32): # 小-R0 列(P* 在小输出轴上过估 R0) if 1 <= R < pR and R <= rmax: add(pX, R) if rnumel <= ub_cap: # FILL 列:兜住漏进 MID 的 wR==rnumel winner for X in [p for p in POW2 if p <= min(x_cap, 128)]: add(X, rnumel) add(x_cap, 2); add(x_cap, min(rnumel, 8)) # hugex 病态锚点 return sorted(cands)逐行解释:
x_cap:X 唯一硬约束(wX ≤ pow2_floor(xnumel),153/153 命中)。X 阶梯封顶它即自动适配 regime——小 xnumel 给少而小的 X(含全部 HOLE / 小 wX),大 xnumel 给完整上阶梯。X 不是候选数瓶颈,故无 X 侧 regime 分支。P*:winner 的 X·R0 跨 kernel 落在 {1024,2048,4096,8192},dtype 决定中心(fp32→4096、bf16→2048),与 buffer 数无关(见 §3.1 理论依据 ②)。B3 把 P* 拆成 (pX,pR)。ub_cap/rmax:UB 软上限,给 R0 一个合理上界;真 UB 由 autotuner 编译失败剪枝(静态 UB 是 loose 上限,故不拿它硬拒候选)。r_cap:仅允许"ceil 一档"过 tile(实测 rnumel=49 → winner R0=56/64),不放大成任意过 tile。_full_r_range(8,16384)(≈59 候选)更少更准。split(OLS):给定 P*,X:R0 的分劈由 xnumel/rnumel 长宽比决定。R²=0.62 偏松,故配小-R0 列 + FILL 列 + hugex 做安全网(系数扰动 ±50% 仍 86-90% 无损,优雅退化)。接入点(drop-in):本函数经
inject.install_injection(inject.py:46)patch 唯一 chokepointNPUCachingAutotuner._precompile_worker(npu_triton_heuristics.py:758,self.configs在:771被读)替换self.configs,随后走原 compile→mspti-bench→argmin 管线(winner 仍由min(timings)决定)。reduction grid 固定 40 程序、与 tile 无关,候选只改 per-core trip count(输出 tile 数 = xnumel/XBLOCK、reduction trip 数 = rnumel/R0_BLOCK)——多余候选只增编译耗时,绝不拖累运行性能。性能指标 / 收益(对比 heuristic 与 dense):
每个 kernel:候选集生成 → autotuner 编译并 benchmark 每个候选、取最快 → 与 dense 全局最优对比。下表为 选中-best / dense 全局最优 的分层占比(n=153,snap=2.0 生产代理口径):
select_reduction_final候选数 / 编译成本:
select_reduction_final候选数 20 → 编译时间相应下降(autotuner 逐候选编译 + mspti bench,候选数近似线性决定 autotune 耗时)。
质量对比(双 snap 口径,n=153):
snap=2.0 生产代理口径(候选集含一个落在 winner 2× 邻域内的 tile):
snap=1.0 严格 exact-tile 口径(winner 必须被精确发射):
3.4 安全隐私与DFX设计
安全隐私:本提案为编译器 autotune 候选生成优化,不涉及用户数据、网络与权限,无安全隐私影响(不适用)。
DFX(兼容性、可维护性、可测试性、可靠性):
reduction()同形,drop-in 替换候选生成步骤,不动 compile→mspti-bench→argmin 管线。3.5 编程与调用设计
本算法以纯 Python 函数提供,供 npu_inductor autotune 集成调用(二次开发)。
3.5.1 编程模型基本设计
开发环境:Ascend910B3(UB=192KB、40 AIV);Python;相关常量
_NPU_UB_CAPACITY_BYTES(=192×1024)、_NPU_UB_OVERHEAD_FACTOR(=2.0)、NPU_CU_COUNT(=40)。开发约束:前置条件
xnumel/rnumel/num_load/num_reduction均为 ≥1 整数;min_ptr_elem_bytes缺省时由dtype经_dt_bytes推导d。可验收设计:n=153 kernel dense mspti 基线;验收指标为 ≤5% 无损占比 / >30% 严重占比 / 真实编译数 n_pred(见 §4、§3.3)。
3.5.2 接口定义与设计
参数 / 常量的源码出处(均在
npu_inductor/npu_triton_heuristics.py,已逐条核对):xnumelxnumelsize_hints["x"](reduction():2372)rnumelrnumelsize_hints["r0_"](reduction():2373)dtypedtypetriton_meta指针签名派生(reduction()不读inductor_meta["dtype"]);fallback:min_ptr_elem_bytes缺省时_dt_bytes据此推dnum_loadnum_loadinductor_meta["num_load"](:1765)num_reductionnum_reductioninductor_meta["num_reduction"](:1766)min_ptr_elem_bytesmin_ptr_elem_bytes_min_ptr_elem_bytes(triton_meta["signature"])(:2459定义,:2474调用)dbuffers(局部)buffersmax(1, num_load+num_reduction+1)(:1749,pointwise UB helper 同式)x_cap(局部)x_capreduction():2521r_cap(局部)r_capreduction():2588_NPU_UB_CAPACITY_BYTES_NPU_UB_CAPACITY_BYTES:1711(= 192×1024)_NPU_UB_OVERHEAD_FACTOR_NPU_UB_OVERHEAD_FACTOR:1716(= 2.0)NPU_CU_COUNTNPU_CU_COUNT:218(=get_npu_vector_core_count()= 40)3.5.2.1 select_candidates
(XBLOCK, R0_BLOCK),作为 npu-inductorreduction()候选生成步骤的 drop-in 替换。select_candidates(xnumel, rnumel, dtype, num_load, num_reduction, min_ptr_elem_bytes=None) -> list[tuple[int, int]]_dt_bytes推导(XBLOCK, R0_BLOCK)候选元组列表(已去重、排序)xnumel/rnumel/num_load/num_reduction均为 ≥1 整数;候选为 winner 模式超集,多余候选只增编译、不损运行。select_candidates(xnumel=64, rnumel=401408, dtype="fp32", num_load=6, num_reduction=2) # B1 HOLE -> 16 档 {1,2,4,8}×{512,1024,2048,4096} select_candidates(xnumel=65536, rnumel=49, dtype="fp32", num_load=2, num_reduction=1) # B2 SMALL -> 12 档(R0∈{49,64},含过 tile) select_candidates(xnumel=2048, rnumel=768, dtype="fp32", num_load=6, num_reduction=2) # B3 MID -> 27 档(预测锚 + 定向格 + FILL 列)3.5.3 编程手册设计
本提案未规划独立《编程手册》(不适用)。集成参考以
select_reduction_final.py源码注释为准。4. 测试设计
方法:每个 kernel 在完整 tile 格点上 dense mspti bench,winner = dense 全局最优 = min ms_mspti;候选集选中最快 ÷ dense 全局最优,按损失分层(≤5% / 5-10% / 10-30% / >30%)。双 snap 口径:snap=2.0(候选集含一个落在 winner 2× 邻域内的 tile)/ snap=1.0(winner 必须被精确发射)。
语料:153 个 reduction kernel(generated 49 + torchbench 8 + backward 96)。
图 1:选中-best / dense 全局最优(n=153):
final_perf_vs_rnumel.png
每个点是一个 kernel:纵轴 = 候选集选中最快 ÷ dense 全局最优(>1=变慢),按损失分层着色、按 regime 分标记。本文算法 95% 落在 ≤5% 无损带、0% 超 1.3×(snap=2.0);两条蓝虚线 = 死区分界 rnumel=64 / rnumel=50000——三 regime 沿 rnumel 轴的视觉证据。
图 2:winner 分布 vs 公式覆盖:
final_winners_XR0.png
winner 在
(XBLOCK, R0_BLOCK)log-log 空间:HOLE(红■)聚集在左上红框(X∈{1,2,4,8} × R0∈{512..4096},正是 B1 固定格);灰对角线 = tile 乘积 P=X·R0(众数 4096)。三 regime 在该空间清晰可分,公式覆盖精准。分 regime 对比(snap=2.0,参考=dense 100%):
本文在所有 regime 都不退化、且把生产启发式的 severe 全部清零;HOLE 灾区从 79%/4% 提到 100%/0%。
工具:
red_eval.py(snap-eval harness)+eval_heuristic.py(生产启发式 vs dense,monkeypatch 真cached_autotune取候选)+select_reduction_final.py自评 main(双 snap)+make_final_figures.py(出图)。数据:kernels.json(153 kernel 紧凑表,由red_data.py从 4 dense CSV 导出)。5. 缺点和风险
6. 现有技术
npu-inductor 生产启发式(autotune_enhance):非定向
_full_r_range(8,16384)撒网,HOLE 段蹭到 4K-8K 故仅 4% severe 但非定向;候选 59、73% 无损(snap=2.0)。本文以定向固定格(HOLE 4×4)精确命中,候选 20、95% 无损。dense lattice:全 tile 格点穷举,100% 无损但 79 候选、编译成本最高。本文为逼近其质量上限的低成本方案(snap=2.0 距 dense 仅差 5pp)。
7. 未解决问题
待社区讨论/决策的开放问题(详见 §5 风险/未来工作):
附录
参考资料链接 / 产物:
select_reduction_final.py(select_candidates,可直接接入 npu-inductor reduction 启发式)。red_eval.py(snap-eval harness)+eval_heuristic.py(生产启发式 vs dense,monkeypatch 真cached_autotune取候选)+select_reduction_final.py自评 main(双 snap)。make_final_figures.py→final_perf_vs_rnumel.png、final_winners_XR0.png。kernels.json(153 kernel 紧凑表,由red_data.py从 4 dense CSV 导出)。规律探索记录.md。术语表:UB(Unified Buffer,192KB)· AIV(向量核,40)· mspti(实测计时)· regime(HOLE / SMALL / MID 三段)· tile(XBLOCK / R0_BLOCK 分块)· snap(候选覆盖 winner 的口径,2.0 / 1.0)· winner(dense 全局最优 = min ms_mspti)· n_pred(真实编译数 / 候选数 per kernel)。
文档更新计划:随 MID 重拟合 / selector 形状接入进展更新。
欢迎加入社区,感谢您对社区的贡献 🎉!