已合并
[inductor][Feature] Adaptive Gear Update #35530
zhudada0120创建于 5月13日
[inductor][Feature] Adaptive Gear Update #35530
已合并
Pull Request已成功合入, 合并人@ascend-robot
(感谢 zhudada0120 的贡献)5月13日 创建了 pull request,commit 4f390cf7
ascend-robot
5月13日 评论:
5月13日 评论:
ascend-robot
5月13日 评论:
5月13日 评论:
Thanks for your pull-request.
The full list of commands accepted by me can be found at here。
You can get sig-info at here
PR Approval Progress
✅ Congratulations! All modules have met the lgtm and approve requirements.
Module Approval Details
| module | lgtm status | approve status |
|---|---|---|
| test | ✅ chujinjin, luochao60 (2/2) | ✅ chujinjin (1/1) |
| torch_npu/_inductor | ✅ chujinjin, luochao60 (2/2) | ✅ chujinjin (1/1) |
| torch_npu/npu | ✅ chujinjin, luochao60 (2/2) | ✅ chujinjin (1/1) |
| torch_npu/utils | ✅ chujinjin, luochao60 (2/2) | ✅ chujinjin (1/1) |
💡 Tip:
- Committer can comment
/approveor/lgtm- Commenting
/approveimplies both code review (lgtm) and intent to merge (approve)
CLA Signature Pass
zhudada, thanks for your pull request. All authors of the commits have signed the CLA. 👍


5月13日 添加了label:ascend-cla/yes
ascend-robot
5月13日 评论:
5月13日 评论:
当前仓库存在以下 保护分支 :
| Protected Branch | Version | Release |
|---|---|---|
| master | ||
| v2.7.1 | ||
| v2.9.0 | ||
| v2.10.0 | ||
| v2.11.0 | ||
| v2.12.0 |
评论 /sync <branch1> <branch2> ... 可将当前 PR 修改同步到其它分支(创建同步 PR):
a) 如果当前 PR 是 Open 状态,同步操作将延迟到 PR 被合并时执行
b) 如果当前 PR 已经 Merged,将立即执行同步操作
注意:
- /sync 命令可以指定同步到多个分支,仅最后一个 /sync 命令生效
- 如果创建的同步 PR 不正确,可通过向同步 PR 的源分支提交轻量级 PR 完善,或使用 /close 命令关闭


此处折叠了218条消息 查看更多
7月1日 添加了label:ci-pipeline-passed
ascend-robot
7月1日 评论:
7月1日 评论:
流水线 PR-pipeline_pytorch#40429 [ commitID:471bc0fd ] 已完成
>>>代码风格自动修复执行成功(无修复内容)
| 阶段 | 任务名 | 状态 | 详情 |
|---|---|---|---|
| 编译构建 | Build_X86 | ✅ | >>> |
| Build_ARM | ✅ | >>> | |
| Build_LibTorch_x86 | ✅ | >>> | |
| Build_LibTorch_ARM | ✅ | >>> | |
| Build_X86_torchair | 🛑 | >>> | |
| Build_ARM_torchair | 🛑 | >>> | |
| patch_test | 🛑 | >>> | |
| 恶意代码检查 | Antipoison | ✅ | >>> |
| 编码安全与规范检查 | CodeCheck | ✅ | >>> |
| check_error | ✅ | >>> | |
| CodeCheck_lintrunner | ✅ | >>> | |
| 开源片段检查 | SCA | ✅ | >>> |
| 开发者测试 | UT_X86_Part_01 | ✅ | >>> |
| UT_X86_Part_02 | ✅ | >>> | |
| UT_ARM_A3_Part_01 | ✅ | >>> | |
| UT_ARM_A3_Part_02 | ✅ | >>> | |
| UT_ARM_A2_Part_01 | ✅ | >>> | |
| UT_ARM_A2_Part_02 | ✅ | >>> | |
| UT_ARM_A2_Part_03 | ✅ | >>> | |
| UT_inductor_Part_01 | ✅ | >>> | |
| UT_inductor_Part_02 | ✅ | >>> | |
| UT_inductor_Part_03 | ✅ | >>> | |
| UT_inductor_Part_04 | ✅ | >>> | |
| UT_DIST_ARM_Part_01 | 🛑 | >>> | |
| UT_DIST_ARM_Part_02 | 🛑 | >>> | |
| UT_DIST_ARM_Part_03 | 🛑 | >>> | |
| UT_DIST_ARM_Part_04 | 🛑 | >>> | |
| UT_ARM_A2_Select_Part_01 | ✅ | >>> | |
| UT_ARM_A2_Select_Part_02 | ✅ | >>> | |
| 流水线 | PR-pipeline_pytorch | ✅ | >>> |
- compile、compile_inductor、compile_torchair : 运行流水线
- retry : 重试流水线所有失败子任务
- retry <任务名> : 仅重试指定失败子任务
- stop : 停止流水线


7月1日 合入了pull request
ascend-robot
7月1日 评论:
7月1日 评论:
Pull Request 已合并或已关闭。
If you want to solve this problem, you can click here to do it in the FAQs.


ascend-robot
7月1日 评论:
7月1日 评论:
流水线 pytorch_gitcode_PR_multiVersion#11669 [ commitID:471bc0fd ] 已完成


1. 背景
当前 torch_npu inductor 动态 shape 的 gear 分档主要在初始化阶段确定,后续运行过程中基本保持不变。这种静态分档策略在实际 workload 中可能暴露出两类问题:
为解决上述问题,需要引入自适应档位更新机制,根据运行时统计动态调整档位分布,在保持稳定性的同时提升资源利用率。
系统在运行期间持续回答三个问题:当前哪些 gear 值得保留、哪些可以安全删除、哪些区域值得新增。
2. 设计目标
3. 总体运行时流程
3.1 端到端闭环
flowchart LR A[请求进入 shape handling] --> B[读取当前 gears 快照] B --> C[按 current gears 执行 transform] C --> D[记录 raw shape / mapped gear / pad / split] D --> E[执行 compiled graph] E --> F[执行 recover] F --> G[请求返回] H[Worker 线程定时醒来] --> I{检查触发条件} I -- 距上次更新 ≥ 300s --> J[执行更新] I -- 显存使用率 ≥ 阈值 --> J I -- 都不满足 --> H J --> K[加锁: 获取快照 + 构建统计 + 打分 + 淘汰 + 新增 + 预算判断] K --> L[更新 current gears 快照] L --> M[调用 pool.remove_by_keys 清理 graph] M --> H3.2 职责划分
get_snapshot()读快照 → transform →src_fn(compile+execute) →consume_recent_keys()→record_event→ recoverrun_once(在_commit_lock下完成打分、淘汰、新增、提交)→ 锁外pool.remove_by_keyssequenceDiagram participant Req as 请求线程 participant RT as AdaptiveGearRuntime participant Worker as Worker 线程 (daemon) participant WK as GearUpdateWorker Note over Worker: 每 60s 醒来一次 Req->>RT: get_snapshot() (无锁读) RT-->>Req: gears 快照副本 Req->>RT: record_event(...) RT-->>Req: 返回 Note over Req,RT: 请求线程只做快照读取和事件记录 Worker->>Worker: _should_trigger_update() alt 触发条件满足 Worker->>WK: run_once(now_ts) Note over WK: 在 _commit_lock 保护下 WK->>RT: get_snapshot() + build_stats_snapshot() WK->>WK: score / evict / add / budget WK->>RT: commit_update() Note over WK: 释放 _commit_lock WK->>WK: pool.remove_by_keys(keys) else 条件不满足 Worker->>Worker: 继续等待 end3.3 并发控制
_sample_lock_states读写record_event、build_stats_snapshot、_collect_cleanup_keys_commit_lockrun_once全程GearSnapshotStore._lockpublish_snapshotget_snapshot()无锁(clone-on-read),请求线程不阻塞。graph 清理在所有锁外执行。3.4 Graph 清理机制
图资源通过
GraphResourcePool(per-device 单例)管理,使用不透明整数句柄作为 key:pool = GraphResourcePool.get_pool(device_index) key = pool.register(resource) # 下层 — graph capture 期间 → int keys = pool.consume_recent_keys() # 上层 — src_fn 返回后 → List[int] pool.remove_by_keys([42, 43]) # 后台 — gear 淘汰后 (幂等)只有下层构造 key,上层通过
consume_recent_keys()读回,从根本上消除 key 一致性问题。完整生命周期:
sequenceDiagram participant NF as new_fn (上层) participant SF as src_fn participant RF as record_function (下层) participant Pool as GraphResourcePool participant MGR as AdaptiveGearRuntime participant WK as GearUpdateWorker (后台) Note over NF: variant 0: BATCHSIZE→32, SEQLEN→128 NF->>SF: src_fn(call_args) activate SF loop 每个子图 (graph break) SF->>RF: record_function(sub_inputs, func_id) RF->>RF: node = NPUGraphNode(...) RF->>Pool: key = pool.register(node) Note over Pool: _entries[key] = node<br/>_pending_by_thread[tid].append(key) Pool-->>RF: key (int, e.g. 100) end SF-->>NF: result deactivate SF NF->>Pool: keys = pool.consume_recent_keys() Pool-->>NF: [100, 101, 102] Note over Pool: _pending_by_thread[tid] 已清空 loop 每个 key NF->>MGR: record_event(..., cleanup_key=key) MGR->>MGR: state["BATCHSIZE:32"].cleanup_keys.add(key) MGR->>MGR: state["SEQLEN:128"].cleanup_keys.add(key) end Note over WK: --- 后台:gear 淘汰 (每 60s) --- WK->>MGR: commit_update(removed_gears=["BATCHSIZE:32"]) MGR->>MGR: 收集 cleanup_keys = {100, 101, 102} MGR-->>WK: removed_keys = [100, 101, 102] WK->>Pool: remove_by_keys([100, 101, 102]) loop 每个 key Pool->>Pool: resource = _entries.pop(key, None) alt resource 存在 Pool->>Pool: torch.npu.synchronize() Pool->>Pool: resource.release() / reset() else resource 不存在 (已删) Note over Pool: pop 返回 None,跳过 end end线程隔离:
register和consume_recent_keys构成 per-thread 生产者-消费者对。register在_lock下自增分配 key,追加到_pending_by_thread[tid];consume排空当前线程的 pending 列表。不同线程互不干扰,同一函数断图产生的 N 个 key 在同一线程上一次性全部消费。Key-Gear 关联:一个 variant 内产生的所有子图 key 挂到该 variant 命中的全部 gear 上。任意 gear 淘汰后该 shape 组合不再生成,所有依赖子图都应清理。收集到的 key 传给
remove_by_keys,使用_entries.pop(key, None)保证幂等——同一 key 被多个 gear 共享时,首次淘汰pop拿到资源并释放,后续pop返回None跳过。4. 时间窗口与统计规则
window_seconds),半衰期 150s,衰减系数 λ=150ln2对 gear g:
hit_rateg=∑e∈allwt(e)∑e∈gwt(e)avg_padg=∑e∈gwt(e)∑e∈gwt(e)⋅pad_ratio(e)avg_splitg=∑e∈gwt(e)∑e∈gwt(e)⋅split_ratio(e)
双维场景按维度独立统计。
5. 基础打分
单一公式:Scoreg=whHg−wpPg−wspSPg
默认权重:wh=0.60,wp=0.20,wsp=0.20。命中率越高分越高,padding/split 越高分越低。双维按两个独立一维问题处理。
6. 淘汰机制
触发:定时(距上次 ≥
update_interval_seconds,默认 300s)或显存压力(≥device_memory_usage_threshold_ratio)。多层过滤(每维度至多淘汰 1 个):
max_gear_by_type)now - max(created_ts, last_hit_ts) < recent_use_protect_seconds(默认 300s)→ 跳过len(gears) ≤ min_gear_count_per_type→ 跳过replace_loss_threshold(0.60)→ 跳过并延长保护flowchart TD A[按维度分组构建候选队列] --> B{候选为空?} B -- 是 --> Z[结束] B -- 否 --> C[按维度遍历] C --> D{该维度 gear 数 > 下限?} D -- 否 --> C D -- 是 --> E[过滤: 跳过最大 gear / 使用保护窗口过滤] E --> F{过滤后仍有候选?} F -- 否 --> C F -- 是 --> G[取最低分候选] G --> H{替代损失超过阈值?} H -- 是 --> I[延长保护窗口] I --> C H -- 否 --> J[标记 gear 为待淘汰] J --> K[继续下一个维度或结束] K --> Z提交阶段:加锁更新 current_gears,收集
cleanup_keys,删除GearRuntimeState。清理阶段:锁外pool.remove_by_keys(keys)。7. 新增机制
两类独立触发(共用同一份窗口统计):
pad_sample_count ≥ add_min_samples且avg_pad ≥ pad_add_threshold(默认 0.35)split_sample_count ≥ add_min_samples且avg_split ≥ split_add_threshold(默认 0.20),仅对最大 gear 生效候选值 =
median(raw_samples),去重后按pressure排序。显存超阈值时budget = len(removed_gears)(替换式新增),否则不限制。每个 shape_type 上限max_gears_per_type(默认 64)。flowchart TD A["遍历所有 gear 的窗口统计"] --> B{"当前 gear 是最大档位?"} B -- 是 --> C["检查 split:<br>avg_split >= theta 且样本数 >= N_min?"] B -- 是 --> D["检查 pad:<br>avg_pad >= theta 且样本数 >= N_min?"] B -- 否 --> D C -- 是 --> E["g_new = median(raw_samples)<br>候选类型: 新最大档位"] D -- 是 --> E C -- 否 --> F["不触发 split 新增"] D -- 否 --> G["不触发 pad 新增"] E --> H["加入候选集合"] H --> I["所有候选按 pressure 排序"] I --> J{"显存压力高?"} J -- 是 --> K["budget = 已淘汰 gear 数<br>(替换式新增)"] J -- 否 --> L["budget = 无限制<br>(直接新增)"] K --> M L --> M["按 pressure 顺序遍历候选"] M --> N{"budget 耗尽?"} N -- 是 --> O["结束"] N -- 否 --> P["加入 active_gears<br>budget -= 1"] P --> M8. 资源预算
device_memory_usage_ratio = 1 - free / total,通过torch.npu.mem_get_info()获取device_memory_usage_threshold_ratio(默认 0.90)时立即触发更新,新增转为替换式(addition_budget = len(removed_gears))None时不启用显存检查9. 测试覆盖
测试文件:
test/_inductor/test_shape_handling.py(63 个用例)、test/npu/test_graph_tree.py(15 个用例)。10. 实现边界限制
11. 总结
本方案引入运行时自适应齿轮调整机制:通过时间衰减窗口统计量化 gear 价值,经多层过滤实现可解释的淘汰决策,由 padding/split 信号驱动新增,在显存预算约束下控制 gear 数量。请求线程无锁读取快照,后台 daemon 线程自主触发更新,graph 清理在锁外幂等执行。