AOE 调优引擎原理与应用
前言
调GEMM算子性能,手写Tiling参数试了21种组合,最好的是tile_m=64, tile_k=64, tile_n=64,吞吐89 tokens/s。用AOE调优引擎自动搜索,搜出tile_m=128, tile_k=64, tile_n=128,吞吐102 tokens/s,比手写最优还高15%。
很多人以为AOE就是"网格搜索",其实它用贝叶斯优化+强化学习,搜索效率比网格搜索高100倍,能在10分钟内找到全局最优Tiling参数。
AOE 的定位
AOE(Ascend Optimization Engine)是CANN内置的调优引擎,自动搜索算子的最优Tiling参数、编译选项、内存布局。
CANN 架构中的 AOE:
AscendCL(编程接口层)
↓
AOL 算子库(ops-math/ops-nn/ops-blas/...)
↓
GE(图引擎)
↓
AOE 调优引擎 ← 你在这(离线调优,生成最优配置)
↓
Runtime(运行时)
↓
驱动层
AOE不是在线调优(不拖慢推理),是离线调优(提前搜好最优配置,推理时直接加载)。
工程经验: 不复用AOE手动调Tiling,要试几十种组合,耗时2-3天。用AOE自动调优,10分钟搜完,性能还更高。不是AOE多智能,是它不基于直觉搜,能找到人想不到的组合。
AOE 的核心技术
1. 搜索空间建模
AOE把Tiling参数、编译选项、内存布局统一建模成搜索空间。
Tiling参数搜索空间:
# GEMM 算子的 Tiling 搜索空间
tiling_search_space = {
# tile_m: L0A 容量约束
"tile_m": list(range(16, 256+1, 16)), # [16, 32, 48, ..., 256]
# tile_k: L0B 容量约束
"tile_k": list(range(16, 256+1, 16)),
# tile_n: L0C 容量约束
"tile_n": list(range(16, 256+1, 16)),
# 流水线策略
"pipeline": ["none", "single_buffer", "double_buffer"],
# L1 预取策略
"l1_prefetch": [True, False],
# 输出对齐策略
"output_align": [True, False],
}
# 搜索空间大小:
# len(tile_m) × len(tile_k) × len(tile_n) × len(pipeline) × len(l1_prefetch) × len(output_align)
# = 16 × 16 × 16 × 3 × 2 × 2 = 49,152 种组合
编译选项搜索空间:
# 编译选项搜索空间
compile_search_space = {
# 优化等级
"opt_level": ["O0", "O1", "O2", "O3"],
# 指令调度策略
"schedule": ["default", "balanced", "throughput", "latency"],
# 循环展开次数
"unroll_factor": [0, 2, 4, 8, 16],
# 内存布局
"layout": ["row_major", "col_major", "z_major"],
}
总搜索空间:49,152 × 240 = 11,796,480 种组合(千万级)。
2. 贝叶斯优化搜索
网格搜索要穷举千万种组合,10年都搜不完。AOE用贝叶斯优化,根据已有结果建代理模型(Gaussian Process),预测哪些组合可能最优,优先搜那些。
# AOE 贝叶斯优化(伪代码)
import numpy as np
from sklearn.gaussian_process import GaussianProcessRegressor
class AOE_BayesianOptimization:
def __init__(self, search_space):
self.search_space = search_space
self.X = [] # 已搜的配置
self.y = [] # 对应的性能(tokens/s)
self.gp = GaussianProcessRegressor() # 代理模型
def suggest_next(self):
# 训练代理模型
self.gp.fit(self.X, self.y)
# 预测所有未搜配置的期望性能
X_candidates = self._get_all_candidates()
mu, sigma = self.gp.predict(X_candidates, return_std=True)
# Upper Confidence Bound (UCB) 采集函数
# 选"期望性能高"且"不确定性大"的配置(平衡探索和利用)
ucb = mu + 1.96 * sigma
best_idx = np.argmax(ucb)
return X_candidates[best_idx]
def evaluate(self, config):
# 编译算子(用config中的Tiling参数、编译选项)
kernel = compile_kernel(config)
# 跑benchmark(测吞吐)
throughput = benchmark(kernel, iterations=100)
return throughput
def run(self, max_iter=100):
for i in range(max_iter):
# 建议下一个要搜的配置
config = self.suggest_next()
# 评估性能
throughput = self.evaluate(config)
# 记录结果
self.X.append(config)
self.y.append(throughput)
# 如果找到够好的配置,提前停止
if throughput >= target_throughput:
break
# 返回最优配置
best_idx = np.argmax(self.y)
return self.X[best_idx]
贝叶斯优化只需要搜100-200个配置,就能找到全局最优(搜索效率比网格搜索高100倍)。
工程经验: AOE的贝叶斯优化能找到人想不到的配置。比如GEMM算子,直觉是"tile_m/tile_k/tile_n都相等(64/64/64)最优",但AOE搜出tile_m=128, tile_k=64, tile_n=128,性能高15%。原因是L0A/L0B/L0C容量不是完全对称的。
3. 强化学习增强
对于超大搜索空间(比如Transformer推理要调Attention+FFN+LayerNorm的Tiling,搜索空间亿级),贝叶斯优化也不够快。AOE用强化学习(Reinforcement Learning)进一步增强。
# AOE 强化学习(伪代码)
import torch
import torch.nn as nn
import torch.optim as optim
class AOE_RL:
def __init__(self, search_space):
self.search_space = search_space
# 策略网络(输入:算子特征,输出:Tiling参数)
self.policy_net = nn.Sequential(
nn.Linear(in_features=64, out_features=128),
nn.ReLU(),
nn.Linear(in_features=128, out_features=len(search_space)),
nn.Softmax(dim=-1),
)
self.optimizer = optim.Adam(self.policy_net.parameters())
def select_action(self, op_features):
# 策略网络输出Tiling参数的概率分布
probs = self.policy_net(op_features)
# 采样一个配置
action = torch.multinomial(probs, 1)
return action
def evaluate_action(self, action):
# 编译算子
config = self._action_to_config(action)
kernel = compile_kernel(config)
# 跑benchmark
throughput = benchmark(kernel, iterations=100)
return throughput
def update_policy(self, op_features, action, reward):
# REINFORCE 算法更新策略网络
log_prob = torch.log(self.policy_net(op_features)[action])
loss = -log_prob * reward
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
def run(self, op_features, max_episode=1000):
for episode in range(max_episode):
# 选一个配置
action = self.select_action(op_features)
# 评估性能(奖励)
reward = self.evaluate_action(action)
# 更新策略网络
self.update_policy(op_features, action, reward)
# 返回最优配置
best_action = self.select_action(op_features) # 贪心
return self._action_to_config(best_action)
强化学习能在1小时内搜完亿级搜索空间(比贝叶斯优化快10倍)。
使用流程
1. 准备待调优算子
// 待调优的 GEMM 算子(Ascend C)
#include "kernel_operator.h"
class GemmKernel {
public:
__aicore__ void Process(GM_ADDR a, GM_ADDR b, GM_ADDR c,
int M, int K, int N) {
// Tiling 参数(待 AOE 调优)
int tile_m = 64; // ← AOE 会改这个
int tile_k = 64; // ← AOE 会改这个
int tile_n = 64; // ← AOE 会改这个
// ... 算子逻辑
}
};
2. 启动 AOE 调优
# 1. 准备调优配置文件
cat > aoe_config.json << EOF
{
"op_name": "GemmKernel",
"search_space": {
"tile_m": [16, 32, 64, 128, 256],
"tile_k": [16, 32, 64, 128, 256],
"tile_n": [16, 32, 64, 128, 256],
"pipeline": [0, 1, 2],
"l1_prefetch": [0, 1],
"output_align": [0, 1]
},
"optimization_objective": "throughput", // 优化目标:吞吐
"max_search_time": 600, // 搜索时间上限:10分钟
"target_throughput": 100 // 目标吞吐:100 tokens/s
}
EOF
# 2. 启动 AOE 调优
aoe_tune --config aoe_config.json \
--op-source gemm_kernel.cpp \
--output optimized_kernel.cpp
# 3. 等待调优完成(10分钟)
# 输出:
# [INFO] AOE tuning started...
# [INFO] Search space size: 49,152
# [INFO] Bayesian optimization: 100 iterations, best throughput: 95 tokens/s
# [INFO] Reinforcement learning: 500 episodes, best throughput: 102 tokens/s
# [INFO] Tuning completed. Optimized kernel saved to optimized_kernel.cpp
3. 使用调优后的算子
# 编译调优后的算子
npu-smi set -t mm -s 0 -d optimized_gemm.o optimized_kernel.cpp
# 链接成动态库
ld -shared optimized_gemm.o -o liboptimized_gemm.so
# 在 ACL 中调用(性能比调优前高 15%)
aclError ret = aclrtLaunchKernel(optimized_gemm_kernel, grid, block, args, 0, stream);
性能对比
AOE调优 vs 手动调优 vs 默认配置(Qwen2.5-7B,910B单卡,FP16):
| 策略 | 吞吐(tokens/s) | 调优时间 |
|---|---|---|
| 默认配置 | 34 | 0 |
| 手动调优(试21种组合) | 89 | 2天 |
| AOE调优(贝叶斯优化) | 95 | 10分钟 |
| AOE调优(强化学习) | 102 | 1小时 |
AOE强化学习比手动调优高15%,时间从2天降到1小时。
工程经验: AOE调优不是"万能"的。搜索空间太大(>1亿种组合),强化学习也要搜1小时。要限制搜索空间:"tile_m": [32, 64, 128](只搜常见值),别写range(16, 256+1, 16)(搜16个值)。
踩坑实录
坑1:AOE调优后算子编译失败
AOE搜出的Tiling参数导致L0A/L0B/L0C溢出,编译时报error: L0A buffer overflow。
解决:搜索空间加约束。"constraints": ["tile_m * tile_k * 2 < 64*1024", ...](保证不溢出)。
坑2:AOE调优后性能反而降
AOE在单卡上调优,多卡推理时通信开销变大,性能反而降。
解决:多卡推理要在多卡环境下调优。aoe_tune --num-devices 8(8卡环境调优)。
坑3:AOE调优时间太长(>2小时)
搜索空间太大(比如Transformer推理要调Attention+FFN+LayerNorm,搜索空间亿级),AOE搜不完。
解决:分算子调优(先调Attention,再调FFN,再调LayerNorm),每个算子搜索空间降到千万级。
坑4:AOE调优后精度掉(FP16 → INT8量化)
AOE为了性能,自动开了INT8量化,精度掉2-3%。
解决:搜索空间禁止量化。"quantization": [false](只搜FP16配置)。
https://atomgit.com/cann/amct
https://atomgit.com/cann/asc-devkit
https://atomgit.com/cann/cann-samples
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)