想象一家餐厅的后厨。传统大厨手里有一本厚厚的祖传菜谱,每道菜都写好了固定刀工和火候——这些菜谱就是“模板”。AutoTVM 就像这位大厨,它把成功经验固化成模板,调优时挨个试菜谱。可菜谱再厚,也只能覆盖已知菜品;遇到从没见过的食材,大厨只能等老师傅来补写新条目。Ansor 完全不同:它不翻菜谱,而是像一位大胆的新主厨,先按一套自动规则把食材写成“烹饪草稿”,再通过随机改刀、换火候、调整摆盘生成大量候选菜,让经验丰富的“试吃员”快速估算哪一道更可能好吃,最后从优秀候选里一代代进化。整个过程不需要专家手工写模板,计算机自己就能发明新菜式。这篇讲解带你走一遍 Ansor 的探索流程,看看它是如何在抽象的调度空间中低成本、大范围地找到好调度。

岗位关联

对 AI 编译从业者而言,掌握 Ansor 的无模板调度搜索,是从“套模板调优”走向“自动化探索优化空间”的关键一步,能显著提升新算子在大规模模型中的部署效率。

目录
  1. 可视化演示
  2. 模板调优痛点
  3. 草图生成规则
  4. 注释变换机制
  5. 随机进化搜索
  6. 代价模型引导
  7. 搜索策略优化
  8. 无模板收益分析
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示Ansor无模板自动调度器的四阶段工作流、循环变换规则及进化搜索机制,交互切换查看调度搜索核心流程。

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

工作流:四阶段无模板调度 分析计算图提取循环结构与依赖 自动生成调度草图并进化搜索 space=sketch+evolve 摆脱人工模板实现广覆盖

模板调优痛点

老主厨有一本祖传菜谱,里面写着每道菜的标准刀工和火候。AutoTVM 就像这位老主厨,把成功经验固化成模板,搜索调度就是打开菜谱一项项试。菜谱看似丰富,翻开却只有几十个固定条目:遇到新的食材组合,条目对不上,老主厨只能放下菜刀等专家来补写。越新的食材,菜谱越是一片空白。

在 AI 编译里,“食材”是算子(也就是一道待烹饪的计算任务),“菜谱条目”就是调度模板。调度(schedule)指循环如何切分、并行、展开等一系列操作顺序。AutoTVM 的模板会预留若干参数,比如循环展开因子、线程数,搜索器只负责填参数。填参数虽然快,但模板本身没有覆盖的算子,根本无法进入填参环节。模板漏掉一个结构,可能就错过最优性能。

假设一个算子的所有调度方案总数为 N,模板能表达的只有 M 种。当新算子不断出现,M 相对 N 会迅速变小。调优时间的大头,反而变成了专家为算子写模板的时间。

模板调优的痛点正像一本残缺菜谱:会做的菜很多,但不会做的更多。当新算子数量爆炸,等待专家补模板会把优化的希望寄托在人工经验上。Ansor 要做的第一件事,就是扔掉这本菜谱。

C=MNC = \frac{M}{N}
覆盖率 C 用模板能生成的调度数 M 除以所有可能的调度数 N,衡量模板到底能触及多少优化空间。
  1. 定义候选空间把对一个算子所有可行调度方式的总数记为 N,它通常随循环层数指数增长。 N=2k1+k2+⋯N = 2^{k_1 + k_2 + \cdots}
  2. 统计模板覆盖把每个模板能生成的参数组合累加起来,得到模板可覆盖的调度数 M。 M=∑t∈T∏i=1rat,iM = \sum_{t \in \mathcal{T}} \prod_{i=1}^{r} a_{t,i}
  3. 计算覆盖率模板覆盖的调度数占总候选数的比例就是覆盖率 C,C 越接近 1 表示覆盖越全。 C=MNC = \frac{M}{N}

草图生成规则

Ansor 的新主厨不翻菜谱,而是先按一套“拆菜规则”写出烹饪草稿。所谓草稿,就是调度草图:它把一道菜切成几个阶段——备料、切配、下锅、摆盘,每个阶段都描述一层循环结构。主厨不用知道最终味道如何,只需要快速写出大量可行的草稿。

在实际调度中,Ansor 会对计算流程做分阶段生成。每个阶段对应一次计算产物的循环结构,比如矩阵乘法的累加循环或是卷积的输出通道循环。Ansor 有一组分派规则,根据算子的形状和访存特征,把不同结构匹配到不同的草图模板片段。这样生成的不是一个完整手工模板,而是许多随机初始草图。

因为草图来自规则,所以每条草图被生成的概率可以分解为各阶段概率的乘积。Ansor 正是利用这种概率分解,在生成阶段就鼓励多样化和覆盖度,避免所有草稿长得一模一样。

有了这套写草稿的规则,第一次把“无模板”落到了实处。不过草稿只是起点,真正让菜品千变万化的,是写完之后在菜谱边上加的“批注”。

P(S)=∏i=1kP(si)P(S) = \prod_{i=1}^{k} P(s_i)
调度草图 S 的生成概率,等于各阶段 s_i 被选中的概率相乘,概率越大代表这条草图越“主流”,但 Ansor 也会故意保留低概率的草图来探索新结构。
  1. 划分阶段把调度草图拆成 k 个阶段,每个阶段对应一个循环计算块。 S=s1s2⋯skS = s_1 s_2 \cdots s_k
  2. 独立选择假定每个阶段的生成规则相互独立,则联合概率为各阶段概率的连乘。 P(S)=∏i=1kP(si)P(S) = \prod_{i=1}^{k} P(s_i)
  3. 指导采样Ansor 在高概率处多采样,同时保留随机性,保证覆盖不同结构。 S∼P(S)S \sim P(S)

注释变换机制

厨师写菜谱时,常会在旁边加一行小字:“蒜末爆香后改为小火慢煨。”这些批注不改变菜是什么,却会改变做菜的速度和口感。Ansor 的注释(annotation)也是如此:它不改变计算结果,只改变循环结构,比如把某个循环展开、绑定向量化宽度、绑定并行线程数。

循环展开是最典型的注释:原来一个循环每次只处理一个元素,现在改成一次处理 4 个元素,循环次数减少了,循环控制的开销也随之摊薄。向量化注释则是告诉编译器把多个数装进同一个寄存器并行计算;并行化注释则是让不同线程同时处理不同数据块。这些注释组合起来,可以在同一张草图上衍生出大量性能不同的变体。

如果把循环展开因子记为 u,原来需要 n 次迭代,展开后大约只需要 n/u 次迭代。迭代次数下降,开销自然下降,这正是注释最直接的价值。

注释像菜谱边上的小批注,同一个草稿能演出几十种变体。但光靠主厨手写批注不够,Ansor 还要让这些批注自动“变异”,从而进入进化搜索。

n′=⌈nu⌉n' = \left\lceil \frac{n}{u} \right\rceil
n' 是循环展开后的迭代次数,u 是展开因子;向上取整是因为剩余不足一次循环也要完整执行一次。
  1. 记录原迭代次数未展开的循环总迭代次数为 n。 n=原循环总次数n = \text{原循环总次数}
  2. 应用展开因子展开因子 u 表示每次迭代同时处理 u 个原数据。 n′=⌈nu⌉n' = \left\lceil \frac{n}{u} \right\rceil
  3. 得到最终循环次数新循环只需执行 n' 次,循环条件判断和跳转开销也随之减少。 n′=⌈nu⌉n' = \left\lceil \frac{n}{u} \right\rceil

随机进化搜索

新主厨最擅长折腾:他会把上一轮的草稿随机改一刀,有时把展开因子从 2 改成 4,有时把并行线程从 1 改成 2,有时干脆交换两层循环的位置。这种随机微调就像做菜时随手换一种配料,有些改动会变难吃,有些却意外惊艳。Ansor 把这一过程叫随机变换。

随机变换不是乱改,它会严格遵守“上下文变换”规则:所有修改都保持计算结果等价,只改变执行方式。比如循环展开、拆分、重排序都属于上下文变换。Ansor 维护一个种群,里面保留多条候选调度,每次从中挑一些进行变异,再结合代价模型给出的分数淘汰较差个体,让种群整体不断进化。

用公式表示,从父代 S 生成子代 S′,子代被保留的概率与它的分数在所有候选分数之和中的占比成正比。分数越高,越容易被留下继续繁殖。

随机进化让 Ansor 能在草稿基础上源源不断地产出新方案。可问题是,每道菜都真做一遍太慢了,Ansor 需要一个能快速“试吃”的帮手。

Pkeep(S′)=score(S′)∑Sj∈Pscore(Sj)P_{\text{keep}}(S') = \frac{\text{score}(S')}{\sum_{S_j \in \mathcal{P}} \text{score}(S_j)}
子代调度 S′ 被保留的概率,等于自身得分占整个种群 P 总得分的比例;这是“优胜劣汰”的数学表达。
  1. 随机变异对父代 S 施加一个随机上下文变换,得到子代 S′。 S′=Mutate(S)S' = \text{Mutate}(S)
  2. 计算个体得分用代价模型给 S′ 一个分数,得分越高代表执行时间越短。 score(S′)=cost_model(S′)\text{score}(S') = \text{cost\_model}(S')
  3. 按比例保留把 S′ 的得分除以种群内所有个体的得分之和,得到保留概率。 Pkeep(S′)=score(S′)∑Sj∈Pscore(Sj)P_{\text{keep}}(S') = \frac{\text{score}(S')}{\sum_{S_j \in \mathcal{P}} \text{score}(S_j)}

代价模型引导

如果每道菜都要完整做一遍才能判断好不好吃,探索几十万种菜谱根本不现实。Ansor 的做法是请一位“试吃员”:他不用真正下厨,只要看看食材切法、火候标注、摆盘顺序,就能估算这道菜大概要花多久。这位试吃员就是代价模型。

代价模型会提取调度草图的特征,比如循环迭代次数、访存次数、并行度、向量化宽度等,然后通过学习历史数据预测执行延迟。Ansor 在搜索中用预测分数给所有候选草图排序,只把分数最高的几百个送入真实编译测量。这样大部分差方案在预测阶段就被筛掉了,大幅降低编译开销。

这很像用特征做加权打分:每个特征乘上一个权重,再加一个基准值,就得到预测耗时。权重来自训练数据,数据来自真实硬件上的测量结果。

试吃员虽然会偶尔看走眼,但在大规模筛选中足够可靠。有了它,Ansor 就能在有限时间里多看很多菜,但它仍需要聪明的“试菜顺序”。

t^(s)=w0+∑j=1dwjfj(s)\hat{t}(s) = w_0 + \sum_{j=1}^{d} w_j f_j(s)
预测耗时 \hat{t}(s) 等于基准值 w0 加上各特征 f_j(s) 与权重 w_j 的乘积之和;权重由训练数据学习而来。
  1. 提取特征从调度草图 s 中提取 d 个关键特征,如循环层数、展开因子。 fj(s),j=1,2,…,df_j(s), \quad j = 1, 2, \ldots, d
  2. 赋予权重每个特征乘上学习到的权重 w_j,表示该特征对耗时的影响程度。 ∑j=1dwjfj(s)\sum_{j=1}^{d} w_j f_j(s)
  3. 加上基准值再加上常数项 w0,得到最终预测执行时间。 t^(s)=w0+∑j=1dwjfj(s)\hat{t}(s) = w_0 + \sum_{j=1}^{d} w_j f_j(s)

搜索策略优化

有经验的试菜不会把每道菜平均用力:先快速尝一轮,锁定价位合适的几道,再对最有希望的反复精调。Ansor 的搜索策略也一样,它采用分层搜索:先粗粒度生成大量草图,划分出几个有潜力的区域,再在区域内细粒度微调。这样既覆盖大范围,又不会均匀撒网导致时间浪费。

搜索还有一个难点:是继续深挖当前最高分,还是去看看还没被评估过的冷门候选?Ansor 在代价模型和进化搜索中都会使用探索与利用平衡策略。公式里有两项:第一项代表当前已知的分数,第二项代表这个候选被评估的次数越少、越值得去探索。当采样总数增加,这个探索奖励还会自动变化。

有了这套试菜顺序,Ansor 能在大厨房里快速找到好菜,而不是漫无目的地乱试。那么,扔掉模板的最终收益到底有多大?

U(s)=score(s)+λln⁡NnsU(s) = \text{score}(s) + \lambda \sqrt{\frac{\ln N}{n_s}}
调度 s 的综合分数 U(s) = 已知分数 + 探索奖励;N 为总采样次数,n_s 为 s 已被评估的次数,评估越少探索奖励越高。
  1. 利用已知分数第一项 score(s) 是代价模型对调度 s 的当前预测,分数高就值得留下。 U1=score(s)U_1 = \text{score}(s)
  2. 计算探索奖励第二项中 ln N 随总采样次数缓慢增长,除以 n_s 后开根号,评估少的候选会获得更大奖励。 U2=λln⁡NnsU_2 = \lambda \sqrt{\frac{\ln N}{n_s}}
  3. 综合排序两项相加后,搜索会优先选择综合分数最高的候选。 U(s)=U1+U2U(s) = U_1 + U_2

无模板收益分析

从祖传菜谱走到自动发明新菜,最大的变化不是做菜动作,而是后厨的运转方式。AutoTVM 像一位依赖菜谱的老主厨,每来一道新菜都要等专家来编写新条目;Ansor 则像一支自动研发团队,从草稿生成到试吃调整全部自动完成。结果就是覆盖率提升、新算子适配加快、调优周期缩短。

覆盖率提升来自草图生成规则:它不再限制于人工模板能表达的参数组合,而是直接生成循环结构与注释组合。自动化程度提高则来自整条流水线闭合:规则、变换、代价模型和进化搜索共同协作,人工只需要选择搜索时间和预算,不再需要手写调度模板。与 AutoTVM 相比,Ansor 在搜索空间广度上明显占优,但也仍然需要人工设计规则库和代价模型特征,并非完全零干预。

Automation=1−TmanualTtotal\text{Automation} = 1 - \frac{T_{\text{manual}}}{T_{\text{total}}}

1. 统计总时长:记录完成一次调优所花费的全部时间 T_total。 Ttotal=Tmanual+TsearchT_{\text{total}} = T_{\text{manual}} + T_{\text{search}}

2. 统计人工时长:其中专家写模板、调规则的时间记为 T_manual。 Tmanual=人工干预时间T_{\text{manual}} = \text{人工干预时间}

3. 计算自动化程度:用 1 减去人工占比,得到自动化程度。 Automation=1−TmanualTtotal\text{Automation} = 1 - \frac{T_{\text{manual}}}{T_{\text{total}}}

无模板的收益,本质上是从“人力补全知识”变成“系统自动探索知识”。Ansor 没有让主厨失业,而是把主厨从重复写菜谱中解放出来,让他专注于设计更好的规则和模型。

AI 应用场景

新算子快速适配

阶段:模型部署与自动调优

解决的问题:当新模型结构(如视线Transformer、稀疏专家模型)不断出现,传统 AutoTVM 需要专家为每个新算子手写模板,可能要数天;Ansor 通过草图生成和进化搜索自动覆盖新算子,调优时间从数天压缩到数小时。

具体案例:某推理加速团队上线一个新型注意力算子,只提供算子定义和硬件平台。Ansor 自动生成上千个候选调度,用代价模型筛选后选出最优方案,全程无需人工编写调度模板。

异构硬件上的自动化适配

阶段:跨平台编译优化

解决的问题:同一模型要在 GPU、CPU、NPU 上部署时,各平台指令集和访存特征差异大,手工模板难以迁移;Ansor 的规则库按平台分别采样,配合代价模型学习各平台特点,自动适配不同硬件。

具体案例:云厂商将统一模型部署到三种硬件,Ansor 分别在每个平台上执行一次无模板搜索,自动生成平台特化调度,省去了三套专家模板的维护。

大规模模型编译流水线

阶段:端到端一键式模型编译

解决的问题:大模型拥有上万个算子,逐个调优不现实;Ansor 将搜索策略嵌入编译流程,只对热点算子做深度搜索,冷门算子用快速规则生成,在整体编译时间预算内取得最好性能。

具体案例:在基于 TVM 的推理引擎中,模型导入后 Ansor 自动识别热点算子并安排搜索预算,最终整网延迟下降 30%,编译时间仍在可控范围。

Python 演示

python48 行
import random WORKLOAD = 10000.0 def generate_sketch():    stages = []    for i in range(3):        stage = {            "name": "conv" if i == 0 else "matmul",            "unroll": random.randint(1, 4),            "vector": random.randint(1, 4),            "parallel": random.randint(1, 2)        }        stages.append(stage)    return stages def mutate(stages):    new_stages = [dict(st) for st in stages]    idx = random.randrange(len(new_stages))    key = random.choice(["unroll", "vector", "parallel"])    delta = random.choice([-1, 1])    new_stages[idx][key] = max(1, new_stages[idx][key] + delta)    if random.random() < 0.2:        new_stages[idx]["name"] = random.choice(["matmul", "conv", "pool"])    return new_stages def cost_model(stages):    cost = 0.0    for st in stages:        ratio = st["unroll"] * st["vector"] * st["parallel"]        cost += WORKLOAD / ratio    return cost def evolutionary_search(iters=200, pop_size=12):    pop = [generate_sketch() for _ in range(pop_size)]    best = min(pop, key=cost_model)    for _ in range(iters):        child = mutate(best)        pop.append(child)        pop.sort(key=cost_model)        pop = pop[:pop_size]        best = pop[0]    return best, cost_model(best) if __name__ == "__main__":    best_schedule, best_cost = evolutionary_search()    print("best schedule:", best_schedule)    print("estimated cost:", best_cost)
目的:用 Python 模拟 Ansor 核心思想——随机生成调度草图、上下文变换、代价模型排序、进化保留种群;每次运行都会输出搜索到的最优调度草图和预测成本。
  1. def generate_sketch():随机生成初始调度草图,相当于主厨先写流程草稿。
  2. def mutate(stages):对某个阶段随机调整一个优化参数,模拟换火候或改刀法。
  3. def cost_model(stages):用简单除法公式估算执行时间,相当于试吃官快速评分。
  4. pop.sort(key=cost_model)每轮按评分排序并只保留前 pop_size 个,实现种群优胜劣汰。
  5. if __name__ == "__main__":以脚本方式运行时输出搜索到的最优调度,验证完整流程。

常见误区

误区Ansor 既然叫无模板,肯定就是像无头苍蝇一样随机乱试,没有任何章法。

正解Ansor 只是不依赖专家为每个算子写完整调度模板,但它仍然使用分派规则生成草图,用上下文变换和剪枝控制搜索范围。它更像按一套“自动写草稿规则”工作,规则照样是结构,只是不再落到某个具体算子头上。

为何常见:把“模板”理解成“规则”,看到无模板就以为连规则都不要了,忽略了规则库仍然存在。

误区代价模型只要预测不准,Ansor 就会把好方案筛掉,所以必须让预测误差尽量小。

正解Ansor 的代价模型只需要拥有不错的相对排序能力:让好方案整体排在前面、差方案集体靠后即可。搜索过程还会结合真实测量修正,少量误判不会致命,真正常见的问题是找错特征而不是模型不够复杂。

为何常见:习惯于把机器学习当作“精确预测器”,忘了在搜索场景里排序质量往往比绝对精度更重要。

误区Ansor 生成调度草图之后,只需要在这些草图中挑一个最快的执行,后面不再改变。

正解草图只是搜索的出发点。Ansor 还会对草图做循环展开、向量化、并行绑定等注释,再经过随机变换和进化搜索不断修改结构。一个初始草图可能最终演化成完全不同的高效调度。

为何常见:只盯着“草图生成”这个名词,忽略了后续变换、搜索、优化才是无模板方法的核心价值。

误区Ansor 既然能生成海量草图,那就把搜索空间无限扩大,一定可以找到全局最优调度。

正解搜索空间过大,评估数量不够时反而会漏掉好方案。Ansor 通过分层搜索、代价模型筛选、剪枝机制和进化保留策略,在有限时间预算内平衡广度和深度。扩大空间必须伴随更聪明的搜索,否则只会增加噪声。

为何常见:把“候选多”和“搜索能力强”混为一谈,忽略了时间预算和评估成本才是真实约束。

面试题

Q1. Ansor 与 AutoTVM 在调度搜索上的核心区别是什么?

单选题 · easy

  • A.Ansor 不需要人工编写完整模板← 正确答案
  • B.Ansor 只能搜索卷积算子
  • C.AutoTVM 支持动态形状
  • D.Ansor 不使用任何一种规则

答案:A。Ansor 的核心创新是摆脱人工模板,通过规则生成草图并进行进化搜索。它仍使用分派规则、变换规则等,所以 D 错误;Ansor 也不是只能处理卷积,B 错误;AutoTVM 在动态形状上并无显著优势,C 错误。

Q2. Ansor 中的“调度草图”最恰当的理解是什么?

单选题 · easy

  • A.一张完整实现代码的缩略图
  • B.包含多个阶段的循环计算流程草稿← 正确答案
  • C.一个已经训练好的神经网络模型
  • D.编译器输出二进制文件的注释

答案:B。调度草图由多个阶段组成,每个阶段描述循环结构和计算顺序,相当于菜谱初稿。它不是最终代码缩略图,也不是神经网络模型或二进制注释,因此 A、C、D 都不准确。

Q3. 下面哪个操作属于 Ansor 的“注释(annotation)”?

单选题 · mid

  • A.修改算子数学定义
  • B.把循环按因子 4 展开← 正确答案
  • C.删除中间张量不计算
  • D.改变卷积权重初始化

答案:B。注释包括循环展开、向量化绑定、并行绑定等,它们不改变数学语义,只改变执行方式。修改数学定义、删除中间张量或改变权重初始化都会破坏原算子语义,不属于注释。

Q4. 在循环展开公式 n'=ceil(n/u) 中,u 代表什么?

单选题 · mid

  • A.总迭代次数
  • B.循环展开因子← 正确答案
  • C.向量寄存器数量
  • D.线程块数量

答案:B。u 是循环展开因子,表示每次迭代同时处理 u 个原数据点。总迭代次数是 n,向量寄存器数量和线程块数量属于并行绑定的其他注释,不符合公式含义。

Q5. 代价模型在 Ansor 搜索中的主要作用是什么?

单选题 · hard

  • A.完全替代真实硬件测量
  • B.只评估最终选择的调度
  • C.用预测分数快速排序候选草图← 正确答案
  • D.生成新的算子表达式

答案:C。代价模型主要用来在搜索过程中快速筛选并排序大量候选草图,减少真实编译次数。它不能完全替代真实测量,也不只是评估最终结果,更不会生成算子表达式。

Q6. 关于随机变换与进化,哪种说法正确?

单选题 · mid

  • A.每次只保留最好的一个个体
  • B.通过变异和保留高分组维持种群多样性← 正确答案
  • C.随机变换不会改变草图结构
  • D.进化只会让搜索变得更慢

答案:B。Ansor 维护一个种群,随机变异后按代价模型排序,保留前若干名并淘汰尾部,既保留精华又维持多样性。只保留一个个体容易陷入局部最优;随机变换会改变结构;进化虽然增加计算,但整体收益更大,A、C、D 均错误。

Q7. 公式 U(s)=score(s)+λ√(lnN/n_s) 中,第二项主要起什么作用?

单选题 · hard

  • A.增加人工反馈权重
  • B.鼓励探索被评估次数较少的调度← 正确答案
  • C.降低代码编译体积
  • D.记录每个算子的日志

答案:B。第二项称为探索奖励,n_s 越小,这一项越大,从而让被评估次数少的候选也有机会被选中。它不涉及人工反馈、编译体积或日志记录,因此 A、C、D 错误。

Q8. Ansor 无模板方法主要提升了哪些方面?

多选题 · easy

  • A.调度覆盖率← 正确答案
  • B.调优自动化程度← 正确答案
  • C.人工模板可读性
  • D.新算子的适配速度← 正确答案

答案:ABD。无模板方法通过自动生成草图扩大覆盖率,也提升了自动化程度和新算子适配速度。人工模板可读性与 Ansor 的目标无关,C 错误。

Q9. 下列哪些属于 Ansor 草图生成阶段使用的机制?

多选题 · mid

  • A.分派规则匹配← 正确答案
  • B.分阶段构建循环结构← 正确答案
  • C.手工指定所有循环次序
  • D.初始草图采样← 正确答案

答案:ABD。Ansor 用分派规则匹配算子特征,分阶段构建循环结构,并采样多个初始草图。手工指定所有循环次序回到了模板方法,不是 Ansor 的做法。

Q10. 哪些措施帮助 Ansor 在更大搜索空间中保持高效?

多选题 · hard

  • A.用代价模型快速筛选← 正确答案
  • B.使用进化搜索保留优秀个体← 正确答案
  • C.对候选进行剪枝← 正确答案
  • D.放弃所有启发式规则

答案:ABC。代价模型快速排序、进化保留优秀个体、剪枝机制都能减少无效评估。Ansor 没有放弃启发式规则,规则库恰恰是草图生成的骨架,D 错误。