mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8451 from imapp-pl/yul-phaser-program-cache
[yul-phaser] Program cache
This commit is contained in:
commit
28c0bc5929
@ -154,6 +154,7 @@ set(yul_phaser_sources
|
|||||||
yulPhaser/Phaser.cpp
|
yulPhaser/Phaser.cpp
|
||||||
yulPhaser/Population.cpp
|
yulPhaser/Population.cpp
|
||||||
yulPhaser/Program.cpp
|
yulPhaser/Program.cpp
|
||||||
|
yulPhaser/ProgramCache.cpp
|
||||||
yulPhaser/Selections.cpp
|
yulPhaser/Selections.cpp
|
||||||
yulPhaser/SimulationRNG.cpp
|
yulPhaser/SimulationRNG.cpp
|
||||||
|
|
||||||
@ -170,6 +171,7 @@ set(yul_phaser_sources
|
|||||||
../tools/yulPhaser/Phaser.cpp
|
../tools/yulPhaser/Phaser.cpp
|
||||||
../tools/yulPhaser/Population.cpp
|
../tools/yulPhaser/Population.cpp
|
||||||
../tools/yulPhaser/Program.cpp
|
../tools/yulPhaser/Program.cpp
|
||||||
|
../tools/yulPhaser/ProgramCache.cpp
|
||||||
../tools/yulPhaser/Selections.cpp
|
../tools/yulPhaser/Selections.cpp
|
||||||
../tools/yulPhaser/SimulationRNG.cpp
|
../tools/yulPhaser/SimulationRNG.cpp
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#include <tools/yulPhaser/AlgorithmRunner.h>
|
#include <tools/yulPhaser/AlgorithmRunner.h>
|
||||||
#include <tools/yulPhaser/Common.h>
|
#include <tools/yulPhaser/Common.h>
|
||||||
|
|
||||||
|
#include <liblangutil/CharStream.h>
|
||||||
|
|
||||||
#include <libsolutil/CommonIO.h>
|
#include <libsolutil/CommonIO.h>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
@ -29,6 +31,7 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace boost::unit_test::framework;
|
using namespace boost::unit_test::framework;
|
||||||
using namespace boost::test_tools;
|
using namespace boost::test_tools;
|
||||||
|
using namespace solidity::langutil;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
@ -92,7 +95,7 @@ BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest)
|
|||||||
BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture)
|
BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture)
|
||||||
{
|
{
|
||||||
m_options.maxRounds = 5;
|
m_options.maxRounds = 5;
|
||||||
AlgorithmRunner runner(Population(m_fitnessMetric), m_options, m_output);
|
AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output);
|
||||||
|
|
||||||
CountingAlgorithm algorithm;
|
CountingAlgorithm algorithm;
|
||||||
|
|
||||||
@ -112,6 +115,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, AlgorithmRunnerFixt
|
|||||||
// NOTE: Chromosomes chosen so that they're not substrings of each other and are not
|
// NOTE: Chromosomes chosen so that they're not substrings of each other and are not
|
||||||
// words likely to appear in the output in normal circumstances.
|
// words likely to appear in the output in normal circumstances.
|
||||||
Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}),
|
Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}),
|
||||||
|
{},
|
||||||
m_options,
|
m_options,
|
||||||
m_output
|
m_output
|
||||||
);
|
);
|
||||||
@ -131,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_f
|
|||||||
{
|
{
|
||||||
m_options.maxRounds = 0;
|
m_options.maxRounds = 0;
|
||||||
m_options.populationAutosaveFile = m_autosavePath;
|
m_options.populationAutosaveFile = m_autosavePath;
|
||||||
AlgorithmRunner runner(m_population, m_options, m_output);
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
assert(!fs::exists(m_autosavePath));
|
assert(!fs::exists(m_autosavePath));
|
||||||
|
|
||||||
runner.run(m_algorithm);
|
runner.run(m_algorithm);
|
||||||
@ -145,7 +149,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_save_population_to_file_if_autosave_file_spec
|
|||||||
{
|
{
|
||||||
m_options.maxRounds = 1;
|
m_options.maxRounds = 1;
|
||||||
m_options.populationAutosaveFile = m_autosavePath;
|
m_options.populationAutosaveFile = m_autosavePath;
|
||||||
AlgorithmRunner runner(m_population, m_options, m_output);
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
assert(!fs::exists(m_autosavePath));
|
assert(!fs::exists(m_autosavePath));
|
||||||
|
|
||||||
runner.run(m_algorithm);
|
runner.run(m_algorithm);
|
||||||
@ -159,7 +163,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_overwrite_existing_file_if_autosave_file_spec
|
|||||||
{
|
{
|
||||||
m_options.maxRounds = 5;
|
m_options.maxRounds = 5;
|
||||||
m_options.populationAutosaveFile = m_autosavePath;
|
m_options.populationAutosaveFile = m_autosavePath;
|
||||||
AlgorithmRunner runner(m_population, m_options, m_output);
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
assert(!fs::exists(m_autosavePath));
|
assert(!fs::exists(m_autosavePath));
|
||||||
|
|
||||||
vector<string> originalContent = {"Original content"};
|
vector<string> originalContent = {"Original content"};
|
||||||
@ -180,7 +184,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_save_population_to_file_if_autosave_file_
|
|||||||
{
|
{
|
||||||
m_options.maxRounds = 5;
|
m_options.maxRounds = 5;
|
||||||
m_options.populationAutosaveFile = nullopt;
|
m_options.populationAutosaveFile = nullopt;
|
||||||
AlgorithmRunner runner(m_population, m_options, m_output);
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
assert(!fs::exists(m_autosavePath));
|
assert(!fs::exists(m_autosavePath));
|
||||||
|
|
||||||
runner.run(m_algorithm);
|
runner.run(m_algorithm);
|
||||||
@ -198,7 +202,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_randomise_duplicate_chromosomes_if_requested,
|
|||||||
m_options.randomiseDuplicates = true;
|
m_options.randomiseDuplicates = true;
|
||||||
m_options.minChromosomeLength = 50;
|
m_options.minChromosomeLength = 50;
|
||||||
m_options.maxChromosomeLength = 50;
|
m_options.maxChromosomeLength = 50;
|
||||||
AlgorithmRunner runner(population, m_options, m_output);
|
AlgorithmRunner runner(population, {}, m_options, m_output);
|
||||||
|
|
||||||
runner.run(algorithm);
|
runner.run(algorithm);
|
||||||
|
|
||||||
@ -227,7 +231,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_re
|
|||||||
|
|
||||||
m_options.maxRounds = 1;
|
m_options.maxRounds = 1;
|
||||||
m_options.randomiseDuplicates = false;
|
m_options.randomiseDuplicates = false;
|
||||||
AlgorithmRunner runner(population, m_options, m_output);
|
AlgorithmRunner runner(population, {}, m_options, m_output);
|
||||||
|
|
||||||
runner.run(algorithm);
|
runner.run(algorithm);
|
||||||
|
|
||||||
@ -237,6 +241,33 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_re
|
|||||||
BOOST_TEST(runner.population().individuals()[2].chromosome == duplicate);
|
BOOST_TEST(runner.population().individuals()[2].chromosome == duplicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_clear_cache_at_the_beginning_and_update_it_before_each_round, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
CharStream sourceStream = CharStream("{}", current_test_case().p_name);
|
||||||
|
vector<shared_ptr<ProgramCache>> caches = {
|
||||||
|
make_shared<ProgramCache>(get<Program>(Program::load(sourceStream))),
|
||||||
|
make_shared<ProgramCache>(get<Program>(Program::load(sourceStream))),
|
||||||
|
};
|
||||||
|
|
||||||
|
m_options.maxRounds = 10;
|
||||||
|
AlgorithmRunner runner(Population(m_fitnessMetric), caches, m_options, m_output);
|
||||||
|
CountingAlgorithm algorithm;
|
||||||
|
|
||||||
|
BOOST_TEST(algorithm.m_currentRound == 0);
|
||||||
|
BOOST_TEST(caches[0]->currentRound() == 0);
|
||||||
|
BOOST_TEST(caches[1]->currentRound() == 0);
|
||||||
|
|
||||||
|
runner.run(algorithm);
|
||||||
|
BOOST_TEST(algorithm.m_currentRound == 10);
|
||||||
|
BOOST_TEST(caches[0]->currentRound() == 10);
|
||||||
|
BOOST_TEST(caches[1]->currentRound() == 10);
|
||||||
|
|
||||||
|
runner.run(algorithm);
|
||||||
|
BOOST_TEST(algorithm.m_currentRound == 20);
|
||||||
|
BOOST_TEST(caches[0]->currentRound() == 10);
|
||||||
|
BOOST_TEST(caches[1]->currentRound() == 10);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
@ -76,15 +76,16 @@ protected:
|
|||||||
Chromosome m_chromosome{vector<string>{UnusedPruner::name, EquivalentFunctionCombiner::name}};
|
Chromosome m_chromosome{vector<string>{UnusedPruner::name, EquivalentFunctionCombiner::name}};
|
||||||
Program m_program = get<Program>(Program::load(m_sourceStream));
|
Program m_program = get<Program>(Program::load(m_sourceStream));
|
||||||
Program m_optimisedProgram = optimisedProgram(m_program);
|
Program m_optimisedProgram = optimisedProgram(m_program);
|
||||||
|
shared_ptr<ProgramCache> m_programCache = make_shared<ProgramCache>(m_program);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture
|
class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
vector<shared_ptr<FitnessMetric>> m_simpleMetrics = {
|
vector<shared_ptr<FitnessMetric>> m_simpleMetrics = {
|
||||||
make_shared<ProgramSize>(m_program, 1),
|
make_shared<ProgramSize>(m_program, nullptr, 1),
|
||||||
make_shared<ProgramSize>(m_program, 2),
|
make_shared<ProgramSize>(m_program, nullptr, 2),
|
||||||
make_shared<ProgramSize>(m_program, 3),
|
make_shared<ProgramSize>(m_program, nullptr, 3),
|
||||||
};
|
};
|
||||||
vector<size_t> m_fitness = {
|
vector<size_t> m_fitness = {
|
||||||
m_simpleMetrics[0]->evaluate(m_chromosome),
|
m_simpleMetrics[0]->evaluate(m_chromosome),
|
||||||
@ -97,31 +98,66 @@ BOOST_AUTO_TEST_SUITE(Phaser)
|
|||||||
BOOST_AUTO_TEST_SUITE(FitnessMetricsTest)
|
BOOST_AUTO_TEST_SUITE(FitnessMetricsTest)
|
||||||
BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest)
|
BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest)
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program, ProgramBasedMetricFixture)
|
BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture)
|
||||||
{
|
{
|
||||||
string code = toString(DummyProgramBasedMetric(m_program).optimisedProgram(m_chromosome));
|
string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgram(m_chromosome));
|
||||||
|
|
||||||
BOOST_TEST(code != toString(m_program));
|
BOOST_TEST(code != toString(m_program));
|
||||||
BOOST_TEST(code == toString(m_optimisedProgram));
|
BOOST_TEST(code == toString(m_optimisedProgram));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramBasedMetricFixture)
|
||||||
|
{
|
||||||
|
string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgram(m_chromosome));
|
||||||
|
|
||||||
|
BOOST_TEST(code != toString(m_program));
|
||||||
|
BOOST_TEST(code == toString(m_optimisedProgram));
|
||||||
|
BOOST_TEST(m_programCache->size() == m_chromosome.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture)
|
||||||
|
{
|
||||||
|
string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgramNoCache(m_chromosome));
|
||||||
|
|
||||||
|
BOOST_TEST(code != toString(m_program));
|
||||||
|
BOOST_TEST(code == toString(m_optimisedProgram));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_not_use_cache_even_if_available, ProgramBasedMetricFixture)
|
||||||
|
{
|
||||||
|
string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgramNoCache(m_chromosome));
|
||||||
|
|
||||||
|
BOOST_TEST(code != toString(m_program));
|
||||||
|
BOOST_TEST(code == toString(m_optimisedProgram));
|
||||||
|
BOOST_TEST(m_programCache->size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
BOOST_AUTO_TEST_SUITE(ProgramSizeTest)
|
BOOST_AUTO_TEST_SUITE(ProgramSizeTest)
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture)
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture)
|
||||||
{
|
{
|
||||||
size_t fitness = ProgramSize(m_program).evaluate(m_chromosome);
|
size_t fitness = ProgramSize(m_program, nullptr).evaluate(m_chromosome);
|
||||||
|
|
||||||
BOOST_TEST(fitness != m_program.codeSize());
|
BOOST_TEST(fitness != m_program.codeSize());
|
||||||
BOOST_TEST(fitness == m_optimisedProgram.codeSize());
|
BOOST_TEST(fitness == m_optimisedProgram.codeSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture)
|
||||||
|
{
|
||||||
|
size_t fitness = ProgramSize(nullopt, m_programCache).evaluate(m_chromosome);
|
||||||
|
|
||||||
|
BOOST_TEST(fitness != m_program.codeSize());
|
||||||
|
BOOST_TEST(fitness == m_optimisedProgram.codeSize());
|
||||||
|
BOOST_TEST(m_programCache->size() == m_chromosome.length());
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture)
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture)
|
||||||
{
|
{
|
||||||
Program const& programOptimisedOnce = m_optimisedProgram;
|
Program const& programOptimisedOnce = m_optimisedProgram;
|
||||||
Program programOptimisedTwice = optimisedProgram(programOptimisedOnce);
|
Program programOptimisedTwice = optimisedProgram(programOptimisedOnce);
|
||||||
|
|
||||||
ProgramSize metric(m_program, 2);
|
ProgramSize metric(m_program, nullptr, 2);
|
||||||
size_t fitness = metric.evaluate(m_chromosome);
|
size_t fitness = metric.evaluate(m_chromosome);
|
||||||
|
|
||||||
BOOST_TEST(fitness != m_program.codeSize());
|
BOOST_TEST(fitness != m_program.codeSize());
|
||||||
@ -131,7 +167,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number
|
|||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture)
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture)
|
||||||
{
|
{
|
||||||
ProgramSize metric(m_program, 0);
|
ProgramSize metric(m_program, nullptr, 0);
|
||||||
size_t fitness = metric.evaluate(m_chromosome);
|
size_t fitness = metric.evaluate(m_chromosome);
|
||||||
|
|
||||||
BOOST_TEST(fitness == m_program.codeSize());
|
BOOST_TEST(fitness == m_program.codeSize());
|
||||||
@ -143,7 +179,13 @@ BOOST_AUTO_TEST_SUITE(RelativeProgramSizeTest)
|
|||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised_program_and_original_program, ProgramBasedMetricFixture)
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised_program_and_original_program, ProgramBasedMetricFixture)
|
||||||
{
|
{
|
||||||
BOOST_TEST(RelativeProgramSize(m_program, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize()));
|
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture)
|
||||||
|
{
|
||||||
|
BOOST_TEST(RelativeProgramSize(nullopt, m_programCache, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize()));
|
||||||
|
BOOST_TEST(m_programCache->size() == m_chromosome.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture)
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture)
|
||||||
@ -151,17 +193,17 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number
|
|||||||
Program const& programOptimisedOnce = m_optimisedProgram;
|
Program const& programOptimisedOnce = m_optimisedProgram;
|
||||||
Program programOptimisedTwice = optimisedProgram(programOptimisedOnce);
|
Program programOptimisedTwice = optimisedProgram(programOptimisedOnce);
|
||||||
|
|
||||||
RelativeProgramSize metric(m_program, 3, 2);
|
RelativeProgramSize metric(m_program, nullptr, 3, 2);
|
||||||
size_t fitness = metric.evaluate(m_chromosome);
|
size_t fitness = metric.evaluate(m_chromosome);
|
||||||
|
|
||||||
BOOST_TEST(fitness != 1000);
|
BOOST_TEST(fitness != 1000);
|
||||||
BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, 3, 1).evaluate(m_chromosome));
|
BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, nullptr, 3, 1).evaluate(m_chromosome));
|
||||||
BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize() / m_program.codeSize()));
|
BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize() / m_program.codeSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture)
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture)
|
||||||
{
|
{
|
||||||
RelativeProgramSize metric(m_program, 3, 0);
|
RelativeProgramSize metric(m_program, nullptr, 3, 0);
|
||||||
|
|
||||||
BOOST_TEST(metric.evaluate(m_chromosome) == 1000);
|
BOOST_TEST(metric.evaluate(m_chromosome) == 1000);
|
||||||
}
|
}
|
||||||
@ -171,7 +213,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_
|
|||||||
CharStream sourceStream = CharStream("{}", "");
|
CharStream sourceStream = CharStream("{}", "");
|
||||||
Program program = get<Program>(Program::load(sourceStream));
|
Program program = get<Program>(Program::load(sourceStream));
|
||||||
|
|
||||||
RelativeProgramSize metric(program, 3);
|
RelativeProgramSize metric(program, nullptr, 3);
|
||||||
|
|
||||||
BOOST_TEST(metric.evaluate(m_chromosome) == 1000);
|
BOOST_TEST(metric.evaluate(m_chromosome) == 1000);
|
||||||
BOOST_TEST(metric.evaluate(Chromosome("")) == 1000);
|
BOOST_TEST(metric.evaluate(Chromosome("")) == 1000);
|
||||||
@ -181,11 +223,11 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_
|
|||||||
BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture)
|
BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture)
|
||||||
{
|
{
|
||||||
double sizeRatio = static_cast<double>(m_optimisedProgram.codeSize()) / m_program.codeSize();
|
double sizeRatio = static_cast<double>(m_optimisedProgram.codeSize()) / m_program.codeSize();
|
||||||
BOOST_TEST(RelativeProgramSize(m_program, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio));
|
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio));
|
||||||
BOOST_TEST(RelativeProgramSize(m_program, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio));
|
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio));
|
||||||
BOOST_TEST(RelativeProgramSize(m_program, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio));
|
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio));
|
||||||
BOOST_TEST(RelativeProgramSize(m_program, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio));
|
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio));
|
||||||
BOOST_TEST(RelativeProgramSize(m_program, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio));
|
BOOST_TEST(RelativeProgramSize(m_program, nullptr, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
@ -55,7 +55,7 @@ protected:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class FitnessMetricFactoryFixture
|
class FixtureWithPrograms
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
vector<CharStream> m_sourceStreams = {
|
vector<CharStream> m_sourceStreams = {
|
||||||
@ -68,6 +68,11 @@ protected:
|
|||||||
get<Program>(Program::load(m_sourceStreams[1])),
|
get<Program>(Program::load(m_sourceStreams[1])),
|
||||||
get<Program>(Program::load(m_sourceStreams[2])),
|
get<Program>(Program::load(m_sourceStreams[2])),
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class FitnessMetricFactoryFixture: public FixtureWithPrograms
|
||||||
|
{
|
||||||
|
protected:
|
||||||
FitnessMetricFactory::Options m_options = {
|
FitnessMetricFactory::Options m_options = {
|
||||||
/* metric = */ MetricChoice::CodeSize,
|
/* metric = */ MetricChoice::CodeSize,
|
||||||
/* metricAggregator = */ MetricAggregatorChoice::Average,
|
/* metricAggregator = */ MetricAggregatorChoice::Average,
|
||||||
@ -154,7 +159,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMet
|
|||||||
{
|
{
|
||||||
m_options.metric = MetricChoice::RelativeCodeSize;
|
m_options.metric = MetricChoice::RelativeCodeSize;
|
||||||
m_options.metricAggregator = MetricAggregatorChoice::Sum;
|
m_options.metricAggregator = MetricAggregatorChoice::Sum;
|
||||||
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]});
|
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr});
|
||||||
BOOST_REQUIRE(metric != nullptr);
|
BOOST_REQUIRE(metric != nullptr);
|
||||||
|
|
||||||
auto sumMetric = dynamic_cast<FitnessMetricSum*>(metric.get());
|
auto sumMetric = dynamic_cast<FitnessMetricSum*>(metric.get());
|
||||||
@ -172,7 +177,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, Fitn
|
|||||||
m_options.metric = MetricChoice::CodeSize;
|
m_options.metric = MetricChoice::CodeSize;
|
||||||
m_options.metricAggregator = MetricAggregatorChoice::Average;
|
m_options.metricAggregator = MetricAggregatorChoice::Average;
|
||||||
m_options.chromosomeRepetitions = 5;
|
m_options.chromosomeRepetitions = 5;
|
||||||
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]});
|
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr});
|
||||||
BOOST_REQUIRE(metric != nullptr);
|
BOOST_REQUIRE(metric != nullptr);
|
||||||
|
|
||||||
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
|
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
|
||||||
@ -190,7 +195,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac
|
|||||||
m_options.metric = MetricChoice::RelativeCodeSize;
|
m_options.metric = MetricChoice::RelativeCodeSize;
|
||||||
m_options.metricAggregator = MetricAggregatorChoice::Average;
|
m_options.metricAggregator = MetricAggregatorChoice::Average;
|
||||||
m_options.relativeMetricScale = 10;
|
m_options.relativeMetricScale = 10;
|
||||||
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]});
|
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr});
|
||||||
BOOST_REQUIRE(metric != nullptr);
|
BOOST_REQUIRE(metric != nullptr);
|
||||||
|
|
||||||
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
|
auto averageMetric = dynamic_cast<FitnessMetricAverage*>(metric.get());
|
||||||
@ -205,7 +210,11 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac
|
|||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, FitnessMetricFactoryFixture)
|
BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, FitnessMetricFactoryFixture)
|
||||||
{
|
{
|
||||||
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_programs);
|
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(
|
||||||
|
m_options,
|
||||||
|
m_programs,
|
||||||
|
vector<shared_ptr<ProgramCache>>(m_programs.size(), nullptr)
|
||||||
|
);
|
||||||
BOOST_REQUIRE(metric != nullptr);
|
BOOST_REQUIRE(metric != nullptr);
|
||||||
|
|
||||||
auto combinedMetric = dynamic_cast<FitnessMetricCombination*>(metric.get());
|
auto combinedMetric = dynamic_cast<FitnessMetricCombination*>(metric.get());
|
||||||
@ -213,6 +222,31 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, Fitne
|
|||||||
BOOST_REQUIRE(combinedMetric->metrics().size() == m_programs.size());
|
BOOST_REQUIRE(combinedMetric->metrics().size() == m_programs.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_pass_program_caches_to_metrics, FitnessMetricFactoryFixture)
|
||||||
|
{
|
||||||
|
assert(m_programs.size() == 3);
|
||||||
|
vector<shared_ptr<ProgramCache>> caches = {
|
||||||
|
make_shared<ProgramCache>(m_programs[0]),
|
||||||
|
make_shared<ProgramCache>(m_programs[1]),
|
||||||
|
make_shared<ProgramCache>(m_programs[2]),
|
||||||
|
};
|
||||||
|
|
||||||
|
m_options.metric = MetricChoice::RelativeCodeSize;
|
||||||
|
unique_ptr<FitnessMetric> metric = FitnessMetricFactory::build(m_options, m_programs, caches);
|
||||||
|
BOOST_REQUIRE(metric != nullptr);
|
||||||
|
|
||||||
|
auto combinedMetric = dynamic_cast<FitnessMetricCombination*>(metric.get());
|
||||||
|
BOOST_REQUIRE(combinedMetric != nullptr);
|
||||||
|
BOOST_REQUIRE(combinedMetric->metrics().size() == caches.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < caches.size(); ++i)
|
||||||
|
{
|
||||||
|
auto programBasedMetric = dynamic_cast<ProgramBasedMetric*>(combinedMetric->metrics()[i].get());
|
||||||
|
BOOST_REQUIRE(programBasedMetric != nullptr);
|
||||||
|
BOOST_TEST(programBasedMetric->programCache() == caches[i].get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
BOOST_AUTO_TEST_SUITE(PopulationFactoryTest)
|
BOOST_AUTO_TEST_SUITE(PopulationFactoryTest)
|
||||||
|
|
||||||
@ -317,6 +351,35 @@ BOOST_FIXTURE_TEST_CASE(build_should_combine_populations_from_all_sources, Poula
|
|||||||
BOOST_TEST(count(begin, end, Individual(Chromosome("fcL"), *m_fitnessMetric)) >= 2);
|
BOOST_TEST(count(begin, end, Individual(Chromosome("fcL"), *m_fitnessMetric)) >= 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
BOOST_AUTO_TEST_SUITE(ProgramCacheFactoryTest)
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_create_cache_for_each_input_program_if_cache_enabled, FixtureWithPrograms)
|
||||||
|
{
|
||||||
|
ProgramCacheFactory::Options options{/* programCacheEnabled = */ true};
|
||||||
|
vector<shared_ptr<ProgramCache>> caches = ProgramCacheFactory::build(options, m_programs);
|
||||||
|
assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful");
|
||||||
|
|
||||||
|
BOOST_TEST(caches.size() == m_programs.size());
|
||||||
|
for (size_t i = 0; i < m_programs.size(); ++i)
|
||||||
|
{
|
||||||
|
BOOST_REQUIRE(caches[i] != nullptr);
|
||||||
|
BOOST_TEST(toString(caches[i]->program()) == toString(m_programs[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(build_should_return_nullptr_for_each_input_program_if_cache_disabled, FixtureWithPrograms)
|
||||||
|
{
|
||||||
|
ProgramCacheFactory::Options options{/* programCacheEnabled = */ false};
|
||||||
|
vector<shared_ptr<ProgramCache>> caches = ProgramCacheFactory::build(options, m_programs);
|
||||||
|
assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful");
|
||||||
|
|
||||||
|
BOOST_TEST(caches.size() == m_programs.size());
|
||||||
|
for (size_t i = 0; i < m_programs.size(); ++i)
|
||||||
|
BOOST_TEST(caches[i] == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
BOOST_AUTO_TEST_SUITE(ProgramFactoryTest)
|
BOOST_AUTO_TEST_SUITE(ProgramFactoryTest)
|
||||||
|
|
||||||
|
207
test/yulPhaser/ProgramCache.cpp
Normal file
207
test/yulPhaser/ProgramCache.cpp
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <tools/yulPhaser/ProgramCache.h>
|
||||||
|
#include <tools/yulPhaser/Chromosome.h>
|
||||||
|
|
||||||
|
#include <liblangutil/CharStream.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonIO.h>
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
|
||||||
|
namespace solidity::phaser::test
|
||||||
|
{
|
||||||
|
|
||||||
|
class ProgramCacheFixture
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
static constexpr char SampleSourceCode[] =
|
||||||
|
"{\n"
|
||||||
|
" for { let i := 0 } not(eq(i, 15)) { i := add(i, 1) }\n"
|
||||||
|
" {\n"
|
||||||
|
" let x := 1\n"
|
||||||
|
" mstore(i, 2)\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
Program optimisedProgram(Program _program, string _abbreviatedOptimisationSteps) const
|
||||||
|
{
|
||||||
|
Program result = move(_program);
|
||||||
|
result.optimise(Chromosome(_abbreviatedOptimisationSteps).optimisationSteps());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static set<string> cachedKeys(ProgramCache const& _programCache)
|
||||||
|
{
|
||||||
|
set<string> keys;
|
||||||
|
for (auto pair = _programCache.entries().begin(); pair != _programCache.entries().end(); ++pair)
|
||||||
|
keys.insert(pair->first);
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharStream m_sourceStream = CharStream(SampleSourceCode, "program-cache-test");
|
||||||
|
Program m_program = get<Program>(Program::load(m_sourceStream));
|
||||||
|
ProgramCache m_programCache{m_program};
|
||||||
|
};
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(Phaser)
|
||||||
|
BOOST_AUTO_TEST_SUITE(ProgramCacheTest)
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_apply_optimisation_steps_to_program, ProgramCacheFixture)
|
||||||
|
{
|
||||||
|
Program expectedProgram = optimisedProgram(m_program, "IuO");
|
||||||
|
assert(toString(expectedProgram) != toString(m_program));
|
||||||
|
|
||||||
|
Program cachedProgram = m_programCache.optimiseProgram("IuO");
|
||||||
|
|
||||||
|
BOOST_TEST(toString(cachedProgram) == toString(expectedProgram));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_store_programs_for_all_prefixes, ProgramCacheFixture)
|
||||||
|
{
|
||||||
|
Program programI = optimisedProgram(m_program, "I");
|
||||||
|
Program programIu = optimisedProgram(programI, "u");
|
||||||
|
Program programIuO = optimisedProgram(programIu, "O");
|
||||||
|
assert(toString(m_program) != toString(programI));
|
||||||
|
assert(toString(m_program) != toString(programIu));
|
||||||
|
assert(toString(m_program) != toString(programIuO));
|
||||||
|
assert(toString(programI) != toString(programIu));
|
||||||
|
assert(toString(programI) != toString(programIuO));
|
||||||
|
assert(toString(programIu) != toString(programIuO));
|
||||||
|
|
||||||
|
BOOST_REQUIRE(m_programCache.size() == 0);
|
||||||
|
|
||||||
|
Program cachedProgram = m_programCache.optimiseProgram("IuO");
|
||||||
|
|
||||||
|
BOOST_TEST(toString(cachedProgram) == toString(programIuO));
|
||||||
|
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "IuO"}));
|
||||||
|
BOOST_TEST(toString(*m_programCache.find("I")) == toString(programI));
|
||||||
|
BOOST_TEST(toString(*m_programCache.find("Iu")) == toString(programIu));
|
||||||
|
BOOST_TEST(toString(*m_programCache.find("IuO")) == toString(programIuO));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_repeat_the_chromosome_requested_number_of_times, ProgramCacheFixture)
|
||||||
|
{
|
||||||
|
string steps = "IuOIuO";
|
||||||
|
|
||||||
|
Program cachedProgram = m_programCache.optimiseProgram("IuO", 2);
|
||||||
|
|
||||||
|
ProgramCache cacheNoRepetitions(m_program);
|
||||||
|
Program cachedProgramNoRepetitions = cacheNoRepetitions.optimiseProgram("IuOIuO");
|
||||||
|
|
||||||
|
BOOST_TEST(toString(cachedProgram) == toString(cachedProgramNoRepetitions));
|
||||||
|
|
||||||
|
for (size_t size = 1; size <= 6; ++size)
|
||||||
|
{
|
||||||
|
BOOST_REQUIRE(m_programCache.contains(steps.substr(0, size)));
|
||||||
|
BOOST_REQUIRE(cacheNoRepetitions.contains(steps.substr(0, size)));
|
||||||
|
BOOST_TEST(
|
||||||
|
toString(*cacheNoRepetitions.find(steps.substr(0, size))) ==
|
||||||
|
toString(*m_programCache.find(steps.substr(0, size)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_reuse_the_longest_prefix_and_move_it_to_the_next_round, ProgramCacheFixture)
|
||||||
|
{
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 0);
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("Iu");
|
||||||
|
m_programCache.optimiseProgram("Ia");
|
||||||
|
m_programCache.startRound(1);
|
||||||
|
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 1);
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "Ia"}));
|
||||||
|
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0);
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("IuOI");
|
||||||
|
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "Ia", "IuO", "IuOI"}));
|
||||||
|
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 1);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 1);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("IuO")->second.roundNumber == 1);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("IuOI")->second.roundNumber == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, ProgramCacheFixture)
|
||||||
|
{
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 0);
|
||||||
|
BOOST_TEST(m_programCache.size() == 0);
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("Iu");
|
||||||
|
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 0);
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu"}));
|
||||||
|
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("a");
|
||||||
|
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 0);
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "a"}));
|
||||||
|
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0);
|
||||||
|
|
||||||
|
m_programCache.startRound(1);
|
||||||
|
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 1);
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "a"}));
|
||||||
|
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0);
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("af");
|
||||||
|
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 1);
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "a", "af"}));
|
||||||
|
BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1);
|
||||||
|
|
||||||
|
m_programCache.startRound(2);
|
||||||
|
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 2);
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"a", "af"}));
|
||||||
|
BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1);
|
||||||
|
BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1);
|
||||||
|
|
||||||
|
m_programCache.startRound(3);
|
||||||
|
|
||||||
|
BOOST_TEST(m_programCache.currentRound() == 3);
|
||||||
|
BOOST_TEST(m_programCache.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
}
|
@ -35,6 +35,8 @@ add_executable(yul-phaser
|
|||||||
yulPhaser/PairSelections.cpp
|
yulPhaser/PairSelections.cpp
|
||||||
yulPhaser/Selections.h
|
yulPhaser/Selections.h
|
||||||
yulPhaser/Selections.cpp
|
yulPhaser/Selections.cpp
|
||||||
|
yulPhaser/ProgramCache.h
|
||||||
|
yulPhaser/ProgramCache.cpp
|
||||||
yulPhaser/Program.h
|
yulPhaser/Program.h
|
||||||
yulPhaser/Program.cpp
|
yulPhaser/Program.cpp
|
||||||
yulPhaser/SimulationRNG.h
|
yulPhaser/SimulationRNG.h
|
||||||
|
@ -31,9 +31,12 @@ using namespace solidity::phaser;
|
|||||||
void AlgorithmRunner::run(GeneticAlgorithm& _algorithm)
|
void AlgorithmRunner::run(GeneticAlgorithm& _algorithm)
|
||||||
{
|
{
|
||||||
populationAutosave();
|
populationAutosave();
|
||||||
|
cacheClear();
|
||||||
|
|
||||||
for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round)
|
for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round)
|
||||||
{
|
{
|
||||||
|
cacheStartRound(round + 1);
|
||||||
|
|
||||||
m_population = _algorithm.runNextRound(m_population);
|
m_population = _algorithm.runNextRound(m_population);
|
||||||
randomiseDuplicates();
|
randomiseDuplicates();
|
||||||
|
|
||||||
@ -66,6 +69,20 @@ void AlgorithmRunner::populationAutosave() const
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlgorithmRunner::cacheClear()
|
||||||
|
{
|
||||||
|
for (auto& cache: m_programCaches)
|
||||||
|
if (cache != nullptr)
|
||||||
|
cache->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlgorithmRunner::cacheStartRound(size_t _roundNumber)
|
||||||
|
{
|
||||||
|
for (auto& cache: m_programCaches)
|
||||||
|
if (cache != nullptr)
|
||||||
|
cache->startRound(_roundNumber);
|
||||||
|
}
|
||||||
|
|
||||||
void AlgorithmRunner::randomiseDuplicates()
|
void AlgorithmRunner::randomiseDuplicates()
|
||||||
{
|
{
|
||||||
if (m_options.randomiseDuplicates)
|
if (m_options.randomiseDuplicates)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <tools/yulPhaser/GeneticAlgorithms.h>
|
#include <tools/yulPhaser/GeneticAlgorithms.h>
|
||||||
#include <tools/yulPhaser/Population.h>
|
#include <tools/yulPhaser/Population.h>
|
||||||
|
#include <tools/yulPhaser/ProgramCache.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
@ -50,10 +51,12 @@ public:
|
|||||||
|
|
||||||
AlgorithmRunner(
|
AlgorithmRunner(
|
||||||
Population _initialPopulation,
|
Population _initialPopulation,
|
||||||
|
std::vector<std::shared_ptr<ProgramCache>> _programCaches,
|
||||||
Options _options,
|
Options _options,
|
||||||
std::ostream& _outputStream
|
std::ostream& _outputStream
|
||||||
):
|
):
|
||||||
m_population(std::move(_initialPopulation)),
|
m_population(std::move(_initialPopulation)),
|
||||||
|
m_programCaches(std::move(_programCaches)),
|
||||||
m_options(std::move(_options)),
|
m_options(std::move(_options)),
|
||||||
m_outputStream(_outputStream) {}
|
m_outputStream(_outputStream) {}
|
||||||
|
|
||||||
@ -65,6 +68,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
void populationAutosave() const;
|
void populationAutosave() const;
|
||||||
void randomiseDuplicates();
|
void randomiseDuplicates();
|
||||||
|
void cacheClear();
|
||||||
|
void cacheStartRound(size_t _roundNumber);
|
||||||
|
|
||||||
static Population randomiseDuplicates(
|
static Population randomiseDuplicates(
|
||||||
Population _population,
|
Population _population,
|
||||||
size_t _minChromosomeLength,
|
size_t _minChromosomeLength,
|
||||||
@ -72,6 +78,7 @@ private:
|
|||||||
);
|
);
|
||||||
|
|
||||||
Population m_population;
|
Population m_population;
|
||||||
|
std::vector<std::shared_ptr<ProgramCache>> m_programCaches;
|
||||||
Options m_options;
|
Options m_options;
|
||||||
std::ostream& m_outputStream;
|
std::ostream& m_outputStream;
|
||||||
};
|
};
|
||||||
|
@ -17,14 +17,36 @@
|
|||||||
|
|
||||||
#include <tools/yulPhaser/FitnessMetrics.h>
|
#include <tools/yulPhaser/FitnessMetrics.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonIO.h>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace solidity::util;
|
||||||
using namespace solidity::phaser;
|
using namespace solidity::phaser;
|
||||||
|
|
||||||
Program ProgramBasedMetric::optimisedProgram(Chromosome const& _chromosome) const
|
Program const& ProgramBasedMetric::program() const
|
||||||
{
|
{
|
||||||
Program programCopy = m_program;
|
if (m_programCache == nullptr)
|
||||||
|
return m_program.value();
|
||||||
|
else
|
||||||
|
return m_programCache->program();
|
||||||
|
}
|
||||||
|
|
||||||
|
Program ProgramBasedMetric::optimisedProgram(Chromosome const& _chromosome)
|
||||||
|
{
|
||||||
|
if (m_programCache == nullptr)
|
||||||
|
return optimisedProgramNoCache(_chromosome);
|
||||||
|
|
||||||
|
return m_programCache->optimiseProgram(
|
||||||
|
toString(_chromosome),
|
||||||
|
m_repetitionCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Program ProgramBasedMetric::optimisedProgramNoCache(Chromosome const& _chromosome) const
|
||||||
|
{
|
||||||
|
Program programCopy = program();
|
||||||
for (size_t i = 0; i < m_repetitionCount; ++i)
|
for (size_t i = 0; i < m_repetitionCount; ++i)
|
||||||
programCopy.optimise(_chromosome.optimisationSteps());
|
programCopy.optimise(_chromosome.optimisationSteps());
|
||||||
|
|
||||||
|
@ -22,8 +22,10 @@
|
|||||||
|
|
||||||
#include <tools/yulPhaser/Chromosome.h>
|
#include <tools/yulPhaser/Chromosome.h>
|
||||||
#include <tools/yulPhaser/Program.h>
|
#include <tools/yulPhaser/Program.h>
|
||||||
|
#include <tools/yulPhaser/ProgramCache.h>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace solidity::phaser
|
namespace solidity::phaser
|
||||||
{
|
{
|
||||||
@ -50,7 +52,7 @@ public:
|
|||||||
* Abstract base class for fitness metrics that return values based on program size.
|
* Abstract base class for fitness metrics that return values based on program size.
|
||||||
*
|
*
|
||||||
* The class provides utilities for optimising programs according to the information stored in
|
* The class provides utilities for optimising programs according to the information stored in
|
||||||
* chromosomes.
|
* chromosomes. Allows using @a ProgramCache.
|
||||||
*
|
*
|
||||||
* It can also store weights for the @a CodeSize metric. It does not do anything with
|
* It can also store weights for the @a CodeSize metric. It does not do anything with
|
||||||
* them because it does not actually compute the code size but they are readily available for use
|
* them because it does not actually compute the code size but they are readily available for use
|
||||||
@ -60,19 +62,27 @@ class ProgramBasedMetric: public FitnessMetric
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ProgramBasedMetric(
|
explicit ProgramBasedMetric(
|
||||||
Program _program,
|
std::optional<Program> _program,
|
||||||
|
std::shared_ptr<ProgramCache> _programCache,
|
||||||
size_t _repetitionCount = 1
|
size_t _repetitionCount = 1
|
||||||
):
|
):
|
||||||
m_program(std::move(_program)),
|
m_program(std::move(_program)),
|
||||||
m_repetitionCount(_repetitionCount) {}
|
m_programCache(std::move(_programCache)),
|
||||||
|
m_repetitionCount(_repetitionCount)
|
||||||
|
{
|
||||||
|
assert(m_program.has_value() == (m_programCache == nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
Program const& program() const { return m_program; }
|
Program const& program() const;
|
||||||
|
ProgramCache const* programCache() const { return m_programCache.get(); }
|
||||||
size_t repetitionCount() const { return m_repetitionCount; }
|
size_t repetitionCount() const { return m_repetitionCount; }
|
||||||
|
|
||||||
Program optimisedProgram(Chromosome const& _chromosome) const;
|
Program optimisedProgram(Chromosome const& _chromosome);
|
||||||
|
Program optimisedProgramNoCache(Chromosome const& _chromosome) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Program m_program;
|
std::optional<Program> m_program;
|
||||||
|
std::shared_ptr<ProgramCache> m_programCache;
|
||||||
size_t m_repetitionCount;
|
size_t m_repetitionCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,11 +108,12 @@ class RelativeProgramSize: public ProgramBasedMetric
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit RelativeProgramSize(
|
explicit RelativeProgramSize(
|
||||||
Program _program,
|
std::optional<Program> _program,
|
||||||
|
std::shared_ptr<ProgramCache> _programCache,
|
||||||
size_t _fixedPointPrecision,
|
size_t _fixedPointPrecision,
|
||||||
size_t _repetitionCount = 1
|
size_t _repetitionCount = 1
|
||||||
):
|
):
|
||||||
ProgramBasedMetric(std::move(_program), _repetitionCount),
|
ProgramBasedMetric(std::move(_program), std::move(_programCache), _repetitionCount),
|
||||||
m_fixedPointPrecision(_fixedPointPrecision) {}
|
m_fixedPointPrecision(_fixedPointPrecision) {}
|
||||||
|
|
||||||
size_t fixedPointPrecision() const { return m_fixedPointPrecision; }
|
size_t fixedPointPrecision() const { return m_fixedPointPrecision; }
|
||||||
|
@ -158,9 +158,11 @@ FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po:
|
|||||||
|
|
||||||
unique_ptr<FitnessMetric> FitnessMetricFactory::build(
|
unique_ptr<FitnessMetric> FitnessMetricFactory::build(
|
||||||
Options const& _options,
|
Options const& _options,
|
||||||
vector<Program> _programs
|
vector<Program> _programs,
|
||||||
|
vector<shared_ptr<ProgramCache>> _programCaches
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
assert(_programCaches.size() == _programs.size());
|
||||||
assert(_programs.size() > 0 && "Validations should prevent this from being executed with zero files.");
|
assert(_programs.size() > 0 && "Validations should prevent this from being executed with zero files.");
|
||||||
|
|
||||||
vector<shared_ptr<FitnessMetric>> metrics;
|
vector<shared_ptr<FitnessMetric>> metrics;
|
||||||
@ -168,9 +170,10 @@ unique_ptr<FitnessMetric> FitnessMetricFactory::build(
|
|||||||
{
|
{
|
||||||
case MetricChoice::CodeSize:
|
case MetricChoice::CodeSize:
|
||||||
{
|
{
|
||||||
for (Program& program: _programs)
|
for (size_t i = 0; i < _programs.size(); ++i)
|
||||||
metrics.push_back(make_unique<ProgramSize>(
|
metrics.push_back(make_unique<ProgramSize>(
|
||||||
move(program),
|
_programCaches[i] != nullptr ? optional<Program>{} : move(_programs[i]),
|
||||||
|
move(_programCaches[i]),
|
||||||
_options.chromosomeRepetitions
|
_options.chromosomeRepetitions
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -178,9 +181,10 @@ unique_ptr<FitnessMetric> FitnessMetricFactory::build(
|
|||||||
}
|
}
|
||||||
case MetricChoice::RelativeCodeSize:
|
case MetricChoice::RelativeCodeSize:
|
||||||
{
|
{
|
||||||
for (Program& program: _programs)
|
for (size_t i = 0; i < _programs.size(); ++i)
|
||||||
metrics.push_back(make_unique<RelativeProgramSize>(
|
metrics.push_back(make_unique<RelativeProgramSize>(
|
||||||
move(program),
|
_programCaches[i] != nullptr ? optional<Program>{} : move(_programs[i]),
|
||||||
|
move(_programCaches[i]),
|
||||||
_options.relativeMetricScale,
|
_options.relativeMetricScale,
|
||||||
_options.chromosomeRepetitions
|
_options.chromosomeRepetitions
|
||||||
));
|
));
|
||||||
@ -281,6 +285,25 @@ Population PopulationFactory::buildFromFile(
|
|||||||
return buildFromStrings(readLinesFromFile(_filePath), move(_fitnessMetric));
|
return buildFromStrings(readLinesFromFile(_filePath), move(_fitnessMetric));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgramCacheFactory::Options ProgramCacheFactory::Options::fromCommandLine(po::variables_map const& _arguments)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
_arguments["program-cache"].as<bool>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<shared_ptr<ProgramCache>> ProgramCacheFactory::build(
|
||||||
|
Options const& _options,
|
||||||
|
vector<Program> _programs
|
||||||
|
)
|
||||||
|
{
|
||||||
|
vector<shared_ptr<ProgramCache>> programCaches;
|
||||||
|
for (Program& program: _programs)
|
||||||
|
programCaches.push_back(_options.programCacheEnabled ? make_shared<ProgramCache>(move(program)) : nullptr);
|
||||||
|
|
||||||
|
return programCaches;
|
||||||
|
}
|
||||||
|
|
||||||
ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments)
|
ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
@ -507,6 +530,19 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
|||||||
;
|
;
|
||||||
keywordDescription.add(metricsDescription);
|
keywordDescription.add(metricsDescription);
|
||||||
|
|
||||||
|
po::options_description cacheDescription("CACHE", lineLength, minDescriptionLength);
|
||||||
|
cacheDescription.add_options()
|
||||||
|
(
|
||||||
|
"program-cache",
|
||||||
|
po::bool_switch(),
|
||||||
|
"Enables caching of intermediate programs corresponding to chromosome prefixes.\n"
|
||||||
|
"This speeds up fitness evaluation by a lot but eats tons of memory if the chromosomes are long. "
|
||||||
|
"Disabled by default since there's currently no way to set an upper limit on memory usage but "
|
||||||
|
"highly recommended if your computer has enough RAM."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
keywordDescription.add(cacheDescription);
|
||||||
|
|
||||||
po::positional_options_description positionalDescription;
|
po::positional_options_description positionalDescription;
|
||||||
positionalDescription.add("input-files", -1);
|
positionalDescription.add("input-files", -1);
|
||||||
|
|
||||||
@ -562,12 +598,15 @@ AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map c
|
|||||||
void Phaser::runAlgorithm(po::variables_map const& _arguments)
|
void Phaser::runAlgorithm(po::variables_map const& _arguments)
|
||||||
{
|
{
|
||||||
auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments);
|
auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments);
|
||||||
|
auto cacheOptions = ProgramCacheFactory::Options::fromCommandLine(_arguments);
|
||||||
auto metricOptions = FitnessMetricFactory::Options::fromCommandLine(_arguments);
|
auto metricOptions = FitnessMetricFactory::Options::fromCommandLine(_arguments);
|
||||||
auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments);
|
auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments);
|
||||||
auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments);
|
auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments);
|
||||||
|
|
||||||
vector<Program> programs = ProgramFactory::build(programOptions);
|
vector<Program> programs = ProgramFactory::build(programOptions);
|
||||||
unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs));
|
vector<shared_ptr<ProgramCache>> programCaches = ProgramCacheFactory::build(cacheOptions, programs);
|
||||||
|
|
||||||
|
unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs), programCaches);
|
||||||
Population population = PopulationFactory::build(populationOptions, move(fitnessMetric));
|
Population population = PopulationFactory::build(populationOptions, move(fitnessMetric));
|
||||||
|
|
||||||
unique_ptr<GeneticAlgorithm> geneticAlgorithm = GeneticAlgorithmFactory::build(
|
unique_ptr<GeneticAlgorithm> geneticAlgorithm = GeneticAlgorithmFactory::build(
|
||||||
@ -575,6 +614,6 @@ void Phaser::runAlgorithm(po::variables_map const& _arguments)
|
|||||||
population.individuals().size()
|
population.individuals().size()
|
||||||
);
|
);
|
||||||
|
|
||||||
AlgorithmRunner algorithmRunner(population, buildAlgorithmRunnerOptions(_arguments), cout);
|
AlgorithmRunner algorithmRunner(population, move(programCaches), buildAlgorithmRunnerOptions(_arguments), cout);
|
||||||
algorithmRunner.run(*geneticAlgorithm);
|
algorithmRunner.run(*geneticAlgorithm);
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ class FitnessMetric;
|
|||||||
class GeneticAlgorithm;
|
class GeneticAlgorithm;
|
||||||
class Population;
|
class Population;
|
||||||
class Program;
|
class Program;
|
||||||
|
class ProgramCache;
|
||||||
|
|
||||||
enum class Algorithm
|
enum class Algorithm
|
||||||
{
|
{
|
||||||
@ -119,7 +120,8 @@ public:
|
|||||||
|
|
||||||
static std::unique_ptr<FitnessMetric> build(
|
static std::unique_ptr<FitnessMetric> build(
|
||||||
Options const& _options,
|
Options const& _options,
|
||||||
std::vector<Program> _programs
|
std::vector<Program> _programs,
|
||||||
|
std::vector<std::shared_ptr<ProgramCache>> _programCaches
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,6 +162,25 @@ public:
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and validates instances of @a ProgramCache.
|
||||||
|
*/
|
||||||
|
class ProgramCacheFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Options
|
||||||
|
{
|
||||||
|
bool programCacheEnabled;
|
||||||
|
|
||||||
|
static Options fromCommandLine(boost::program_options::variables_map const& _arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<std::shared_ptr<ProgramCache>> build(
|
||||||
|
Options const& _options,
|
||||||
|
std::vector<Program> _programs
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds and validates instances of @a Program.
|
* Builds and validates instances of @a Program.
|
||||||
*/
|
*/
|
||||||
|
94
tools/yulPhaser/ProgramCache.cpp
Normal file
94
tools/yulPhaser/ProgramCache.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <tools/yulPhaser/ProgramCache.h>
|
||||||
|
|
||||||
|
#include <libyul/optimiser/Suite.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
using namespace solidity::phaser;
|
||||||
|
|
||||||
|
Program ProgramCache::optimiseProgram(
|
||||||
|
string const& _abbreviatedOptimisationSteps,
|
||||||
|
size_t _repetitionCount
|
||||||
|
)
|
||||||
|
{
|
||||||
|
string targetOptimisations = _abbreviatedOptimisationSteps;
|
||||||
|
for (size_t i = 1; i < _repetitionCount; ++i)
|
||||||
|
targetOptimisations += _abbreviatedOptimisationSteps;
|
||||||
|
|
||||||
|
size_t prefixSize = 0;
|
||||||
|
for (size_t i = 1; i <= targetOptimisations.size(); ++i)
|
||||||
|
{
|
||||||
|
auto const& pair = m_entries.find(targetOptimisations.substr(0, i));
|
||||||
|
if (pair != m_entries.end())
|
||||||
|
{
|
||||||
|
pair->second.roundNumber = m_currentRound;
|
||||||
|
++prefixSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program intermediateProgram = (
|
||||||
|
prefixSize == 0 ?
|
||||||
|
m_program :
|
||||||
|
m_entries.at(targetOptimisations.substr(0, prefixSize)).program
|
||||||
|
);
|
||||||
|
|
||||||
|
for (size_t i = prefixSize + 1; i <= targetOptimisations.size(); ++i)
|
||||||
|
{
|
||||||
|
string stepName = OptimiserSuite::stepAbbreviationToNameMap().at(targetOptimisations[i - 1]);
|
||||||
|
intermediateProgram.optimise({stepName});
|
||||||
|
|
||||||
|
m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}});
|
||||||
|
}
|
||||||
|
|
||||||
|
return intermediateProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramCache::startRound(size_t _roundNumber)
|
||||||
|
{
|
||||||
|
assert(_roundNumber > m_currentRound);
|
||||||
|
m_currentRound = _roundNumber;
|
||||||
|
|
||||||
|
for (auto pair = m_entries.begin(); pair != m_entries.end();)
|
||||||
|
{
|
||||||
|
assert(pair->second.roundNumber < m_currentRound);
|
||||||
|
|
||||||
|
if (pair->second.roundNumber < m_currentRound - 1)
|
||||||
|
m_entries.erase(pair++);
|
||||||
|
else
|
||||||
|
++pair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramCache::clear()
|
||||||
|
{
|
||||||
|
m_entries.clear();
|
||||||
|
m_currentRound = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program const* ProgramCache::find(string const& _abbreviatedOptimisationSteps) const
|
||||||
|
{
|
||||||
|
auto const& pair = m_entries.find(_abbreviatedOptimisationSteps);
|
||||||
|
if (pair == m_entries.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return &(pair->second.program);
|
||||||
|
}
|
91
tools/yulPhaser/ProgramCache.h
Normal file
91
tools/yulPhaser/ProgramCache.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tools/yulPhaser/Program.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace solidity::phaser
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure used by @a ProgramCache to store intermediate programs and metadata associated
|
||||||
|
* with them.
|
||||||
|
*/
|
||||||
|
struct CacheEntry
|
||||||
|
{
|
||||||
|
Program program;
|
||||||
|
size_t roundNumber;
|
||||||
|
|
||||||
|
CacheEntry(Program _program, size_t _roundNumber):
|
||||||
|
program(std::move(_program)),
|
||||||
|
roundNumber(_roundNumber) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that optimises programs one step at a time which allows it to store and later reuse the
|
||||||
|
* results of the intermediate steps.
|
||||||
|
*
|
||||||
|
* The cache keeps track of the current round number and associates newly created entries with it.
|
||||||
|
* @a startRound() must be called at the beginning of a round so that entries that are too old
|
||||||
|
* can be purged. The current strategy is to store programs corresponding to all possible prefixes
|
||||||
|
* encountered in the current and the previous rounds. Entries older than that get removed to
|
||||||
|
* conserve memory.
|
||||||
|
*
|
||||||
|
* The current strategy does speed things up (about 4:1 hit:miss ratio observed in my limited
|
||||||
|
* experiments) but there's room for improvement. We could fit more useful programs in
|
||||||
|
* the cache by being more picky about which ones we choose.
|
||||||
|
*
|
||||||
|
* There is currently no way to purge entries without starting a new round. Since the programs
|
||||||
|
* take a lot of memory, this may lead to the cache eating up all the available RAM if sequences are
|
||||||
|
* long and programs large. A limiter based on entry count or total program size would be useful.
|
||||||
|
*/
|
||||||
|
class ProgramCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ProgramCache(Program _program):
|
||||||
|
m_program(std::move(_program)) {}
|
||||||
|
|
||||||
|
Program optimiseProgram(
|
||||||
|
std::string const& _abbreviatedOptimisationSteps,
|
||||||
|
size_t _repetitionCount = 1
|
||||||
|
);
|
||||||
|
void startRound(size_t _nextRoundNumber);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
size_t size() const { return m_entries.size(); }
|
||||||
|
Program const* find(std::string const& _abbreviatedOptimisationSteps) const;
|
||||||
|
bool contains(std::string const& _abbreviatedOptimisationSteps) const { return find(_abbreviatedOptimisationSteps) != nullptr; }
|
||||||
|
|
||||||
|
std::map<std::string, CacheEntry> const& entries() const { return m_entries; };
|
||||||
|
Program const& program() const { return m_program; }
|
||||||
|
size_t currentRound() const { return m_currentRound; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The best matching data structure here would be a trie of chromosome prefixes but since
|
||||||
|
// the programs are orders of magnitude larger than the prefixes, it does not really matter.
|
||||||
|
// A map should be good enough.
|
||||||
|
std::map<std::string, CacheEntry> m_entries;
|
||||||
|
|
||||||
|
Program m_program;
|
||||||
|
size_t m_currentRound = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user