想象一家餐厅,主厨小安面前摆着顾客的订单:A食材和B食材,先处理B再混合C。他不会直接冲进厨房乱炒,而是先站在白板前,用记号笔快速画出几种做法草稿——先蒸后炒、先炸再烩、或是把所有食材一起慢炖。每张草稿都只有三五笔,不写火候细节,只画大方向。等草稿贴满一墙,他才挑出几份写详细菜谱,让学徒试做、反复改良。 这套「先画草稿、再抠细节、最后试做」的思路,正是 AI 编译器领域 AutoTVM/Ansor 的核心思想。所谓草图生成(Sketch Generation),是 Ansor 自动调优流程的第一步。它把深度学习模型的计算图当作「顾客订单」,自动枚举出成百上千种可能的调度框架,取代了过去靠工程师手写算例模板的苦活累活。本文就用小安的故事,带你走一遍草图生成的全过程:如何读懂计算图、有哪些调度手法、怎样组合成草图、这些草图如何支撑后续搜索,以及它在真实工程中的价值。
理解草图生成,你就握住了现代 AI 编译器自动调优的第一块拼图——这是从「手写高性能算子」迈向「全自动化算子优化」的分水岭,也是面试编译器岗位时区分「懂概念」与「懂机制」的关键考点。
可视化演示
展示Ansor草图生成器从计算DAG按拓扑序推导调度骨架、派生规则匹配及多草图覆盖搜索空间的过程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
DAG推导:拓扑序枚举骨架 沿DAG拓扑序遍历每个算子 按算子类型匹配派生规则 sketch=derive(DAG) 生成循环嵌套结构骨架
1. 草图生成概念
小安在白板前站定,掏出记号笔。他没有直接开火,而是先画出三种做法的初步构想:第一种,把B蒸软再与A快炒;第二种,把A和B分别炸脆,最后下C烩;第三种,把所有食材一起慢炖。每张草稿只有三五笔,标记「先」「后」「并」几个关键词。在 Ansor 的世界里,这一步就是草图生成——整条自动调优流水线的第一站。
草图生成的目标,是拿到一份计算图后不依赖任何人工预设模板,自动产出一批「大体可行」的调度方案。调度(schedule)在 AI 编译器中指「如何安排计算循环的顺序、并行方式与数据放置」。草图只决定大框架,不规定细致参数——就像草稿只写先炒还是先炖,不写精确到几克盐。有了这批框架,后续的规则注解与进化搜索才有施展的舞台。
形式上,一张草图可以理解为作用于计算图 G 的一组调度规则序列:
对厨师来说,一墙草稿顶不上一盘菜,却决定了菜品可能的样貌;对编译器来说,一张草图也还不能执行,它只是把「哪些做法值得试」从无穷收敛到有限。这份克制,恰恰是草图生成最了不起的地方。
- Sketch(草图)多张候选草稿之一,代表一个调度大框架。
- G(计算图)被调度的对象,是编译器生成的中间表示,描述算子和数据流动。
- R_i(调度规则)取自规则库的原子变换,如循环分块、融合、并行化。
2. 计算图解析
小安拿到订单后,第一步不是翻菜谱,而是弄清楚食材之间的「先后关系」:B 必须提前处理才能和 A 同锅,C 又必须等 A、B 齐了才能下锅。他把这些关系画成一张箭头图——食材是点,箭头表示「谁等谁」。这张箭头图,就是计算图的缩影。
在 Ansor 中,草图生成器读取 TVM 编译流程产出的中间表示。这个中间表示把神经网络的层与算子展开成一张静态图:每个算子(矩阵乘、卷积、激活函数)是一个顶点,张量数据在算子间的流动形成边。系统还要推导每个算子的输出形状和计算规模,这些信息决定了循环的边界与迭代次数。
计算图用有向无环图(DAG)描述,这是后续一切枚举的基础:
小安把食材关系图钉在墙上,心里就有了谱:哪些步骤可以并行(两个灶台同时开工),哪些步骤必须排队。计算图解析做的正是这件事——它把「什么能先做、什么必须等」翻译成机器能理解的结构信息,为接下来的调度手法选择铺平道路。
- V(顶点集)图中的顶点是算子节点,如矩阵乘、卷积、归一化。
- E(边集)有向边表示张量数据从上游算子流向依赖它的下游算子。
- 有向无环依赖沿单一方向流动,图中不存在反馈环,因此可以按拓扑序调度。
3. 调度规则库
小安的厨房墙上挂着一排排工具:蒸锅、炒锅、炸锅、炖盅、切片刀、按摩锤。每种工具对应一种改变菜品口感的手法。在草图生成器里,也有一整套这样的「手法百宝箱」——调度规则库,每个规则都是一次原子级的循环结构变换。
循环是算子在硬件上执行的基本形态,深度学习计算可以看成一组嵌套循环。循环变换规则包括:split(分块,把大循环切成小 tile)、fuse(融合,把两个相邻循环合成一个)、reorder(重排,交换循环嵌套顺序);涉及并行的有 parallel(并行化,把循环分到多核上执行)、vectorize(向量化,让一条指令同时处理多个数据);还有面向存储的规则,如改变数据读取的缓存策略。
每条规则都是一个循环结构到另一个循环结构的映射:
规则库就像小安的调味架——每种手法单独看都很普通,但它们的不同组合却能做出南辕北辙的菜。草图生成的价值,正是让系统自己来决定「这盘菜该用哪几样手法、按什么次序下锅」,而不是每次都由主厨拍脑袋。
- L(循环结构)嵌套循环的集合,含循环边界、层顺序与步长。
- R_op(变换算符)把一种循环结构映射为另一种结构的原子操作。
- 典型成员split、fuse、reorder、parallel、vectorize 等都属于规则库。
4. 推导式生成
小安画草稿不是乱画,他有一套自己的推演套路:先看食材关系,再定主手法,最后补并行方案。比如「B 要先蒸——那就分成蒸透 B 的两小段;A 和 B 熟了可以同时下 C——那就把两步融合成一个步骤」。每一步都是在上一步的基础上继续,最后得到一张完整的做法草图。
Ansor 的推导式生成与此同构。它以计算图为起点,逐阶段施加规则:第一个阶段选循环变换,第二个阶段安排并行与向量化,第三个阶段调整存储。每个阶段从规则库中挑一条规则应用,前一个阶段的输出成为后一个阶段的输入。把所有可能的「选择路径」枚举出来,就得到了候选草图集合:
小安画满一墙草稿后,会退后两步整体端详:这份太琐碎,那份来不及,真正值得试的其实就几份。推导式生成同样产出一组「值得试」的草图——它不保证每张都好,但保证好方案大概率藏在这些候选里。这正是它比人工模板覆盖更广的原因。
- S(候选草图集合)所有合法规则组合产生的草图构成的集合。
- 复合作用三条规则依次施加到同一张计算图上,前一步的输出是后一步的输入。
- 规则选取每一步都从规则库中自由选取,可重复使用同一规则。
5. 搜索空间特点
菜单草稿贴满一墙后,小安发现自己面临一个新麻烦:如果每种食材组合都要试一遍,后厨烧一整天也试不完。他必须控制草稿数量,既不能太少而漏掉好菜,也不能太多而拖垮试菜节奏。这个「既要铺得开,又要收得拢」的平衡,就是搜索空间设计的核心问题。
草图生成后的搜索空间,由所有候选草图及其细化的变体组成。它的特点是覆盖广——因为规则是自动枚举的,凡是规则库表达范围内的调度形态都会被纳入;同时它又必须可控——如果每个算子都有多种选择,总候选数量会呈爆炸式增长。Ansor 通过限制枚举深度、约束规则使用次数来压缩空间,再用随机采样让搜索在有限时间内覆盖尽量多的高质量区域。
候选总数可以由乘积公式近似刻画:
如果说草图生成是为后厨「备料」,搜索空间的控制就是决定「一桌菜到底备多少份料」。备少了怕客人点不到,备多了怕浪费。Ansor 的答案很务实:把空间控制在「搜索器跑得完、好方案漏不掉」的甜区,再用采样这双快手,尽可能多翻几份可能的好选择。
- N_i(单算子可选数)第 i 个算子上可以施加的调度方案数量。
- 连乘(总空间)整图候选数量近似为所有算子可选方案数的乘积。
- 规模控制限制每个 N_i 的上界,避免总空间爆炸。
6. 与后续衔接
墙上的草稿只是开始。小安挑出三份最顺眼的草稿,蹲在灶台边把它们写成详细菜谱:精确到克的火候、精确到秒的计时、哪个灶头同时开火。在 Ansor 里,这一步叫规则注解(annotation):把草图这个「空架子」填上具体的循环因子、向量宽度、线程绑定的数值,变成可编译、可执行的完整调度。
完整调度生成后,紧接着是进化搜索(evolutionary search)。Ansor 把每份草图的变体当作「候选物种」,通过评测它们在目标硬件上的真实耗时,保留性能好的个体,再让它们交叉、变异产生新方案,一代代逼近最佳调度。整个过程的数学目标,是在草图集合 S 中找到一个性能最优的完整调度:
回到餐厅:草稿定方向,菜谱写细节,试菜定胜负。如果试菜发现某道菜不够惊艳,小安会回头调整草稿再试一轮——Ansor 的反馈迭代也是这样:搜索完一轮后,用结果反哺下一轮的草图枚举与采样策略。草图、注解、搜索三者首尾相连,形成一条自动化的调优闭环。
- Perf(性能函数)在真实硬件上测得或预估的执行时间、吞吐量等指标。
- s\*(最优调度)性能函数取得最大值的调度,是搜索的最终目标。
- argmax(搜索过程)通过注解生成可执行代码,再经进化搜索逼近最佳点。
7. 工程价值与挑战
自从学会了「画草稿」这套方法,小安的餐厅发生了质变。以前客人点一道没做过的菜,他要翻菜谱、试错好几遍才能定下做法;如今他只要看一眼食材关系,几分钟就能画出七八份可行方案交给学徒试做。更妙的是,即使来了全新的食材组合,他也不用慌——草稿法对「没见过的菜」同样有效。
这正是草图生成在工程上的核心价值:自动化与覆盖度。在传统的 AutoTVM 模板时代,工程师每遇到一种新的算子结构,就要手写一份调度模板,GPU 上动辄成百上千种算子,模板维护成本极高。Ansor 的草图生成把「人工写模板」变成「程序枚举规则组合」,显著降低了人力成本,同时覆盖了手工模板照顾不到的调度形态。实际部署中,它对不规则的张量形状、融合算子、新型硬件都非常友好。
但自动化不是免费的午餐。随着计算图规模变大,搜索空间的膨胀压力也开始显现:
小安明白,草稿画得快是本事,但画太多反而让后厨瘫痪。Ansor 同样清醒:草图生成让编译器更聪明,却不可能让搜索魔法般绕过物理开销。真正的工程智慧,是在自动化的广度与搜索的深度之间,找到那个让性能收益远大于额外开销的平衡点——这也是每个 AI 编译器工程师在实际系统中持续打磨的功课。
- 算子数 n深度学习模型越来越深、算子越来越多,计算图节点数持续增长。
- 乘积膨胀候选草图总数随算子数指数级增长。
- 工程取舍必须通过剪枝、阶段限制与随机采样把空间拉回可控范围。
AI 应用场景
云侧推理服务自动调优
阶段:算子优化与代码生成
解决的问题:传统方式下,工程师需要为 ResNet、BERT 等模型中的每个算子手写 CUDA 模板,一次模型升级就要重写数十份调度代码,人力投入大、覆盖不全。草图生成让编译器根据计算图自动枚举调度方案,再交由搜索器择优,大幅降低人工干预。
具体案例:某互联网公司把图像分类模型部署到 GPU 集群,输入尺寸在 16 到 128 之间动态变化。用 Ansor 草图生成后,系统为不同 batch 各自动画出一批调度草稿,进化搜索在几小时内挑出每档尺寸下的最优 kernel,推理吞吐比手工模板提升约 30%,且模型结构更新时无需重写模板。
新硬件编译适配
阶段:编译器后端开发与算子库构建
解决的问题:AI 芯片(如昇腾、寒武纪)上市时,算子库建设是最耗时的环节,几千个算子全靠手写成本极高。草图生成提供了一条通路:把新硬件的并行宽度、缓存层级、存储带宽建模进调度规则库,计算图自动映射为候选草图,再通过搜索为每块算子找到适配新架构的高性能实现。
具体案例:某芯片厂商在 TVM 之上接入自研 NPU。工程师将芯片的向量单元宽度与内存层级抽象为几条新规则后,草图生成器针对 200 多个常用算子自动产出初始调度库,与手写方案相比开发周期从数月缩短到两周,部分算子性能反超手写版本。
Python 演示
# -*- coding: utf-8 -*-"""草图生成最小演示: 用计算图与调度规则模拟 Ansor 的候选草图枚举""" from itertools import product class OpNode: """计算图上的算子节点""" def __init__(self, name, work_size): self.name = name self.work_size = work_size self.deps = [] def add_dep(self, dep): self.deps.append(dep) def build_graph(): """构造一个小型计算图: A, B -> C -> D""" a = OpNode("A", 1024) b = OpNode("B", 1024) c = OpNode("C", 512) d = OpNode("D", 256) c.add_dep(a) c.add_dep(b) d.add_dep(c) return [a, b, c, d] def gen_sketches(graph): """为每个算子列举可选规则, 再组合出全部候选草图""" rule_pool = ["split", "fuse", "parallel", "vectorize", "reorder"] candidates = [] for node in graph: opts = [(node.name, rule) for rule in rule_pool[:3]] candidates.append(opts) return [list(combo) for combo in product(*candidates)] def main(): graph = build_graph() sketches = gen_sketches(graph) print("计算图节点数:", len(graph)) print("枚举出的草图数量:", len(sketches)) for s in sketches[:5]: print(" ", s) if __name__ == "__main__": main()
from itertools import productproduct 用于计算多个列表的全部组合,正是「规则组合枚举」的引擎。class OpNode计算图节点的数据结构,保存算子名、计算规模与依赖列表。c.add_dep(a)构造「C 依赖 A」的有向边,形成有向无环图。rule_pool[:3]每个算子只取前 3 条规则,模拟空间规模控制。product(*candidates)逐节点选一种规则,组合成一张完整的候选草图。print("枚举出的草图数量:", len(sketches))直观看到 3^4=81 个候选草图,体会组合爆炸的威力。
常见误区
误区很多人以为草图生成就是像摇骰子一样随机抛出一堆调度方案,碰运气看哪个快。既然是「生成」,那不就是随机吗?
正解草图生成是结构化推导,不是随机瞎猜。它严格遵循计算图结构,按阶段从规则库中枚举合法变换,像厨师按食材关系推演做法而非乱炖。随机性只出现在后续搜索的采样环节,用来在已生成的空间里分配探索预算,两者完全不同。
为何常见:看到「枚举」「生成」这类词,容易联想到随机漫游,忽略了 Ansor 对调度规则合法性和推导顺序的约束。
误区新手看到「生成草图」就以为得到了可以直接上硬件运行的 GPU kernel,拿它去测性能,测出来不理想就以为机制失败了。
正解草图只描述调度的大框架,类似建筑设计的概念图——定了「哪里是卧室、哪里是客厅」,但没有墙厚、没有水电点位。Ansor 里草图之后还有规则注解阶段,把循环因子、向量宽度、线程绑定等细节填进去,才变成可编译的完整调度。
为何常见:直觉上「生成结果=可运行产物」,忽略了 AI 编译器流水线的分层设计,把「框架」当成了「成品」。
误区AutoTVM 的模板也能生成调度,那 Ansor 的草图生成是不是画蛇添足?老方法不是一直能用吗?
正解AutoTVM 的模板是工程师针对特定算子手工编写的,每新增一种算子结构就要写新模板,成本高、覆盖窄。草图生成把「人工写模板」升级为「程序枚举规则组合」,对任意计算图自动搭建候选方案库,覆盖面远超人肉维护的模板集。它不是小优化,而是把调优自动化推向新的层次。
为何常见:人们容易用「新旧工具都能响」来低估结构性改进,忽略了模板维护的人力成本和覆盖盲区。
误区既然草图决定了搜索空间的上界,那是不是草图生成得越多,搜索器就越容易找到最优调度?
正解草图是候选方案的上界而非「免费午餐」。空间每扩大一倍,搜索器需要评测的方案就翻倍,而硬件评测极其耗时。Ansor 通过限制规则组合深度、约束规则使用次数、引入随机采样,把空间压缩到搜索预算可承受的范围。空间过大反而会让搜索顾此失彼,性能不升反降。
为何常见:把「覆盖广」的收益绝对化,忽略了搜索开销这个现实约束,忘了工程的核心是性价比。
误区以为草图生成把「所有可能调度」都枚举了,进化搜索就一定能命中全局最优,性能一定胜过一切手写方案。
正解草图生成枚举的是「规则库表达范围内的框架」,并不包含所有想象得到的调度方式;且庞大的空间只能被采样,无法全量评估。所以最终结果大概率是「很好的局部最优」而非数学上的全局最优。对固定形状的经典算子,工匠级手写 kernel 仍可能胜出;草图的真正优势在于「广谱」和「自动化」。
为何常见:混淆了「搜索空间覆盖」与「全局最优可达」,高估了枚举方法的理论保证。
面试题
Q1. 在 Ansor 调优流程中,草图生成处于哪个位置?
- A.最后一步判定性能
- B.第一步构建搜索空间← 正确答案
- C.第二步细化注解
- D.第三步进化搜索
答案:B。草图生成是 Ansor 调优流程的第一站,负责构建搜索空间的候选基础。就像主厨先画草稿再试菜,草稿必然在试菜之前。规则注解和进化搜索都发生在草图生成之后。
Q2. 草图生成的直接输入是什么?
- A.编译后的机器码
- B.计算图结构← 正确答案
- C.训练好的权重
- D.历史性能数据
答案:B。草图生成器的输入是编译器产出的计算图中间表示,它描述算子与数据依赖。机器码是最终产物,权重属于训练阶段,历史性能数据是搜索反馈用的,都不是草图生成的直接输入。
Q3. 计算图用什么结构描述算子间依赖?
- A.有向无环图结构← 正确答案
- B.平衡二叉树结构
- C.单链式线性结构
- D.哈希键值对结构
答案:A。计算图的顶点是算子,边是数据流向,且不存在反馈环,标准结构是有向无环图(DAG)。二叉树、链表和哈希表都无法表达「多输入汇聚」这种一般的依赖关系。
Q4. 下列哪项属于调度规则库中的典型变换?
- A.循环分块融合← 正确答案
- B.梯度下降更新
- C.权重矩阵裁剪
- D.数据增强扩样
答案:A。调度规则库包含 split(分块)、fuse(融合)、parallel、vectorize 等循环结构变换。梯度下降、权重裁剪属于训练优化手段,数据增强属于数据预处理,都与调度规则无关。
Q5. Ansor 中「草图」与最终可执行代码的关系是?
- A.草图就是最终代码
- B.草图仅是大体框架← 正确答案
- C.草图与代码无关联
- D.草图只在训练使用
答案:B。草图只规定调度的大框架,类似概念设计图,不含循环因子、向量宽度等细节。它必须经规则注解填入具体参数、变成完整调度后才能编译执行。「草图就是代码」是常见误区。
Q6. 与传统 AutoTVM 模板相比,草图生成的核心优势是什么?
- A.免算子模板人工← 正确答案
- B.缩小搜索空间
- C.无需硬件设备
- D.代码体积更小
答案:A。传统 AutoTVM 需要工程师为每种算子手写模板,Ansor 的草图生成用程序化枚举代替人工写模板,既降低了人力成本,又覆盖了手工模板难以顾及的调度形态。它并不保证缩小空间或减小代码体积。
Q7. 草图生成之后,规则注解阶段的主要作用是什么?
- A.细化循环参数← 正确答案
- B.重新生成草图
- C.删除无效分支
- D.合并多个草图
答案:A。规则注解把草图确定的框架细化成具体参数:循环分块因子、展开系数、向量宽度、线程绑定等,使其成为可编译的完整调度。「重新生成草图」是倒退,「删除分支」「合并草图」都不是注解的职责。
Q8. 草图生成需要分析计算图的哪些信息?
- A.算子类型与形状← 正确答案
- B.算子间依赖关系← 正确答案
- C.训练数据分布
- D.损失函数大小
答案:AB。草图生成依赖算子类型(决定可用规则)、输出形状(决定循环边界)以及算子间的依赖关系(决定调度顺序约束)。训练数据分布与损失函数属于模型训练范畴,与调度枚举无关。
Q9. 关于 Ansor 搜索空间的说法,正确的有哪些?
- A.空间决定调优覆盖← 正确答案
- B.空间越大效果越好
- C.采样控制搜索开销← 正确答案
- D.空间需要限制规模← 正确答案
答案:ACD。搜索空间决定候选方案的覆盖范围,采样用于在有限预算内探索空间,规模必须受控防爆炸。「空间越大效果越好」是典型误区——空间过大导致搜索器顾此失彼,性能反而下降,就像菜单太厚反而让客人无从下口。
Q10. 关于草图枚举的推导过程,正确的说法有哪些?
- A.规则按阶段依次施加← 正确答案
- B.前一步输出作为后一步输入← 正确答案
- C.草图覆盖所有可能调度
- D.组合结果构成候选集合← 正确答案
答案:ABD。草图生成是分阶段推导,规则依次复合,前一个规则的输出是后一个的输入,所有组合路径构成候选草图集合。但它只覆盖规则库表达范围内的调度,并非「所有可能调度」——这也是搜索无法保证全局最优的原因。