solidity/tools/yulPhaser
2020-04-24 17:31:50 +02:00
..
AlgorithmRunner.cpp [yul-phaser] Make the Population constructor that takes individuals public and use it to speed up some operations 2020-04-06 19:06:08 +02:00
AlgorithmRunner.h [yul-phaser] AlgorithmRunner: Measure CPU time rather than wall-clock time 2020-03-25 10:21:18 +01:00
Chromosome.cpp [yul-phaser] Chromosome: Add a constructor that reads steps from an abbreviation string 2020-02-18 19:38:55 +01:00
Chromosome.h [yul-phaser] Chromosome: Add a constructor that reads steps from an abbreviation string 2020-02-18 19:38:55 +01:00
Common.cpp [yul-phaser] Common: Add readLinesFromFile() 2020-03-18 13:30:58 +01:00
Common.h [yul-phaser] Common: Add readLinesFromFile() 2020-03-18 13:30:58 +01:00
Exceptions.h [yul-phaser] AlgorithmRunner: Population autosave 2020-03-18 13:30:58 +01:00
FitnessMetrics.cpp [yul-phaser] ProgramBasedMetric: Add the ability to use ProgramCache 2020-03-24 17:39:24 +01:00
FitnessMetrics.h [yul-phaser] ProgramBasedMetric: Add the ability to use ProgramCache 2020-03-24 17:39:24 +01:00
GeneticAlgorithms.cpp [yul-phaser] Add options for selecting crossover operator used by the algorithms 2020-04-20 15:30:10 +02:00
GeneticAlgorithms.h [yul-phaser] GeneticAlgorithm::runNextRound(): Fix outdated docstring 2020-04-24 17:31:50 +02:00
main.cpp [yul-phaser] Don't return exit code from Phaser::main() and just assume 0 if it does not throw. 2020-03-16 20:32:59 +01:00
Mutations.cpp [yul-phaser] Mutations: Minor style tweak, missing space in the ternary operator 2020-04-20 15:30:10 +02:00
Mutations.h [yul-phaser] Mutations: Add two-point and uniform crossover operators 2020-04-20 15:30:10 +02:00
PairSelections.cpp Merge pull request #8515 from imapp-pl/yul-phaser-classic-genetic-algorithm 2020-04-15 12:01:51 +02:00
PairSelections.h [yul-phaser] Selections+PairSelections: Add RandomSubset and PairsFromRandomSubset 2020-04-06 19:06:08 +02:00
Phaser.cpp [yul-phaser] Tweak default values according to experiment results 2020-04-24 17:31:50 +02:00
Phaser.h [yul-phaser] Add options for selecting crossover operator used by the algorithms 2020-04-20 15:30:10 +02:00
Population.cpp [yul-phaser] Population: Add combine() 2020-04-06 19:06:08 +02:00
Population.h [yul-phaser] Population: Add combine() 2020-04-06 19:06:08 +02:00
Program.cpp [yul-phaser] Program: Switch from using parseCode() to parseObject() 2020-03-23 16:31:19 +01:00
Program.h [yul-phaser] Program: Switch from using parseCode() to parseObject() 2020-03-23 16:31:19 +01:00
ProgramCache.cpp [yul-phaser] ProgramCache: Add ability to gather cache stats 2020-03-25 10:21:18 +01:00
ProgramCache.h [yul-phaser] ProgramCache: Add ability to gather cache stats 2020-03-25 10:21:18 +01:00
README.md [yul-phaser] README 2020-04-24 17:31:50 +02:00
Selections.cpp [yul-phaser] Selections+PairSelections: Add RandomSubset and PairsFromRandomSubset 2020-04-06 19:06:08 +02:00
Selections.h [yul-phaser] Selections+PairSelections: Add RandomSubset and PairsFromRandomSubset 2020-04-06 19:06:08 +02:00
SimulationRNG.cpp Apply modernize-use-nullptr. 2020-04-01 12:46:19 -05:00
SimulationRNG.h [yul-phaser] SimulationRNG: Add bernoulliTrial() 2020-02-16 02:18:21 +01:00

yul-phaser

yul-phaser is an internal tool for finding good sequences of optimisation steps for Yul optimiser.

How it works

The space of possible solutions to this problem (usually referred to as phase-ordering problem) is extremely large and there may even be no single sequence that produces optimal results for all possible programs.

The tool uses genetic algorithms to find sequences that result in better programs than others and to iteratively refine them. The input is a set of one or more Yul programs and each sequence is applied to all of these programs. Optimised programs are given numeric scores according to the selected metric.

Optimisation step sequences are presented in an abbreviated form - as strings of letters where each character represents one step. The abbreviations are defined in OptimiserSuite::stepNameToAbbreviationMap().

How to use it

The application has sensible defaults for most parameters. An invocation can be as simple as:

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

This assumes that you have a working copy of the Solidity repository and you're in the build directory within that working copy.

Run yul-phaser --help for a full list of available options.

Restarting from a previous state

yul-phaser can save the list of sequences found after each round:

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

If you stop the application, you can later use the file to continue the search from the point you left off:

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

Analysing a sequence

Apart from running the genetic algorithm, yul-phaser can also provide useful information about a particular sequence.

For example, to see the value of a particular metric for a given sequence and program run:

tools/yul-phaser *.yul            \
    --show-initial-population     \
    --rounds            0         \
    --metric            code-size \
    --metric-aggregator sum       \
    --population        <your sequence>

You can also easily see program code after being optimised using that sequence:

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

Using output from Solidity compiler

yul-phaser can process the intermediate representation produced by solc:

solc/solc <sol file>  \
    --ir              \
    --no-optimize-yul \
    --output-dir <output directory>

After running this command you'll find one or more .yul files in the output directory. These files contain whole Yul objects rather than just raw Yul programs but yul-phaser is prepared to handle them.

How to choose good parameters

Choosing good parameters for a genetic algorithm is not a trivial task but phaser's defaults are generally enough to find a sequence that gives results comparable or better than one hand-crafted by an experienced developer for a given set of programs. The difficult part is providing a fairly representative set of input files. If the files you give don't need certain optimisations the tool will find sequences that don't use these optimisations and perform badly for programs that could benefit from them. If all the provided files greatly benefit from a specific optimisation, the sequence may not work well for programs that do not.

We have conducted a set of rough experiments to evaluate some combinations of parameter values. The conclusions were used to adjust the defaults but you might still benefit from some general observations:

  1. The algorithm that performed the best was GEWEP.
  2. Using longer sequences in the initial population yields better results. The algorithm is good at removing superfluous steps.
  3. Preserving the top sequences from previous rounds improves results. Elite should contain at least a few individuals, especially when using the classic algorithm.
  4. Don't set mutation/deletion/addition chance too high. It makes results worse because it destroys the good patterns preserved by crossover. Values around 1-5% seem to work best.
  5. Keep the algorithm running for 1000 rounds or more. It usually finds good sequences faster than that but it can shorten them significantly if you let it run longer. This is especially important when starting with long sequences.