news 2026/9/12 16:24:21

用遗传算法自动搜索 Yul 优化器最佳优化步骤序列:Solidity yul-phaser 工具实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用遗传算法自动搜索 Yul 优化器最佳优化步骤序列:Solidity yul-phaser 工具实战指南

用遗传算法自动搜索 Yul 优化器最佳优化步骤序列:Solidity yul-phaser 工具实战指南

【免费下载链接】soliditySolidity, the Smart Contract Programming Language项目地址: https://gitcode.com/GitHub_Trending/so/solidity

yul-phaser是 Solidity 仓库内置的一个内部工具,用于自动寻找 Yul 优化器(Yul optimiser)的最优优化步骤序列。它把"为给定程序挑选优化步骤与排列顺序"这一被称为phase-ordering problem的难题建模为搜索问题,并使用遗传算法(genetic algorithm)在庞大的解空间中迭代求解。读完本文,你将掌握 yul-phaser 的工作原理、全部核心命令行参数与默认值、如何从零开始搜索、如何从上次的搜索结果续跑、如何分析某个特定序列的指标与优化效果,以及如何把搜到的序列通过--yul-optimizations直接交给solc使用。

yul-phaser 要解决的问题

Solidity 编译器在启用优化后会经过多个 Yul 优化阶段(optimisation steps),每个阶段各司其职:有的消除无用代码(如UnusedPruner)、有的做公共子表达式消除(如CommonSubexpressionEliminator)、有的展开函数(如FullInliner)等。

这些阶段的执行顺序对最终生成代码的质量影响显著,而可能的排列组合空间极其庞大——这就是经典的 phase-ordering problem。更棘手的是,从理论上讲,可能不存在一个对所有程序都最优的单一序列:某个序列对合约 A 效果极佳,对合约 B 却可能适得其反。因此,与其手工反复试验,不如让程序自动搜索。

yul-phaser 正是为此而生的内部工具(位于 tools/yulPhaser 目录,编译目标名为yul-phaser,由 tools/CMakeLists.txt 构建)。它不参与日常编译流程,而是作为开发者的"参数搜索器":输入一组 Yul 程序,输出一个(或一批)适合这些程序的优化步骤序列。

工作流程:从问题建模到遗传算法

三个核心抽象

从源码结构看,整个工具建立在三个核心抽象之上(均在 tools/yulPhaser 下):

  1. Program(程序):Program.h 负责读取并解析输入文件,将其编译为 Yul AST,并允许把一组合规的优化步骤应用到程序上。
  2. Chromosome(染色体):Chromosome.h 表示一条优化步骤序列,即种群中的一个个体。它内部把步骤序列编码成字符串(每个字符代表一个优化步骤,见后文),并且一旦创建便不可变——想"变异"只能基于旧染色体生成新染色体。
  3. FitnessMetric(适应度指标):FitnessMetrics.h 计算一个染色体(序列)作用于程序后的"得分":得分越低,序列越好。所有指标都是确定性的,只依赖染色体本身与指标状态。

遗传算法的运转方式

算法入口在 AlgorithmRunner:它持有当前种群,在每一轮中调用可插拔的GeneticAlgorithm::runNextRound()对种群做进化,并负责打印轮次反馈、去重(把重复染色体替换为随机新个体,可用--no-randomise-duplicates关闭)、以及按需把种群写入文件。

yulPhaser/GeneticAlgorithms.h 中实现了三种可选算法:

  • GEWEPGenerationalElitistWithExclusivePools):每一轮将种群分为互斥的三部分——精英池直接保留,交叉池与变异池分别由精英个体通过交叉、变异生成新个体。这是默认算法。
  • classicClassicGeneticAlgorithm):经典三段式流程——按适应度比例选择(可重复选中同一染色体)、按概率配对做交叉、再按概率对每个基因逐一施加变异(基因随机化/删除/添加)。
  • randomRandomAlgorithm):每轮仅保留固定比例的精英,其余全部替换为随机生成的全新染色体。它不基于当前种群产生后代,相当于一个"带精英保留的随机搜索基线"。

三种算法的参数与默认值都在 Phaser.cpp 的命令行描述中定义(详见下文参数表)。

序列的简写表示:字母即优化步骤

优化步骤序列在 yul-phaser 中以字母字符串呈现,每个字符代表一个优化步骤。完整对照表见 docs/internals/optimizer.rst 的 "Optimizer Steps" 一节,例如:

缩写完整步骤名
fBlockFlattener
cCommonSubexpressionEliminator
DDeadCodeEliminator
iFullInliner
uUnusedPruner
gFunctionGrouper
hFunctionHoister
aSSATransform
vEquivalentFunctionCombiner
tStructuralSimplifier

注意:BlockFlattenerFunctionGrouperForLoopInitRewriter三个步骤是其他步骤的前提,Yul 优化器总会先于用户指定步骤应用它们(见 docs/internals/optimizer.rst 说明),因此 yul-phaser 在--prefix说明中也提到它会隐式添加hgo前缀,确保染色体可以包含任意优化步骤。

此外,[...]中的步骤会被循环重复应用,直到 Yul 代码不再变化或达到最大轮数(当前为 12)——这个机制直接复用到solc--yul-optimizations中(详见 docs/internals/optimizer.rst)。

快速上手:一次最简单的搜索

yul-phaser 对大多数参数都有合理的默认值,最简单的调用只需给出输入文件与随机种群大小:

tools/yul-phaser ../test/libyul/yulOptimizerTests/fullSuite/*.yul \ --random-population 100

前提是你已经编译了 Solidity 源码树(从而生成tools/yul-phaser可执行文件),并且当前位于该工作副本的构建目录中。输入是一个或多个 Yul 程序,每个候选序列都会被应用到所有这些程序上,再根据所选指标打分。

--random-population 100表示初始种群包含 100 条随机生成的序列。从 PopulationFactory 的实现可以看到,初始种群支持三种来源,且可以组合--population(显式指定序列)、--random-population(随机生成)、--population-from-file(从文件读取),最终种群是三者的并集。

运行yul-phaser --help可以查看全部可用选项(帮助文本由 buildCommandLineDescription 生成)。

常用工作模式

从上次搜索结果继续

搜索可能需要很长时间,yul-phaser 支持每轮结束后把当前种群保存到文件,中断后可无缝续跑:

tools/yul-phaser *.yul \ --random-population 100 \ --population-autosave /tmp/population.txt

停止应用后,用--population-from-file读回种群继续搜索,同时继续自动保存(保存文件会随每轮更新):

tools/yul-phaser *.yul \ --population-from-file /tmp/population.txt \ --population-autosave /tmp/population.txt

保存文件是纯文本格式:每行一条染色体(即一串字母)。这来自 buildFromFile 的实现——它按行读取文件并把每行当作一个基因序列。

分析一条给定序列

如果已经有了一条序列(无论是手工构造的还是搜出来的),可以用--rounds 0跳过算法迭代,让 phaser 只做"分析"工作。查看某条序列在指定指标下的得分:

tools/yul-phaser *.yul \ --show-initial-population \ --rounds 0 \ --metric code-size \ --metric-aggregator sum \ --population <your sequence>
  • --rounds 0:算法运行 0 轮,即不进化;
  • --metric code-size:使用程序大小指标(默认是relative-code-size);
  • --metric-aggregator sum:把该序列对每个输入程序的得分求和(默认为average);
  • --population <your sequence>:把目标序列作为初始(也是唯一)种群。

查看该序列优化后的程序长什么样:

tools/yul-phaser *.yul \ --rounds 0 \ --mode print-optimised-programs \ --population <your sequence>

--mode共有三种取值(见 Phaser.h):

  • run-algorithm:默认模式,运行遗传算法;
  • print-optimised-programs:打印每个染色体优化后的程序代码(对应 printOptimisedProgramsOrASTs 中operator<<输出的 Yul 文本);
  • print-optimised-asts:打印优化后的程序 AST(JSON 形式,对应同一函数中的toJson()分支)。

使用 solc 生成的中间表示

solc可以把 Solidity 合约编译成 Yul IR,而这些输出可以直接作为 yul-phaser 的输入:

solc/solc <sol file> --ir --output-dir <output directory>

输出目录中会出现一个或多个.yul文件。注意这些文件包含的是完整的 Yul 对象(object),而不仅是裸的 Yul 程序——yul-phaser 已经准备好处理这种情况(Program::load 负责解析包含 object 的输入)。这一步是"针对你自己的合约搜索优化序列"的标准做法:把项目里有代表性的合约转成 IR,喂给 phaser。

把搜索结果应用到编译器

搜索结束后,把得到的序列直接通过--yul-optimizations交给solc,让 Yul 优化器按该序列执行优化:

solc/solc <sol file> --optimize --ir-optimized --yul-optimizations <sequence>

该选项的语义与默认序列的差异在 docs/internals/optimizer.rst 中有详细说明:默认情况下优化器应用预定义的步骤序列;传入--yul-optimizations后即覆盖之。例如文档中的示例'dhfoD[xarrscLMcCTU]uljmul:fDnTOcmu'——其中方括号内的部分会被循环应用。

完整命令行参数参考

以下参数及默认值均直接取自 Phaser.cpp 中buildCommandLineDescription()的定义(yul-phaser --help的输出同源)。

通用参数

参数默认值说明
--help显示帮助信息并退出
--input-files <PATH>必填输入文件(位置参数,可多个)
--prefix <CHROMOSOME>空字符串自动应用于每个输入程序的初始优化步骤,结果视为实际输入;这些步骤不属于染色体、不会被变异;相对指标的基准也是应用这些步骤之后的程序。phaser 总是隐式加hgo前缀,本选项值在其后
--seed <NUM>随机生成随机数生成器种子(配合--show-seed可复现实验)
--rounds <NUM>无限制算法停止前的轮数
--mode <NAME>run-algorithm运行模式,见上文三种取值

算法通用参数

参数默认值说明
--algorithm <NAME>GEWEP可选GEWEPclassicrandom
--no-randomise-duplicates关闭默认每轮后把重复染色体替换为随机个体;开启后禁用该后处理
--min-chromosome-length <NUM>100随机染色体最小长度
--max-chromosome-length <NUM>100随机染色体最大长度
--crossover <NAME>uniform交叉算子:single-pointtwo-pointuniform
--uniform-crossover-swap-chance <PROBABILITY>0.5uniform 交叉中两个基因互换的概率

GEWEP 算法参数

参数默认值说明
--gewep-mutation-pool-size <FRACTION>0.25每轮由变异再生的种群比例
--gewep-crossover-pool-size <FRACTION>0.25每轮由交叉再生的种群比例
--gewep-randomisation-chance <PROBABILITY>0.9选择基因随机化作为变异操作的概率
--gewep-deletion-vs-addition-chance <PROBABILITY>0.5未选择随机化时,选择基因删除而非添加的概率
--gewep-genes-to-randomise <PROBABILITY>1/max-chromosome-length基因随机化中任意基因被变异的概率
--gewep-genes-to-add-or-delete <PROBABILITY>1/max-chromosome-length基因添加(或删除)中某个基因被添加/删除的概率

classic 算法参数

参数默认值说明
--classic-elite-pool-size <FRACTION>0.25精英占种群比例,每轮直接保留
--classic-crossover-chance <FRACTION>0.75染色体被选中参与交叉的概率
--classic-mutation-chance <FRACTION>0.01基因被随机化的概率
--classic-deletion-chance <PROBABILITY>0.01基因被删除的概率
--classic-addition-chance <PROBABILITY>0.01随机基因被添加的概率

random 算法参数

参数默认值说明
--random-elite-pool-size <FRACTION>每轮保留 1 个个体(与种群大小无关)每轮保留的种群比例

种群参数

参数默认值说明
--population <CHROMOSOMES>加入初始种群的显式序列;可空格分隔多个值或多次指定该选项
--random-population <SIZE>加入初始种群的随机染色体数量
--population-from-file <FILE>从文本文件(每行一条染色体)读取并加入初始种群
--population-autosave <FILE>禁用每轮结束后把种群写入指定文件

指标参数

参数默认值说明
--metric <NAME>relative-code-size适应度指标:code-size(程序绝对大小)或relative-code-size(相对原程序的比值)
--metric-aggregator <NAME>average多个程序得分如何合并:averagesummaximumminimum
--relative-metric-scale <EXPONENT>3相对指标的定标因子指数:指标为整数,相对值乘以 10^exp 后取整,如 exp=3 时 0.5→500、1.0→1000。因子越大越能区分细微差异,但数值可读性变差且大数可能丢精度
--chromosome-repetitions <COUNT>1染色体代表的步骤序列被重复应用的次数

指标权重参数

相对/绝对代码大小指标按 Yul AST 节点计"成本"(CodeWeights)。默认权重如下(见 Phaser.cpp 的 "METRIC WEIGHTS" 一节),源码注释指出这组权重是过渡方案,只为保证任何语句/表达式都不为零成本:

参数默认值
--expression-statement-cost1
--assignment-cost1
--variable-declaration-cost1
--function-definition-cost1
--if-cost2
--switch-cost1
--case-cost2
--for-loop-cost3
--break-cost2
--continue-cost2
--leave-cost2
--block-cost1
--function-call-cost1
--identifier-cost1
--literal-cost1

缓存与输出参数

参数默认值说明
--program-cache关闭缓存染色体前缀对应的中间程序,大幅加速适应度评估,但染色体较长时极其消耗内存;默认关闭(目前无法设置内存上限),内存充足时强烈建议开启
--show-initial-population关闭算法开始前打印初始种群
--show-only-top-chromosome关闭每轮只打印最优染色体而非整个种群
--hide-round关闭隐藏轮次信息(轮数与已用时间)
--show-cache-stats关闭每轮后打印缓存大小与命中情况
--show-seed关闭打印选中的随机种子

其中--program-cache的实现位于 ProgramCache.h:由于优化步骤是顺序应用的,前缀相同的序列共享相同的中间程序,缓存这些中间结果可以避免大量重复优化计算,代价是内存占用随染色体长度显著上升。

如何选好参数:来自实验的实用建议

挑选遗传算法参数并不简单,但 phaser 的默认值通常已足够在给定程序集上找到"与经验丰富的开发者手工调优结果相当甚至更好"的序列。真正困难的其实是提供有代表性的输入文件集

  • 如果输入文件用不到某些优化,工具会倾向产出不使用这些优化的序列,遇到能受益于它们的程序时会表现很差;
  • 反之,如果所有输入文件都极度依赖某个优化,找到的序列可能对不依赖它的程序不友好。

因此,输入文件集的代表性比参数微调更重要。在默认值基础上,以下经验结论来自维护者基于一组粗略实验的总结(相关讨论见 Solidity 问题 #7806),供参考:

  1. 表现最好的算法是GEWEP(因此它也是默认算法)。
  2. 初始种群使用更长的序列效果更好——算法本身擅长裁剪多余步骤。
  3. 保留上一轮的最优序列有助于提升结果;尤其在使用classic算法时,精英池(elite)至少应包含少量个体。
  4. 不要把变异/删除/添加概率设得太高:过高会破坏交叉保留的良好模式。1%–5% 左右的取值似乎效果最好(classic 算法默认的 0.01 正落在这个区间)。
  5. 让算法至少运行 1000 轮以上。通常它能更快找到好序列,但运行更久可以显著缩短序列长度——当以长序列起步时这一点尤其重要。

源码阅读指引

如果你想深入理解实现细节,推荐按以下路径阅读:

  • Phaser.cpp:命令行解析、各组件工厂(GeneticAlgorithmFactoryFitnessMetricFactoryPopulationFactoryProgramFactoryProgramCacheFactory)与总调度逻辑;
  • GeneticAlgorithms.h:三种遗传算法的接口与选项结构;
  • Chromosome.h:染色体(基因串)与优化步骤序列之间的编码/解码(genesToSteps/stepsToGenes);
  • FitnessMetrics.h:ProgramSizeRelativeProgramSize以及四种聚合器(average/sum/maximum/minimum);
  • Population.h:种群数据结构与makeRandom
  • Mutations.h:基因随机化、基因添加、基因删除三类变异算子;
  • AlgorithmRunner.h:轮次循环、去重、自动保存与输出反馈。

对应的 Yul 优化器全貌(所有优化步骤的详细说明)见 docs/internals/optimizer.rst,步骤缩写表见其中的 "Optimizer Steps" 一节;Yul 语言本身的文档见 docs/yul.rst。

小结

yul-phaser 把"Yul 优化步骤排序"这一组合优化问题交给遗传算法求解,并提供了从搜索、续跑、分析到与solc联动的完整工作流。它的正确使用姿势可以概括为三步:准备有代表性的 Yul 程序集(最好来自你自己的合约、经solc --ir转换)、以默认参数跑足够多轮(必要时调大初始序列长度与精英池)、把搜到的序列通过--yul-optimizations应用到编译器,从而为特定合约量身定制优化管线。

【免费下载链接】soliditySolidity, the Smart Contract Programming Language项目地址: https://gitcode.com/GitHub_Trending/so/solidity

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 16:22:53

GAIA实践指南:用5大维度和3项指标看清AI助手的真实能力

GAIA实践指南&#xff1a;用5大维度和3项指标看清AI助手的真实能力 【免费下载链接】agents-course This repository contains the Hugging Face Agents Course. 项目地址: https://gitcode.com/GitHub_Trending/ag/agents-course 同一个复杂任务丢给三个 AI 助手&…

作者头像 李华
网站建设 2026/9/12 16:17:04

Flutter for OpenHarmony开发环境与path_provider插件集成指南

1. Flutter for OpenHarmony 开发环境准备 在开始使用 path_provider 插件前&#xff0c;我们需要先搭建好 Flutter for OpenHarmony 的开发环境。与标准 Flutter 开发环境相比&#xff0c;这里有几个关键区别点需要注意。 1.1 鸿蒙版 Flutter SDK 安装 首先需要获取专门为 O…

作者头像 李华
网站建设 2026/9/12 16:17:01

协同过滤推荐算法:UserCF与ItemCF原理与实践

1. 协同过滤推荐算法概述推荐系统已经成为现代互联网服务的核心组件之一&#xff0c;而协同过滤&#xff08;Collaborative Filtering&#xff09;作为最经典且广泛应用的推荐算法&#xff0c;其核心思想可以概括为"物以类聚&#xff0c;人以群分"。简单来说&#xf…

作者头像 李华