想象你是一家米其林餐厅新来的主厨,老板丢给你一个任务:研发一道招牌菜。传统师傅的做法是翻出祖传的菜谱模板,在“红烧”或“清蒸”这些固定框架里微调调料;而你不打算被旧模板束缚——你决定把研发拆成三步:先定这道菜的“骨架”(选清蒸还是爆炒,配什么食材次序),再填“参数”(切多大块、多大火、放多少盐),最后反复“试菜”改良(随机试一批配方,请评委打分,让高分配方互相杂交出更好的下一代)。这套流程,就是 AI 编译器里 Ansor 做的事。 深度学习模型的每个算子(卷积、矩阵乘法)本质上都是多层循环。调度(schedule)就是决定这些循环怎么切分、怎么并行、怎么向量化——相当于决定“菜怎么做”。可调度的组合空间有万亿种,暴力枚举不可能。Ansor 提出“三级调度”思想:草图(sketch)→ 注释(annotation)→ 随机采样与进化搜索(random + evolutionary search),把庞大的搜索空间一步步结构化,让编译器像大厨一样自动找到最优做法。 这篇内容将带你完整拆解 Ansor 的三级调度流水线、成本模型、与 AutoTVM 的对比,以及实际调优效果。读完你会理解:为什么“自动生成骨架 + 智能搜索”能取代“专家手写模板”,成为深度学习编译器的标配。

岗位关联

一句话价值:理解 Ansor 三级调度如何把天文数字的搜索空间压缩成可搜索的结构,是构建与使用 AI 编译自动调优工具链的底层认知,也是 AI 编译器工程师的必修课。

目录
  1. 可视化演示
  2. 三级调度概述
  3. 草图生成
  4. 注释填充
  5. 随机采样
  6. 进化搜索
  7. 成本模型
  8. AutoTVM对比
  9. 效果与实战
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示Q-Learning算法在5x5网格世界中的学习过程,智能体通过探索和利用策略逐步学习从起点到终点的最优路径。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Q-Learning 探索学习 智能体在网格世界中通过ε-贪心策略探索,逐步更新Q值表 Q←Q+α[r+γmaxQ(s_next)-Q] 绿色为起点,金色为目标,红色为障碍

三级调度概述

研发新菜最怕什么?怕选择太多。切法、火候、调味、装盘,每一样都有几十种选择,组合起来是天文数字。传统大厨的做法是“凭经验限死选择”,而 Ansor 这位智能大厨反其道而行:它先定骨架缩小范围,再用参数展开覆盖面,最后用试菜精选最优——用三级流水线把“盲目试错”变成“结构化搜索”。

在深度学习编译器中,每个算子对应一段多层循环。所谓调度,就是决定循环被怎样变换:外层循环如何平铺(tiling)、内层如何向量化(vectorization)、哪些循环可以并行(parallel),以及语句以什么顺序执行。这些决策的任意组合构成了“调度空间”。问题是:调度空间的大小是各个维度自由度的乘积,常常超过 \(10^{30}\),直接把 100 台机器跑一年都搜不完。

Ansor 的关键洞察是:绝大多数优秀调度共享少量“高层次的程序结构”。于是它把调度空间推导拆成三级——第一级 sketch 只决定循环骨架,把空间压到几十个;第二级 annotation 为骨架填充平铺尺寸、并行/向量化标志,把空间展开到百万级;第三级 random 采样加进化搜索,在真实硬件反馈下快速逼近最优。三级合起来,等于把“大海捞针”变成“在精心设计的水池里摸针”。

这就像大厨的研发流水线:先定“清蒸还是爆炒”(骨架),再填“每味料放多少”(参数),最后反复试菜改良(搜索)。三级各司其职、环环相扣,Ansor 才能在学习一个算子时,快、准、省地找到好调度。

∣S∣=∑s∈G∣Ann(s)∣\displaystyle |\mathcal{S}| = \sum_{s \in \mathcal{G}} |\text{Ann}(s)|
这个公式说明:Ansor 的完整调度空间,等于所有草图(sketch)各自能衍生出的注释(annotation)数量之和。草图把空间切成了少数几块,每一块内部再展开参数。
  1. 调度空间符号等号左边的 \(|\mathcal{S}|\) 表示所有完整调度组成的集合的元素个数,它衡量搜索空间的总大小。
    ∣S∣|\mathcal{S}|
  2. 对草图求和\(\mathcal{G}\) 是所有程序草图的集合,求和符号表示把每条草图对应的候选调度数量全部加起来。
    ∑s∈G\sum_{s \in \mathcal{G}}
  3. 注释集合大小\(\text{Ann}(s)\) 是草图 \(s\) 上所有合法注释的集合,\(|\text{Ann}(s)|\) 就是这条草图能衍生出的调度数量。
    ∣Ann(s)∣|\text{Ann}(s)|

草图生成

如果大厨接到一道完全陌生的菜(比如分子料理),他不会直接跳进调料堆里瞎试,而是先想:这道菜的整体流程是什么?先怎么处理主料、中间经过哪些工序、最后怎么装盘?在 Ansor 里,这个“整体流程”就是草图(sketch):一段多层循环的骨架,规定了循环嵌套结构、计算拆分方式,但所有的尺寸和并行标志都留空待填。

草图不是人写的,而是 Ansor 内置的一组“草图规则”(sketch rules)自动生成的。每条规则都来自资深编译器工程师对“什么样的调度结构容易跑得快”的总结,例如:多层平铺规则(multi-level tiling)把一个大循环拆成“线程块—线程—向量”几个层次;约简轴拆分规则(split reduction axis)把累加循环拆开做并行;还有数据布局变换规则。

以矩阵乘法 C = A × B、维度 (M, N, K) 为例,一条草图规则会把计算写成六层循环 (Mo, No, Ko, Mi, Ni, Ki):外三层控制线程块,内三层控制单线程内的计算。注意,这里的尺寸 Mo、Mi 等都是“变量”,还没有具体数值。一个算子的表达式经过全部草图规则推导,通常只产生几条到几十条草图——相比万亿级的完整调度,这是几个数量级的压缩。

就像菜谱骨架决定了一道菜是“清蒸还是一鱼两吃”,草图决定了算子的“循环格局”。骨架选得对,后面怎么填都差不到哪去;骨架选得全,优秀调度就不会被排除在搜索空间外。

ϕ:E→G,∣G∣≪∣S∣\displaystyle \phi: \mathcal{E} \rightarrow \mathcal{G}, \quad |\mathcal{G}| \ll |\mathcal{S}|
草图生成规则把“计算表达式”映射成“少量草图集合”,草图数量远小于完整调度空间,这是第一级压缩。
  1. 规则映射\(\phi\) 表示草图生成规则,它把计算表达式集合 \(\mathcal{E}\) 中的每个算子映射到一个草图集合。
    ϕ:E→G\phi: \mathcal{E} \rightarrow \mathcal{G}
  2. 草图集合\(\mathcal{G}\) 是生成的草图集合,数量很少,通常只有几条到几十条。
    ∣G∣|\mathcal{G}|
  3. 规模对比符号 \(\ll\) 读作“远小于”,说明草图空间只是完整调度空间的极小一部分,这正是压缩的威力。
    ∣G∣≪∣S∣|\mathcal{G}| \ll |\mathcal{S}|

注释填充

骨架定了,接下来就是“填参数”:清蒸鲈鱼要切几刀、蒸几分钟、淋多少豉油?Ansor 的注释(annotation)阶段做的正是这件事——为草图里的每个空白填上具体数值和标志:每层循环的平铺尺寸、是否对某层做并行、内层循环是否向量化、是否展开。

注释不是随便填的,必须满足“合法性”:平铺尺寸的乘积必须等于原循环长度;并行层数受硬件线程数约束;向量化宽度要匹配 SIMD 位宽。Ansor 会枚举某个草图上的全部合法注释,把每条注释“贴”到草图上,就得到一个完整可编译的调度。

一个草图能衍生出多少注释?以一维长度为 N 的循环、切分成 k 层平铺为例,答案等于 N 的所有“因子拆分方式”之和。这正是组合数学里的递推数:第一层可以从 N 的任一个因子 d 中选,剩下的 N/d 再递归地交给 k-1 层去拆。

注释就是“配料表”的每一格:格子越多,候选配方就越多,但同时也越能覆盖不同硬件口味。填完注释,搜索空间正式成型——这是第二级调度空间的全部内容。

fk(N)=∑d∣Nfk−1 ⁣(Nd),f1(N)=1\displaystyle f_k(N) = \sum_{d \mid N} f_{k-1}\!\left(\frac{N}{d}\right), \quad f_1(N) = 1
这个递推公式计算“把长度为 N 的循环切成 k 层”有多少种合法切法:先选第一个因子 d,再递归切剩下的部分。
  1. 一层平铺当只切 1 层时,唯一合法的切法就是“不切”,所以 \(f_1(N)=1\)。
    f1(N)=1f_1(N) = 1
  2. 拆出最外层\(d\) 是 \(N\) 的一个因子,记号 \(d \mid N\) 表示 \(d\) 整除 \(N\),对应切出的第一层尺寸。
    ∑d∣N\sum_{d \mid N}
  3. 递归剩余部分第一层取走 \(d\) 后,剩余长度 \(N/d\) 交给 \(k-1\) 层继续切分,于是得到递推关系 \(f_k(N) = \sum_{d \mid N} f_{k-1}(N/d)\)。
    fk−1 ⁣(Nd)f_{k-1}\!\left(\frac{N}{d}\right)

随机采样

注释空间的候选调度可能有上百万个,大厨不可能把每一道都真做一遍来试味。他的策略是先“闷头随机试一批”:从所有配方里均匀抓 50 个,真刀真枪地做出来,既尝到了味道分布,也为后续的“评委”积累了经验。

Ansor 在第三级的第一步就是随机采样(random sampling):对每个草图,从它的合法注释集合中均匀随机抽取若干条,得到一组完整调度。这组调度被编译并在真实硬件上各跑一次,记下实际执行时间——它们既是“初代种群”,又是训练成本模型的“测量数据”。

采样讲究均匀与覆盖:Ansor 将每条注释看成等概率事件,确保搜索空间的不同区域都能被探到。典型做法是每个草图采样几十到几百个点,总采样量在一千到几千的量级——相比全空间,这只是沧海一粟,但足以勾勒出“味道分布”。

随机试菜看似盲目,却是有价值的起点:它用很低的成本换来两样东西——对空间的初步感知(哪些区域耗时低),以及训练评委(成本模型)的第一批样本。没有这一步,后面的进化就无从谈起。

P(a∣s)=1∣Ann(s)∣,a∈Ann(s)\displaystyle P(a \mid s) = \frac{1}{|\text{Ann}(s)|}, \quad a \in \text{Ann}(s)
在随机采样时,草图 s 上的每一条合法注释 a 被抽中的概率相等,都是注释总数的倒数,保证均匀覆盖。
  1. 条件概率\(P(a \mid s)\) 表示在给定草图 \(s\) 的条件下,选中注释 \(a\) 的概率。
    P(a∣s)P(a \mid s)
  2. 均匀取等随机采样时每个合法注释机会均等,所以概率是 1 除以注释总数。
    1∣Ann(s)∣\frac{1}{|\text{Ann}(s)|}
  3. 注释集合\(a \in \text{Ann}(s)\) 指明注释 \(a\) 必须是该草图上的合法注释。
    a∈Ann(s)a \in \text{Ann}(s)

进化搜索

第一轮试菜后,大厨发现“酸甜口”的评分普遍高。接下来他不会把剩余一万个配方都做一遍,而是玩起“优生优育”:让高分配方杂交产生后代,偶尔让某个后代随机变异一味料,再用评委快速打分,把低分后代淘汰。循环十几代,种群整体水平一代比一代高。

Ansor 的进化搜索(evolutionary search)正是如此。它以随机采样得到的种群为起点,反复执行:用成本模型给种群中的每个程序打预测分,保留分数最高的前一半作为“父本”;然后对父本做变异(mutation)与交叉(crossover)生成新的完整调度,补充回种群。

变异算子很直接:把某个平铺尺寸改成它的因子、对换两层循环、把某层循环从 parallel 改成串行或加上 vectorize。交叉则把两个优秀程序的不同部分拼接成新程序。每个程序按适应度(fitness)排序,适应度定义为预测耗时的倒数。经过上百轮迭代,种群会向成本模型认为的最优区域快速收敛。

进化搜索的精髓是“用模型代替品尝”:真实试菜(硬件实测)很贵,只有极少数候选值得真做;而评委打分的成本极低,可以一轮筛掉几万个后代。大厨的改良速度因此快了几十倍。

fitness(p)=1t^(p)+ϵ\displaystyle \text{fitness}(p) = \frac{1}{\hat{t}(p) + \epsilon}
适应度是进化搜索的“打分标准”:程序 p 的预测耗时越短,适应度越高,越有机会被保留和繁殖。
  1. 适应度函数\(\text{fitness}(p)\) 衡量程序 \(p\) 的“优秀程度”,值越大越值得保留。
    fitness(p)\text{fitness}(p)
  2. 预测耗时\(\hat{t}(p)\) 是成本模型给出的预测执行时间,耗时越短适应度越高。
    t^(p)\hat{t}(p)
  3. 平滑项\(\epsilon\) 是一个很小的正数,防止分母为零导致除零错误。
    ϵ\epsilon

成本模型

凭什么评委不用尝就能打分?因为他吃过几百道菜,心里有一套“菜谱特征→好吃程度”的经验:比如糖色深的往往更香、火候太猛容易糊。成本模型(cost model)就是 Ansor 的评委——它从历史实测数据中学习“程序特征→运行时间”的映射,专门用来预测没跑过的调度。

Ansor 的成本模型采用梯度提升回归树(GBDT)。输入是程序提取出的特征向量 \(f(p)\):各层循环的迭代次数、平铺尺寸的取值、访存次数、并行/向量化标志、缓存复用情况等;输出是预测执行时间 \(\hat{t}(p)\)。特征不需要人工精调,GBDT 会自动找到关键特征与非线性关系。

模型质量直接决定进化搜索的方向准不准。Ansor 的做法是“边搜边学”:随机采样阶段实测的数据喂给模型;进化搜索每跑一段时间,再挑几个候选真实测量,把新数据加进训练集,让评委越用越准。

评委越老辣,大厨的改良路线越靠谱。成本模型与进化搜索形成正反馈:搜索产生数据,数据训练模型,模型指导搜索——这正是 Ansor 能高效收敛的引擎。

t^(p)=∑m=1Mη⋅hm(f(p))\displaystyle \hat{t}(p) = \sum_{m=1}^{M} \eta \cdot h_m(f(p))
成本模型把多个回归树的预测加权求和:每棵树从不同角度“品尝”程序特征,最终给出一个综合耗时预测。
  1. 预测值\(\hat{t}(p)\) 是成本模型对程序 \(p\) 运行时间的预测,带帽子的符号表示“估计值”。
    t^(p)\hat{t}(p)
  2. 特征提取\(f(p)\) 把程序结构转换成数值特征向量(循环层数、平铺尺寸、访存次数等)。
    f(p)f(p)
  3. 集成回归树\(h_m\) 是第 \(m\) 棵回归树的输出,\(\eta\) 是学习率,总预测是 \(M\) 棵树输出的加权和。
    ∑m=1Mη⋅hm(f(p))\sum_{m=1}^{M} \eta \cdot h_m(f(p))

AutoTVM对比

在 Ansor 之前,TVM 已经有一位“老师傅”——AutoTVM。老师傅的做法是:每来一道新菜,都要先翻出他手写的几套模具(模板),新菜只能在模具里微调盐量。如果这道菜本身不在模具覆盖范围内,调得再久也没用。更麻烦的是,模具必须由人手工打造,每道新菜都要重新打模具。

AutoTVM 是 TVM 最早期的自动调优方案。工程师需要为每个算子手写调度模板,模板里留出可调参数(比如平铺尺寸有哪些候选),然后用模拟退火等算法在模板参数空间里搜索。它的优点是简单可靠;缺点是搜索空间完全被模板结构锁死,模板之外的好调度永远不会被发现,而且模板编写非常依赖专家经验。

Ansor 与 AutoTVM 的本质区别在于搜索空间如何构造。AutoTVM 的空间来自“手写模板井”,Ansor 的空间来自“自动草图展开”。论文实验显示,Ansor 推导出的调度空间比 AutoTVM 模板空间大数倍到数十倍,空间不仅更大,而且覆盖了更多样的循环结构。两代系统的搜索策略也不同:AutoTVM 用贝叶斯优化/模拟退火,Ansor 用成本模型引导的进化搜索。

用模具做点心 vs 自动捏造型再试口味:前者稳定但天花板低,后者前期工程量大但泛化能力强。Ansor 的胜出,本质是“从经验驱动到数据驱动”的范式跃迁。

∣Sansor∣∣Stemplate∣≫1\displaystyle \frac{|\mathcal{S}_{\text{ansor}}|}{|\mathcal{S}_{\text{template}}|} \gg 1
Ansor 自动推导的调度空间规模远大于 AutoTVM 手写模板能表达的空间,这是两者最本质的差距。
  1. Ansor 空间\(|\mathcal{S}_{\text{ansor}}|\) 是 Ansor 自动推导出的调度空间大小,包含所有草图展开的合法注释。
    ∣Sansor∣|\mathcal{S}_{\text{ansor}}|
  2. 模板空间\(|\mathcal{S}_{\text{template}}|\) 是 AutoTVM 手写模板能表达的调度空间大小,受模板结构限制。
    ∣Stemplate∣|\mathcal{S}_{\text{template}}|
  3. 数量级差距比值远大于 1,说明自动推导的搜索空间比模板空间大若干个数量级。
    ∣Sansor∣∣Stemplate∣≫1\frac{|\mathcal{S}_{\text{ansor}}|}{|\mathcal{S}_{\text{template}}|} \gg 1

效果与实战

新大厨终于上菜了。同样的食材,出菜更快、味道更稳,而且全程不需要老师傅在一旁指点。Ansor 交出的成绩单:在 NVIDIA GPU 上,端到端模型推理比 TVM 默认实现快约 3.8 倍,比 AutoTVM 调优结果再快 2.6 倍;在 ARM CPU、树莓派等平台上也有 2~10 倍的提升。

这些数字来自 Ansor 论文的实验:覆盖 ResNet、MobileNet、BERT 等多种模型,以及卷积、矩阵乘法、注意力等常见算子。加速比定义为调优前的默认耗时除以 Ansor 搜索后的耗时。默认耗时是 TVM 的默认调度,Ansor 耗时是自动搜索出的最优调度实测时间。

实际落地也很简单。在 TVM 生态中,通过 Relay 的 auto_scheduler(Ansor)接口,只需两步:先 “tune“——给定目标硬件和每算子的时间预算,Ansor 自动执行三级调度搜索;再 ”apply”——把搜索到的最优调度打包进编译产物。工程师不需要手写任何模板,剩下的交给流水线。

从“老师傅手工调参”到“新大厨自动流水线”,Ansor 把算子优化的门槛从“编译器专家”降到了“调用一个接口”。三级调度思想后来也被 MetaSchedule 等新一代调度器继承,成为 AI 编译自动调优的事实标准。

speedup=TdefaultTansor\displaystyle \text{speedup} = \frac{T_{\text{default}}}{T_{\text{ansor}}}
加速比就是“默认耗时除以 Ansor 调优后耗时”:比值大于 1 表示调优带来了实实在在的提速。
  1. 默认耗时\(T_{\text{default}}\) 是无调优(或默认 schedule)下算子的执行时间。
    TdefaultT_{\text{default}}
  2. 调优后耗时\(T_{\text{ansor}}\) 是 Ansor 找到的最优调度的执行时间。
    TansorT_{\text{ansor}}
  3. 加速比两者之比就是加速倍数,大于 1 表示调优带来提升。
    TdefaultTansor\frac{T_{\text{default}}}{T_{\text{ansor}}}

AI 应用场景

自研 NPU 的算子库自动生成

阶段:后端代码生成与算子调优

解决的问题:芯片公司为 NPU 开发算子库时,传统方法是工程师手写汇编或手调类 CUDA 的 kernel,一个算子动辄数周,且只能覆盖常用形状。Ansor 的三级调度自动推导搜索空间,用成本模型引导进化搜索,几天内就能产出接近手写性能的算子实现。

具体案例:某 AI 芯片团队在 TVM 中接入 Ansor,对 40 余个算子(conv、matmul、softmax、layernorm)自动调优,单算子调优时间控制在 5~20 分钟,最终性能达到手写算子的 90% 以上,人力成本大幅下降。

端侧模型的高效部署

阶段:推理优化与部署

解决的问题:手机端 NPU/CPU 与服务器 GPU 的硬件特性差异巨大,同一算子在两处的最优调度完全不同。手工为每一款端侧芯片调优不现实,Ansor 针对目标硬件自动执行三级调度,一键获得适配调度。

具体案例:某视觉模型团队要在多款 Android 手机上部署 YOLOv5,使用 Ansor 分别针对高通和联发科平台自动调优,端到端推理速度相比 TVM 默认提升 1.8~3.2 倍,全程未写一行调度代码。

大模型 Attention 算子优化

阶段:计算图与算子协同优化

解决的问题:Transformer 的 attention、MoE 等新算子迭代快、没有成熟模板。工程师用 Ansor 自动探索类似 flash-attention 的调度结构变体,在保持语义不变的前提下显著提升访存效率。

具体案例:某训练框架团队优化自研 LLM 的 attention 核,用 Ansor 在 A100 上自动搜索,得到比基线手写 kernel 快 1.4 倍的调度,且自动适配不同 head 维度。

Python 演示

python67 行
# -*- coding: utf-8 -*-"""Ansor three-stage scheduling simplified demo."""import randomfrom math import log  def fake_cost_model(scheme, N=1024):    """Simulate hardware latency, regular tilings win."""    tile = scheme['tile']    cost = 0.0    for t in tile:        cost += log(t + 2)    if scheme['parallel']:        cost -= 0.2    if scheme['vectorize']:        cost -= 0.2    if max(tile) <= N // 4:        cost -= 0.3    return round(cost + 1.0, 4)  def enumerate_annotations(N):    """Stage 2: enumerate all legal annotations."""    anns = []    for t1 in range(1, N + 1):        if N % t1 != 0: continue        anns.append({'tile': [t1], 'parallel': False, 'vectorize': False})        for t2 in range(1, N // t1 + 1):            if (N // t1) % t2 != 0: continue            anns.append({'tile': [t1, t2], 'parallel': True, 'vectorize': True})    return anns  def sketch_generation():    """Stage 1: derive a few sketches (only one here)."""    return [{'name': 'sketch_matmul', 'loop_dims': [1024]}]  def random_sampling(anns, num=50):    rng = random.Random(7)    return rng.sample(anns, min(num, len(anns)))  def evolve(pop, anns, generations=12):    rng = random.Random(11)    for _ in range(generations):        pop = sorted(pop, key=fake_cost_model)        keep = pop[:len(pop) // 2]        pop = keep + rng.sample(anns, len(pop) - len(keep))    return pop[0]  def main():    N = 1024    sketches = sketch_generation()    anns = enumerate_annotations(N)    print('Stage1 sketch count =', len(sketches))    print('Stage2 annotation count =', len(anns))    init = random_sampling(anns, 60)    print('Stage3 random sample =', len(init), 'candidates')    best = evolve(init, anns)    print('Best schedule:', best)    print('Predicted cost:', fake_cost_model(best))  if __name__ == '__main__':    main()
目的:用一段简化代码复现 Ansor 三级调度——先生成草图,再枚举全部合法注释,随机采样构建初始种群,最后用成本模型引导进化搜索选出最优调度。
  1. def enumerate_annotations(N):第二级注释填充:把一维 N 拆成 1~2 层平铺并枚举合法尺寸组合。
  2. if N % t1 != 0: continue平铺尺寸必须整除 N,不合法的注释直接跳过。
  3. anns.append({'tile': [t1, t2], 'parallel': True, 'vectorize': True})记下一个合法注释:双层平铺且开启并行与向量化。
  4. def sketch_generation():第一级草图生成:这里简化为只有一种骨架。
  5. def random_sampling(anns, num=50):第三级第一步:均匀随机采样出初始种群。
  6. pop = sorted(pop, key=fake_cost_model)用成本模型预测值给种群排序,耗时短的排前面。
  7. keep = pop[:len(pop) // 2]保留预测最优的前一半作为“父本”。
  8. best = evolve(init, anns)执行多代进化搜索,返回最终最优调度。

常见误区

误区有初学者以为 sketch、annotation、random 是三个独立的工具,可以只用一个,甚至跳过第一级直接随机搜。

正解三级是环环相扣的流水线:没有 sketch,annotation 就没有骨架可填;没有 annotation,随机采样就没有合法空间可采。就像做菜不能跳过备菜直接下锅——框架决定上限,参数决定可行性。跳过任何一级,搜索要么空间太小错过最优,要么空间太大无法收敛。

为何常见:因为论文里分了三章讲,读者容易当成三个并列功能,忽略了它们是同一流水线上的先后工序。

误区有同学对比 AutoTVM 后觉得:Ansor 的 sketch 不就是 AutoTVM 的 template 换个名字?还是专家手工设计。

正解完全不同。AutoTVM 模板由工程师为每个算子手写,且模板内的循环结构写死;Ansor 的草图由一组通用规则自动生成,覆盖各种形状,无需人工参与。模板是“模具”,草图的“骨架”——前者是死的,后者是自动推导的。模具只能做出模具允许的形状,骨架却能展开出远超模具的调度空间。

为何常见:因为两者都是“调度空间的结构性约束”,从表面形式看很像,容易混淆本质区别。

误区有的同学认为第三级就是随机撒几千个点然后挑个最好的,跟抛硬币差不多,毫无技术含量。

正解随机采样只是起点,用来获得初始种群和训练数据。真正的主力是成本模型引导的进化搜索:变异、交叉、按预测分数保留最优。随机撒点 + 进化改良 + 模型打分三件套配合,才能高效逼近最优。随机只是“开门”,进化才是“走路”。

为何常见:只记住了名字里的 random,忽略了后面还有 evolutionary search 和 cost model 两个重头戏。

误区有同学在实战中把调优时间设到几天几夜,以为时间越长性能越强,结果收益甚微。

正解进化搜索和成本模型是渐进收敛的:前期提升快,后期收益递减,甚至因测量噪声而抖动。Ansor 的实践是给一个合理的时间预算(比如每算子几分钟到几十分钟),跑完就取当前最优,不必无限挂机。好钢要用在刀刃上,把时间分给多个算子通常比死磕一个算子更划算。

为何常见:直觉上“搜得更久 = 更优”,忽略了搜索的收敛规律和硬件测量噪声的影响。

误区有的同学以为自动调度只能用在 NVIDIA GPU 上,因为论文实验图表几乎都是 GPU,其他硬件没法用。

正解Ansor 是平台无关的调度框架,论文也在 ARM CPU、树莓派等平台上做了实验,同样有 2~10 倍加速。不同平台只是成本模型的行为不同,三级调度流程完全一致。只要目标硬件能编译执行 TVM 程序,Ansor 就能为其自动调优。

为何常见:论文实验图表以 GPU 居多,形成“Ansor 是 GPU 专用工具”的刻板印象。

面试题

Q1. Ansor 三级调度的正确顺序是?

单选题 · easy

  • A.注释、草图、随机采样
  • B.草图、注释、随机采样← 正确答案
  • C.随机采样、草图、注释
  • D.进化、注释、草图

答案:B。Ansor 先用草图规则生成程序骨架,再在骨架上填充注释(平铺、并行等参数),最后对完整注释空间做随机采样与进化搜索。顺序颠倒会导致骨架未定就无参数可填、参数未定就无处可采。

Q2. 程序草图(sketch)的核心作用是?

单选题 · easy

  • A.直接输出最优调度
  • B.定义循环计算骨架← 正确答案
  • C.替代成本模型评估
  • D.负责生成最终机器码

答案:B。草图只是定义多层循环的结构骨架,尺寸和并行标志都留空,因此它既不是最终调度,也不做评估或代码生成。它唯一的作用是压缩搜索空间的结构维度,把万亿级空间收敛到几十条骨架。

Q3. 注释(annotation)阶段需要确定的参数有?

多选题 · mid

  • A.平铺尺寸大小← 正确答案
  • B.是否并行执行← 正确答案
  • C.展开与向量化← 正确答案
  • D.算子融合方式

答案:ABC。注释负责填平铺尺寸、并行/串行、向量化/展开等循环级参数;算子融合属于计算图优化范畴,发生在调度搜索之前,不属于注释阶段。就像配料表只管分量火候,不管菜品是否拼盘。

Q4. 随机采样阶段的首要目的是?

单选题 · easy

  • A.直接得到最优结果
  • B.初始化种群和训练集← 正确答案
  • C.生成程序草图集合
  • D.降低硬件测量开销

答案:B。随机采样均匀抽出完整调度,一方面构成进化搜索的初代种群,另一方面实测结果成为成本模型的训练数据。采样本身通常不直接产出最优调度,但它是整个搜索流程的“第一桶金”。

Q5. 成本模型在 Ansor 中的角色是?

单选题 · mid

  • A.计算模型参数量
  • B.预测未测调度的耗时← 正确答案
  • C.自动生成程序草图
  • D.替代编译器后端工作

答案:B。成本模型用历史实测数据训练,输入程序特征、输出预测运行时间,从而在进化搜索中快速筛掉差候选。它不生成草图,也不算参数量,更不替代后端。它的角色是“评委”而不是“厨师”。

Q6. 与 AutoTVM 相比,Ansor 最本质的区别是?

单选题 · mid

  • A.支持更多的硬件平台
  • B.自动推导搜索空间← 正确答案
  • C.使用 Python 实现
  • D.只优化卷积运算

答案:B。AutoTVM 依赖工程师为每个算子手写模板,Ansor 用草图规则自动生成骨架并推导完整搜索空间。硬件支持和实现语言都不是本质差异。一个是“模具厂”需要人打模,一个是“自动捏造型再试口味”。

Q7. 进化搜索中的变异操作可能包括?

多选题 · mid

  • A.修改平铺尺寸大小← 正确答案
  • B.交换循环的执行顺序← 正确答案
  • C.调整向量化与展开← 正确答案
  • D.修改算子的数学定义

答案:ABC。变异在调度层面做小改动:换平铺尺寸、换循环次序、增删并行或向量化标志。算子本身的数学含义(如把矩阵乘法改成加法)不属于调度变异,那是改菜谱配方而不是调火候。

Q8. 一维循环长度 N=32,切分成 2 层平铺(顺序相关),合法注释数约为?

单选题 · hard

  • A.共有 3 种
  • B.共有 6 种← 正确答案
  • C.共有 32 种
  • D.只有 1 种

答案:B。根据递推公式 \(f_2(32)=\sum_{d \mid 32} f_1(32/d)=d(32)\)。32 的因子有 1、2、4、8、16、32 共 6 个,因此两两成对的平铺方案是 6 种。(1,32)(2,16)(4,8)(8,4)(16,2)(32,1)。

Q9. 成本模型的训练数据直接来源是?

单选题 · hard

  • A.公开的公共数据集
  • B.真实硬件实测耗时← 正确答案
  • C.专家人工标注结果
  • D.理论推导的计算量

答案:B。Ansor 把随机采样得到的调度编译后在真实硬件上运行,记录实际耗时作为标签。公开数据集、人工标注或理论复杂度都无法反映真实硬件的运行时间,必须“真刀真枪”实测才能训练出可靠的评委。

Q10. 关于 Ansor 优化效果的正确说法有?

多选题 · hard

  • A.GPU 上端到端快约 3.8 倍← 正确答案
  • B.ARM CPU 上也能明显加速← 正确答案
  • C.调优越久收益一定线性增长
  • D.三级调度结构化搜索空间← 正确答案

答案:ABD。论文显示 Ansor 在 GPU 上比默认 TVM 快约 3.8 倍,在 ARM 等平台也有明显加速;搜索是收敛的,收益随时间递减,不存在线性增长。三级调度正是通过“骨架→注释→采样”的结构化设计实现高效搜索。