体系结构论文(107):AscendOptimizer: Episodic Agent for Ascend NPU Operator Optimization
摘要:本文提出AscendOptimizer系统,针对华为Ascend NPU的AscendC算子优化难题,通过双阶段交替优化方法实现性能提升。系统将算子拆分为host侧tiling program和device侧kernel program:Stage I采用进化搜索优化tiling策略,利用硬件反馈探索可行解空间;Stage II通过"优化回退"机制从优质kernel反向构
AscendOptimizer: Episodic Agent for Ascend NPU Operator Optimization
这篇文章讲的是什么
这篇文章关注的是华为 Ascend NPU 上的 AscendC operator optimization。
它不是做“从零生成一个 kernel”,而是做:
如何在极度缺少公开经验、缺少训练数据的情况下,把已有 AscendC operator 持续优化得更快。
作者提出的核心系统叫 `AscendOptimizer`。它把一个 Ascend operator 拆成两个耦合部分:
1. host 侧的 tiling program
2. device 侧的 kernel program
然后分别用两种不同策略去优化:
1. Stage I:对 host 侧 tiling 做 evolutionary-guided program search。
2. Stage II:对 kernel 侧代码做 optimization rewind + experience retrieval + guided rewriting。
最后在这两个阶段之间交替迭代。
简单说,这篇文章的核心观点是:
Ascend 上最缺的不是“一个万能模型”,而是“能自己积累优化经验的机制”。
为什么 Ascend operator 优化难
1. Ascend 和 CUDA 的差异不只是语法差异
文章在引言里借 Table 1 强调了一件事:通用甚至前沿模型在 CUDA 上还能做出像样结果,但在 AscendC 上几乎崩掉。
Table 1 里:
1. DeepSeek-R1 在 CUDA 上 Pass@1 为 52.6%,在 AscendC 上只有 1.4%。
2. Claude Sonnet 4 在 CUDA 上 47.0%,在 AscendC 上 2.1%。
3. Qwen3-235B 在 CUDA 上 44.2%,在 AscendC 上 0.7%。
这不是“差一点”,而是两个数量级的差距。
作者认为这背后不是简单的语法问题,而是知识稀缺问题:
1. Ascend 公开实现少。
2. host-kernel 协同知识难学。
3. buffer、tiling、pipeline、同步这些低层细节不在通用语料里。
2. Ascend operator 不是一段单体 kernel
AscendC operator 是一个“两段式工件”:
1. host 侧 tiling program:决定怎么分块、怎么搬数据。
2. device 侧 kernel code:决定怎么调度计算、怎么做 pipeline。
很多小白会误以为“kernel 优化”就是改 kernel 本体,但在 Ascend 上不是。性能往往由这两部分共同决定。
所以文章不是把优化问题看成“改一段代码”,而是看成:
一个 host-side tiling + device-side kernel 的联合优化问题。
3. 为什么不能只靠传统搜索
因为这个空间非常离散、非凸,而且充满隐式约束:
1. tile 稍微改一下就可能 UB 爆掉。
2. 某个数据搬运策略改一下就编译失败。
3. kernel 层面的同步、映射、pipeline 又有另一套结构性规律。
所以作者认为:
1. tiling 更像强约束、离散、脆弱的程序搜索问题。
2. kernel rewrite 更像结构化模式迁移问题。
这就是后面双阶段设计的理论基础。
一. Introduction
作者先从大模型训练和推理开讲,说现在模型越来越大,算力越来越关键,而operator 是 computation graph 的原子执行单元,它直接影响:
- 训练吞吐
- 在线推理延迟。
这段的意思很标准:
硬件峰值再高,算子写得差,实际性能也起不来。
为什么需要自动优化
作者指出,未优化算子通常吃不到硬件性能,原因包括:
- memory bandwidth wall
- 复杂的 instruction pipeline constraints
- 编译成功率低
- profiling 噪声大。
所以“算子自动优化”就成了一个桥梁问题:
一边连接上层算法,一边连接底层硬件。
为什么 GPU 上好做,Ascend 上难做
这一部分是全文最重要的逻辑铺垫。
CUDA 生态为什么能成功
作者说 NVIDIA GPU 这边已经形成了一整套自动优化技术路线:
- 早期:TVM、Ansor 这种搜索型 autotuning
- 后来:Astra、PRAGMA 这种 LLM/agent 驱动优化
- 再往后:基于 profiling、compiler feedback、硬件反馈的闭环优化。
关键原因不是“GPU 更容易”,而是:
GPU 有大量开源代码,提供了丰富的隐式优化模式。
换句话说,CUDA agent 能做起来,很大程度上是因为训练语料和先验太丰富了。
为什么迁移到 Ascend 很难
作者紧接着指出,把这套自动化方法照搬到 DSA/NPU 上会遇到严重问题,尤其是 Ascend:
- Ascend 用的是 Da Vinci architecture
- 它有显式管理的 memory hierarchy
- 不像 GPU 靠 cache 自动帮你处理很多事
- AscendC 需要开发者自己 orchestrate:
- 数据搬运
- UB 中数据布局
- 同步。
这里你可以把它理解成:
- GPU:很多细节被 runtime / compiler / cache 屏蔽了
- Ascend:很多细节要你亲自管
所以对 agent 来说,Ascend 优化更难,因为它不是纯粹“写个 kernel”就完事,而是得知道:
- UB 容量约束
- tile 怎么切
- 同步怎么放
- pipeline 怎么铺
- 搬运和计算怎么重叠
这属于更强的硬件感知软件优化。
作者说:
一个 AscendC operator 不是单体 kernel,而是一个 two-part artifact:
- host-side tiling program
- device-side kernel program
host-side tiling program 干什么
它决定:
- 输入怎么切块
- 一个 tile 多大
- 数据怎么搬到片上
- 搬几次
- 搬运和执行如何匹配
这本质上决定的是:
“where data move”
kernel program 干什么
它决定:
- 指令怎么发
- 计算怎么流水化
- SIMD / vector 指令怎么用
- 同步点怎么安排
这本质上决定的是:
“how instructions flow”
为什么单改 kernel 不够
作者明确说:
porting a kernel is insufficient: performance is co-determined by where data move and how instructions flow.
它的意思是:
- 在 GPU 上,你可能常常把优化理解成“把 kernel 写得更好”
- 但在 Ascend 上,kernel 再好,tiling 不对,也会慢
- 因为数据搬运和片上 buffer 利用本身就是性能决定因素
所以这篇文章不是单纯做 kernel rewrite,而是在做:
host tiling + device kernel 的联合优化
Table 1 给了一个非常关键的数据:
同样是 SOTA 大模型,CUDA kernel 的 one-shot generation pass rate 还能到 44%~52%,而 AscendC 只有 0.7%~2.1%。
这张表的意义
作者用这张表不是为了说“模型太弱”,而是为了证明:
Ascend 上的问题不是单纯语法问题,而是知识稀缺问题。
也就是说,大模型生成 AscendC 代码失败,不是因为不会写 C++ 风格代码,而是因为它不懂:
- tiling 约束
- pipeline orchestration
- buffer overflow 风险
- API 约束
- Ascend 特有执行模式。
所以这篇文章的方向不是继续赌“更强大模型一次写对”,而是转向:
通过反馈闭环 + 自举经验库,让优化过程可持续逼近正确解。
核心 insight
作者在这一段给出了全篇最核心的 insight:
when external data are insufficient, we can bootstrap experience internally by exploiting the structured nature of code.
翻成更直白的话就是:
既然外部没有足够的专家数据,那就从代码本身的结构性出发,自举出优化经验。
它后面分两部分讲:
对 tiling 来说
- tiling 空间不连续
- 稍微一改可能从“快”变成“编不过”
- 但这种脆弱性反过来也是优势
因为真实硬件反馈会直接告诉你: - 可行 / 不可行
- 快 / 慢。
所以 tiling 适合用硬件反馈驱动的搜索。
对 kernel 来说
- kernel 优化模式比较结构化
- 比如 pipelining、vectorization、latency hiding 这些是可迁移的
- 虽然没有现成 bad-to-good 数据
- 但你可以从好代码出发,故意 rewinding,自己造出 bad-to-good 轨迹。
所以 kernel 适合用经验抽取 + 检索增强重写。

Table 2 是一张定位自己方法贡献边界的表。
比较维度有三个:
- Optimizes Existing Impl.
- Automatic Optimization
- Training-free
几类方法对应关系大概是:
- ASPLOS’25 / Hermes:
- 能优化已有实现
- 但不是自动化 end-to-end
- 更偏专家主导分析优化
- AscendKernelGen:
- 自动生成
- 但不属于 training-free
- 更偏从零生成而不是优化现有实现
- AscendOptimizer:
- 优化已有实现
- 自动化
- training-free。
作者想占据的生态位很明确:
不是专家调优系统,不是重训练模型的方法,也不是只做从零生成;而是一个“不依赖额外训练”的现有 AscendC 算子自动优化系统。
二、方法

把 `AscendOptimizer` 分成两个阶段:
1. Stage I:Evolutionary-Guided Program Search
2. Stage II:Optimization-Rewind based Experience Bootstrapping
然后用 alternating optimization 的方式在两者之间切换。
作者的核心思想很聪明:
如果外部数据不够,那就从执行反馈和已有强实现里“自己长经验”。
具体来说:
1. 对 tiling,不依赖规则库,直接让硬件反馈告诉你什么可行、什么更快。
2. 对 kernel,不是盲目搜索,而是从少量好的 seed kernel 出发,故意把它们“去优化”,从而构造 bad-to-good 轨迹,再把这些优化经验存进可检索经验库。
这个想法比“再堆一点 prompt engineering”或者“直接全靠 RL 搜索”更有结构感。
Figure 1 里 Stage I 和 Stage II 并不是两个彼此独立的方法,而是对同一个目标的两种互补求解。
作者在文字里把它正式化成:
给定 operator `O = {T, K, S}`,
其中:
1. `T` 是 host 侧 tiling function
2. `K` 是 AI Core 上的 kernel code
3. `S` 是静态属性,如 shape、dtype、layout
优化目标是在真实硬件上最小化执行延迟。
Figure 1 真正想表达的是:
1. Stage I 通过硬件反馈去摸清可行 tiling 区域。
2. Stage II 通过经验库和语义改写去突破 kernel 结构瓶颈。
3. 两者交替迭代,因为更好的 tiling 会改变 kernel 可发挥空间,更好的 kernel 也会改变 tiling 的收益结构。
这就是典型的 alternating optimization 思路。
Stage I:Evolutionary-Guided Program Search
作者认为 tiling 优化的特点是:
1. 高度依赖 shape 和 layout。
2. 解空间不连续。
3. 很难抽象成通用规则。
所以他们不试图先手写规则库,而是把它建模成 program search。
具体来说:
1. 先构造一个 base tiling function 模板。
2. 在模板里放 evolution markers。
3. 让 LLM 基于这些可变位置做变异。
4. 再用真实 NPU 的 compile / execute / correctness 反馈作为 fitness。
这里的核心不是“用了 evolutionary search”,而是:
作者把硬件反馈本身当作边界探测器。
因为在 Ascend 上,可行域很脆:
1. 编译不过就说明违反约束。
2. 精度不对就说明语义错。
3. 延迟变好才说明真的有效。
这使得 Stage I 更像一种“在硬件围栏内的程序进化”。
Stage II:Optimization Rewind
Stage II 的想法非常值得单独讲。
作者观察到:
1. Ascend 上缺乏成体系的“差代码 -> 好代码”配对数据。
2. 但如果你手头有一小批不错的 kernel,其实可以反向构造这种数据。
怎么构造?
就是所谓 `optimization rewind`:
1. 从一个优化过的 expert kernel 出发。
2. 故意把其中某些优化去掉,也就是“去优化”。
3. 于是你就能得到 slow version 和 fast version 的差别。
4. 再总结出这次优化的 motif / strategy。
这本质上是在自己制造“bad-to-good trajectory”。
这个想法很聪明,因为它绕开了一个现实障碍:
没有人会系统保存“所有糟糕版本是怎么一步步被优化好的”,但你可以从好版本往回拆。
作者进一步把这些轨迹蒸馏成可检索的 experience bank,用于在线优化时的 retrieval-augmented rewriting。
这使得 Stage II 不是纯搜索,而是“带经验回放的结构化改写”。

Algorithm 1 的本质,是一个面向 Ascend 算子的交替优化框架:外层循环逐轮继承当前最优解,内层先在固定 kernel 下搜索更优 tiling,再在固定 tiling 下重写更优 kernel,并通过真实硬件上的编译、正确性和延迟反馈来筛选候选,最终得到联合优化后的最优实现。

Figure 3 可视化了经验库里的优化策略语义聚类。
作者想说明两件事:
1. 有些 cluster 对应官方文档里的典型 best practice,例如 tiling、double buffering。
2. 还有一些 cluster 并不能直接映射到文档显式分类,比如更细粒度的同步、向量化非有限值检查、去高延迟标量指令等。
这张图的意义是:
经验库不只是把官方最佳实践重新抄了一遍,而是从实际 benchmark operator 里挖掘出一些“文档没系统整理、但真实反复出现”的模式。
这也是论文相对有说服力的地方。如果经验库只是文档摘录,那创新性会很弱;而 Figure 3 试图证明它确实挖到了额外结构。
Alternating Optimization

Figure 4 以 `foreach pow scalar and tensor` 为例展示优化轨迹。
这里作者每 10 次迭代切换一次 Stage I / Stage II。
图里看到的现象很有代表性:
1. Stage I 很快给出约 1.09x 提升。
2. 但之后会 plateau。
3. Stage II 通过经验检索和语义重写打破 plateau。
4. 在某个关键改写后速度直接跳到 2.31x。
这说明:
1. 仅靠 tiling/execution tuning 能吃掉一部分剩余空间。
2. 但真正大的突破往往来自 kernel 结构改写。

Figure 5 把其中一个关键 rewrite 展开:从 remainder-based per-core quota assignment 改成 block-level load balancing + nested scan across tensors。
简单理解就是:
原方法按余数分配任务,可能造成核间负载不均;
新方法按 block 更均匀地分配,并且做跨 tensor 的嵌套扫描,从而减少尾部不平衡,提高利用率。
这属于很典型、很硬核的性能优化逻辑。
三、实验
作者从 `cann-ops` 官方 AscendC operator 仓库里出发,筛出了 127 个 operator 作为最终测试集。
这里还有一个很重要的说明:
Stage II 构造经验库用的 seed kernels 就来自同一个 127-operator benchmark。
作者专门解释说,这不是传统机器学习意义下的 train/test leakage,因为他们不是在追求泛化评测,而是在做 training-free、episodic、transductive optimization。
这个解释在系统优化语境里是成立的。
换句话说,这篇文章不是在回答“你能不能泛化到一个完全没见过的 Ascend operator”,而是在回答“面对当前要优化的这批 operator,你能不能把它们调快”。
这两种问题不能混淆。

Table 3 报告了在 level1 / level2 / level3 上,BoN@5、BoN@40、OpenEvolve 和 AscendOptimizer 的表现。
指标包括:
1. GM:几何平均 speedup
2. `fast1.0`
3. `fast1.2`
4. `fast1.4`
5. `fast2.0`
先看总体趋势:
1. BoN 增加采样预算,从 5 到 40,只带来有限收益。
2. OpenEvolve 比 BoN 更强,说明迭代优化比单纯多采样更有效。
3. AscendOptimizer 在三层上都是最优。
具体看数值:
1. Level 1:GM 1.08,`fast1.0` 46.51%。
2. Level 2:GM 1.21,`fast1.0` 49.35%,`fast1.2` 18.18%。
3. Level 3:GM 1.81,`fast1.0` 71.43%,`fast2.0` 28.57%。
这里最值得注意的是:
1. Level 2 提升很扎实,说明方法对中等复杂 operator 很有效。
2. Level 3 样本只有 7 个,所以虽然结果亮眼,但统计稳定性有限,不能过度夸大。
总体来说,Table 3 能支持一个比较强的结论:
AscendOptimizer 相比纯采样和通用迭代优化框架,在 Ascend 场景下确实更有效。

Figure 2 给的是 speedup 的 CDF。
这张图比单纯报 GM 更有意义,因为它说明:
1. 不只是个别 operator 被拉得特别高;
2. 而是有相当比例的 operator 获得了稳定中等提升;
3. 同时右侧长尾又表明少数 operator 能获得非常大收益。
作者给出:
1. 39.7% 的 operator 至少 1.1x
2. 30.2% 至少 1.2x
3. 19.0% 至少 1.5x
4. 14.3% 至少 2.0x
这张图增强了结果可信度,因为它显示收益不是完全由极端个例撑起来的。

Table 4 对比:
1. 只有 Stage I
2. 只有 Stage II
3. 完整 AscendOptimizer
结果很有代表性:
1. Stage I:GM 1.09,`fast1.0` 38.58%,说明它善于保底、扩大可行解覆盖面。
2. Stage II:GM 1.12,`fast1.2` 和 `fast1.4` 更强,说明它更擅长做中高强度结构优化。
3. 完整系统:GM 1.19,`fast1.0` 和 `fast2.0` 最好。
这和作者的理论叙事是吻合的:
1. Stage I 更像参数/调度层面的外圈搜索。
2. Stage II 更像结构层面的内核重写。
3. 两者结合才能同时拉高平均水平和高端收益。
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐


所有评论(0)