mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8452 from imapp-pl/yul-phaser-more-output
[yul-phaser] More output
This commit is contained in:
commit
ff23f165f0
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <tools/yulPhaser/AlgorithmRunner.h>
|
#include <tools/yulPhaser/AlgorithmRunner.h>
|
||||||
#include <tools/yulPhaser/Common.h>
|
#include <tools/yulPhaser/Common.h>
|
||||||
|
#include <tools/yulPhaser/FitnessMetrics.h>
|
||||||
|
|
||||||
#include <liblangutil/CharStream.h>
|
#include <liblangutil/CharStream.h>
|
||||||
|
|
||||||
@ -28,6 +29,9 @@
|
|||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <boost/test/tools/output_test_stream.hpp>
|
#include <boost/test/tools/output_test_stream.hpp>
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
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;
|
||||||
@ -65,8 +69,36 @@ public:
|
|||||||
class AlgorithmRunnerFixture
|
class AlgorithmRunnerFixture
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
// NOTE: Regexes here should not contain spaces because we strip them before matching
|
||||||
|
regex RoundSummaryRegex{R"(-+ROUND\d+\[round:[0-9.]+s,total:[0-9.]+s\]-+)"};
|
||||||
|
regex InitialPopulationHeaderRegex{"-+INITIALPOPULATION-+"};
|
||||||
|
|
||||||
|
string individualPattern(Individual const& individual) const
|
||||||
|
{
|
||||||
|
ostringstream output;
|
||||||
|
output << individual.fitness << individual.chromosome;
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string topChromosomePattern(size_t roundNumber, Individual const& individual) const
|
||||||
|
{
|
||||||
|
ostringstream output;
|
||||||
|
output << roundNumber << R"(\|[0-9.]+\|)" << individualPattern(individual);
|
||||||
|
return output.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nextLineMatches(stringstream& stream, regex const& pattern) const
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
if (getline(stream, line).fail())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return regex_match(stripWhitespace(line), pattern);
|
||||||
|
}
|
||||||
|
|
||||||
shared_ptr<FitnessMetric> m_fitnessMetric = make_shared<ChromosomeLengthMetric>();
|
shared_ptr<FitnessMetric> m_fitnessMetric = make_shared<ChromosomeLengthMetric>();
|
||||||
output_test_stream m_output;
|
Population const m_population = Population::makeRandom(m_fitnessMetric, 5, 0, 20);
|
||||||
|
stringstream m_output;
|
||||||
AlgorithmRunner::Options m_options;
|
AlgorithmRunner::Options m_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -85,7 +117,6 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
TemporaryDirectory m_tempDir;
|
TemporaryDirectory m_tempDir;
|
||||||
string const m_autosavePath = m_tempDir.memberPath("population-autosave.txt");
|
string const m_autosavePath = m_tempDir.memberPath("population-autosave.txt");
|
||||||
Population const m_population = Population::makeRandom(m_fitnessMetric, 5, 0, 20);
|
|
||||||
RandomisingAlgorithm m_algorithm;
|
RandomisingAlgorithm m_algorithm;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -95,7 +126,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(m_population, {}, m_options, m_output);
|
||||||
|
|
||||||
CountingAlgorithm algorithm;
|
CountingAlgorithm algorithm;
|
||||||
|
|
||||||
@ -106,29 +137,249 @@ BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRu
|
|||||||
BOOST_TEST(algorithm.m_currentRound == 10);
|
BOOST_TEST(algorithm.m_currentRound == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, AlgorithmRunnerFixture)
|
BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, AlgorithmRunnerFixture)
|
||||||
{
|
{
|
||||||
// run() is allowed to print more but should at least print the first one
|
|
||||||
|
|
||||||
m_options.maxRounds = 1;
|
m_options.maxRounds = 1;
|
||||||
AlgorithmRunner runner(
|
m_options.showInitialPopulation = false;
|
||||||
// NOTE: Chromosomes chosen so that they're not substrings of each other and are not
|
m_options.showOnlyTopChromosome = false;
|
||||||
// words likely to appear in the output in normal circumstances.
|
m_options.showRoundInfo = true;
|
||||||
Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}),
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
{},
|
RandomisingAlgorithm algorithm;
|
||||||
m_options,
|
|
||||||
m_output
|
|
||||||
);
|
|
||||||
|
|
||||||
CountingAlgorithm algorithm;
|
runner.run(algorithm);
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex));
|
||||||
|
for (auto const& individual: runner.population().individuals())
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual))));
|
||||||
|
|
||||||
BOOST_TEST(m_output.is_empty());
|
|
||||||
runner.run(algorithm);
|
runner.run(algorithm);
|
||||||
BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(runner.population().individuals()[0].chromosome)) == 1);
|
BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex));
|
||||||
|
for (auto const& individual: runner.population().individuals())
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_summary_if_not_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showOnlyTopChromosome = false;
|
||||||
|
m_options.showRoundInfo = false;
|
||||||
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
runner.run(algorithm);
|
runner.run(algorithm);
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("")));
|
||||||
|
for (auto const& individual: runner.population().individuals())
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_not_print_population_if_its_empty, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showOnlyTopChromosome = false;
|
||||||
|
m_options.showRoundInfo = true;
|
||||||
|
AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output);
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
runner.run(algorithm);
|
runner.run(algorithm);
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_print_only_top_chromosome_if_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showOnlyTopChromosome = true;
|
||||||
|
m_options.showRoundInfo = true;
|
||||||
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
runner.run(algorithm);
|
runner.run(algorithm);
|
||||||
BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(runner.population().individuals()[0].chromosome)) == 4);
|
BOOST_TEST(nextLineMatches(m_output, regex(topChromosomePattern(1, runner.population().individuals()[0]))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_number_for_top_chromosome_if_round_info_not_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showOnlyTopChromosome = true;
|
||||||
|
m_options.showRoundInfo = false;
|
||||||
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
|
runner.run(algorithm);
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(runner.population().individuals()[0]))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_not_print_population_if_its_empty_and_only_top_chromosome_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 3;
|
||||||
|
m_options.showRoundInfo = true;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showOnlyTopChromosome = true;
|
||||||
|
AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output);
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
|
runner.run(algorithm);
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_print_initial_population_if_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 0;
|
||||||
|
m_options.showInitialPopulation = true;
|
||||||
|
m_options.showRoundInfo = false;
|
||||||
|
m_options.showOnlyTopChromosome = false;
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
|
runner.run(algorithm);
|
||||||
|
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex));
|
||||||
|
for (auto const& individual: m_population.individuals())
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_not_print_initial_population_if_not_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 0;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showRoundInfo = false;
|
||||||
|
m_options.showOnlyTopChromosome = false;
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
|
runner.run(algorithm);
|
||||||
|
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_print_whole_initial_population_even_if_only_top_chromosome_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 0;
|
||||||
|
m_options.showInitialPopulation = true;
|
||||||
|
m_options.showRoundInfo = false;
|
||||||
|
m_options.showOnlyTopChromosome = true;
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
|
AlgorithmRunner runner(m_population, {}, m_options, m_output);
|
||||||
|
runner.run(algorithm);
|
||||||
|
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex));
|
||||||
|
for (auto const& individual: m_population.individuals())
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_print_cache_stats_if_requested, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 4;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showRoundInfo = false;
|
||||||
|
m_options.showOnlyTopChromosome = true;
|
||||||
|
m_options.showCacheStats = true;
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
|
vector<CharStream> sourceStreams = {
|
||||||
|
CharStream("{mstore(10, 20)}", ""),
|
||||||
|
CharStream("{mstore(10, 20)\nsstore(10, 20)}", ""),
|
||||||
|
};
|
||||||
|
vector<Program> programs = {
|
||||||
|
get<Program>(Program::load(sourceStreams[0])),
|
||||||
|
get<Program>(Program::load(sourceStreams[1])),
|
||||||
|
};
|
||||||
|
vector<shared_ptr<ProgramCache>> caches = {
|
||||||
|
make_shared<ProgramCache>(programs[0]),
|
||||||
|
make_shared<ProgramCache>(programs[1]),
|
||||||
|
};
|
||||||
|
shared_ptr<FitnessMetric> fitnessMetric = make_shared<FitnessMetricAverage>(vector<shared_ptr<FitnessMetric>>{
|
||||||
|
make_shared<ProgramSize>(nullopt, caches[0]),
|
||||||
|
make_shared<ProgramSize>(nullopt, caches[1]),
|
||||||
|
});
|
||||||
|
Population population = Population::makeRandom(fitnessMetric, 2, 0, 5);
|
||||||
|
|
||||||
|
AlgorithmRunner runner(population, caches, m_options, m_output);
|
||||||
|
runner.run(algorithm);
|
||||||
|
|
||||||
|
BOOST_TEST(caches[0]->currentRound() == m_options.maxRounds.value());
|
||||||
|
BOOST_TEST(caches[1]->currentRound() == m_options.maxRounds.value());
|
||||||
|
|
||||||
|
CacheStats stats = caches[0]->gatherStats() + caches[1]->gatherStats();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_options.maxRounds.value() - 1; ++i)
|
||||||
|
{
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(".*")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+")));
|
||||||
|
if (i > 0)
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)")));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_REQUIRE(stats.roundEntryCounts.size() == 2);
|
||||||
|
BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value() - 1) == 1);
|
||||||
|
BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value()) == 1);
|
||||||
|
|
||||||
|
size_t round = m_options.maxRounds.value();
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(".*")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round - 1) + ":" + toString(stats.roundEntryCounts[round - 1]) + "entries")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round) + ":" + toString(stats.roundEntryCounts[round]) + "entries")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("Totalhits:" + toString(stats.hits))));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("Totalmisses:" + toString(stats.misses))));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("Sizeofcachedcode:" + toString(stats.totalCodeSize))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_print_message_if_cache_stats_requested_but_cache_disabled, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showRoundInfo = false;
|
||||||
|
m_options.showOnlyTopChromosome = true;
|
||||||
|
m_options.showCacheStats = true;
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
|
AlgorithmRunner runner(m_population, {nullptr}, m_options, m_output);
|
||||||
|
runner.run(algorithm);
|
||||||
|
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(".*")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled"))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(run_should_print_partial_stats_and_message_if_some_caches_disabled, AlgorithmRunnerFixture)
|
||||||
|
{
|
||||||
|
m_options.maxRounds = 1;
|
||||||
|
m_options.showInitialPopulation = false;
|
||||||
|
m_options.showRoundInfo = false;
|
||||||
|
m_options.showOnlyTopChromosome = true;
|
||||||
|
m_options.showCacheStats = true;
|
||||||
|
RandomisingAlgorithm algorithm;
|
||||||
|
|
||||||
|
CharStream sourceStream = CharStream("{}", "");
|
||||||
|
shared_ptr<ProgramCache> cache = make_shared<ProgramCache>(get<Program>(Program::load(sourceStream)));
|
||||||
|
|
||||||
|
AlgorithmRunner runner(m_population, {cache, nullptr}, m_options, m_output);
|
||||||
|
BOOST_REQUIRE(cache->gatherStats().roundEntryCounts.size() == 0);
|
||||||
|
|
||||||
|
runner.run(algorithm);
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(".*")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)")));
|
||||||
|
BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled for 1 out of 2 programs"))));
|
||||||
|
BOOST_TEST(m_output.peek() == EOF);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture)
|
BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture)
|
||||||
@ -250,7 +501,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_clear_cache_at_the_beginning_and_update_it_be
|
|||||||
};
|
};
|
||||||
|
|
||||||
m_options.maxRounds = 10;
|
m_options.maxRounds = 10;
|
||||||
AlgorithmRunner runner(Population(m_fitnessMetric), caches, m_options, m_output);
|
AlgorithmRunner runner(m_population, caches, m_options, m_output);
|
||||||
CountingAlgorithm algorithm;
|
CountingAlgorithm algorithm;
|
||||||
|
|
||||||
BOOST_TEST(algorithm.m_currentRound == 0);
|
BOOST_TEST(algorithm.m_currentRound == 0);
|
||||||
|
@ -71,6 +71,15 @@ protected:
|
|||||||
BOOST_AUTO_TEST_SUITE(Phaser)
|
BOOST_AUTO_TEST_SUITE(Phaser)
|
||||||
BOOST_AUTO_TEST_SUITE(ProgramCacheTest)
|
BOOST_AUTO_TEST_SUITE(ProgramCacheTest)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(CacheStats_operator_plus_should_add_stats_together)
|
||||||
|
{
|
||||||
|
CacheStats statsA{11, 12, 13, {{1, 14}, {2, 15}}};
|
||||||
|
CacheStats statsB{21, 22, 23, {{2, 24}, {3, 25}}};
|
||||||
|
CacheStats statsC{32, 34, 36, {{1, 14}, {2, 39}, {3, 25}}};
|
||||||
|
|
||||||
|
BOOST_CHECK(statsA + statsB == statsC);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_apply_optimisation_steps_to_program, ProgramCacheFixture)
|
BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_apply_optimisation_steps_to_program, ProgramCacheFixture)
|
||||||
{
|
{
|
||||||
Program expectedProgram = optimisedProgram(m_program, "IuO");
|
Program expectedProgram = optimisedProgram(m_program, "IuO");
|
||||||
@ -201,6 +210,45 @@ BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds,
|
|||||||
BOOST_TEST(m_programCache.size() == 0);
|
BOOST_TEST(m_programCache.size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_CASE(gatherStats_should_return_cache_statistics, ProgramCacheFixture)
|
||||||
|
{
|
||||||
|
size_t sizeI = optimisedProgram(m_program, "I").codeSize();
|
||||||
|
size_t sizeIu = optimisedProgram(m_program, "Iu").codeSize();
|
||||||
|
size_t sizeIuO = optimisedProgram(m_program, "IuO").codeSize();
|
||||||
|
size_t sizeL = optimisedProgram(m_program, "L").codeSize();
|
||||||
|
size_t sizeLT = optimisedProgram(m_program, "LT").codeSize();
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("L");
|
||||||
|
m_programCache.optimiseProgram("Iu");
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"L", "I", "Iu"}));
|
||||||
|
CacheStats expectedStats1{0, 3, sizeL + sizeI + sizeIu, {{0, 3}}};
|
||||||
|
BOOST_CHECK(m_programCache.gatherStats() == expectedStats1);
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("IuO");
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"L", "I", "Iu", "IuO"}));
|
||||||
|
CacheStats expectedStats2{2, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 4}}};
|
||||||
|
BOOST_CHECK(m_programCache.gatherStats() == expectedStats2);
|
||||||
|
|
||||||
|
m_programCache.startRound(1);
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"L", "I", "Iu", "IuO"}));
|
||||||
|
BOOST_CHECK(m_programCache.gatherStats() == expectedStats2);
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("IuO");
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"L", "I", "Iu", "IuO"}));
|
||||||
|
CacheStats expectedStats3{5, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 1}, {1, 3}}};
|
||||||
|
BOOST_CHECK(m_programCache.gatherStats() == expectedStats3);
|
||||||
|
|
||||||
|
m_programCache.startRound(2);
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"I", "Iu", "IuO"}));
|
||||||
|
CacheStats expectedStats4{5, 4, sizeI + sizeIu + sizeIuO, {{1, 3}}};
|
||||||
|
BOOST_CHECK(m_programCache.gatherStats() == expectedStats4);
|
||||||
|
|
||||||
|
m_programCache.optimiseProgram("LT");
|
||||||
|
BOOST_REQUIRE((cachedKeys(m_programCache) == set<string>{"L", "LT", "I", "Iu", "IuO"}));
|
||||||
|
CacheStats expectedStats5{5, 6, sizeL + sizeLT + sizeI + sizeIu + sizeIuO, {{1, 3}, {2, 2}}};
|
||||||
|
BOOST_CHECK(m_programCache.gatherStats() == expectedStats5);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
@ -31,22 +31,102 @@ using namespace solidity::phaser;
|
|||||||
void AlgorithmRunner::run(GeneticAlgorithm& _algorithm)
|
void AlgorithmRunner::run(GeneticAlgorithm& _algorithm)
|
||||||
{
|
{
|
||||||
populationAutosave();
|
populationAutosave();
|
||||||
|
printInitialPopulation();
|
||||||
cacheClear();
|
cacheClear();
|
||||||
|
|
||||||
|
clock_t totalTimeStart = clock();
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
clock_t roundTimeStart = clock();
|
||||||
cacheStartRound(round + 1);
|
cacheStartRound(round + 1);
|
||||||
|
|
||||||
m_population = _algorithm.runNextRound(m_population);
|
m_population = _algorithm.runNextRound(m_population);
|
||||||
randomiseDuplicates();
|
randomiseDuplicates();
|
||||||
|
|
||||||
m_outputStream << "---------- ROUND " << round + 1 << " ----------" << endl;
|
printRoundSummary(round, roundTimeStart, totalTimeStart);
|
||||||
m_outputStream << m_population;
|
printCacheStats();
|
||||||
|
|
||||||
populationAutosave();
|
populationAutosave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlgorithmRunner::printRoundSummary(
|
||||||
|
size_t _round,
|
||||||
|
clock_t _roundTimeStart,
|
||||||
|
clock_t _totalTimeStart
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
clock_t now = clock();
|
||||||
|
double roundTime = static_cast<double>(now - _roundTimeStart) / CLOCKS_PER_SEC;
|
||||||
|
double totalTime = static_cast<double>(now - _totalTimeStart) / CLOCKS_PER_SEC;
|
||||||
|
|
||||||
|
if (!m_options.showOnlyTopChromosome)
|
||||||
|
{
|
||||||
|
if (m_options.showRoundInfo)
|
||||||
|
{
|
||||||
|
m_outputStream << "---------- ROUND " << _round + 1;
|
||||||
|
m_outputStream << " [round: " << fixed << setprecision(1) << roundTime << " s,";
|
||||||
|
m_outputStream << " total: " << fixed << setprecision(1) << totalTime << " s]";
|
||||||
|
m_outputStream << " ----------" << endl;
|
||||||
|
}
|
||||||
|
else if (m_population.individuals().size() > 0)
|
||||||
|
m_outputStream << endl;
|
||||||
|
|
||||||
|
m_outputStream << m_population;
|
||||||
|
}
|
||||||
|
else if (m_population.individuals().size() > 0)
|
||||||
|
{
|
||||||
|
if (m_options.showRoundInfo)
|
||||||
|
{
|
||||||
|
m_outputStream << setw(5) << _round + 1 << " | ";
|
||||||
|
m_outputStream << setw(5) << fixed << setprecision(1) << totalTime << " | ";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_outputStream << m_population.individuals()[0] << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlgorithmRunner::printInitialPopulation() const
|
||||||
|
{
|
||||||
|
if (!m_options.showInitialPopulation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_outputStream << "---------- INITIAL POPULATION ----------" << endl;
|
||||||
|
m_outputStream << m_population;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlgorithmRunner::printCacheStats() const
|
||||||
|
{
|
||||||
|
if (!m_options.showCacheStats)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CacheStats totalStats{};
|
||||||
|
size_t disabledCacheCount = 0;
|
||||||
|
for (size_t i = 0; i < m_programCaches.size(); ++i)
|
||||||
|
if (m_programCaches[i] != nullptr)
|
||||||
|
totalStats += m_programCaches[i]->gatherStats();
|
||||||
|
else
|
||||||
|
++disabledCacheCount;
|
||||||
|
|
||||||
|
m_outputStream << "---------- CACHE STATS ----------" << endl;
|
||||||
|
|
||||||
|
if (disabledCacheCount < m_programCaches.size())
|
||||||
|
{
|
||||||
|
for (auto& [round, count]: totalStats.roundEntryCounts)
|
||||||
|
m_outputStream << "Round " << round << ": " << count << " entries" << endl;
|
||||||
|
m_outputStream << "Total hits: " << totalStats.hits << endl;
|
||||||
|
m_outputStream << "Total misses: " << totalStats.misses << endl;
|
||||||
|
m_outputStream << "Size of cached code: " << totalStats.totalCodeSize << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disabledCacheCount == m_programCaches.size())
|
||||||
|
m_outputStream << "Program cache disabled" << endl;
|
||||||
|
else if (disabledCacheCount > 0)
|
||||||
|
{
|
||||||
|
m_outputStream << "Program cache disabled for " << disabledCacheCount << " out of ";
|
||||||
|
m_outputStream << m_programCaches.size() << " programs" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AlgorithmRunner::populationAutosave() const
|
void AlgorithmRunner::populationAutosave() const
|
||||||
{
|
{
|
||||||
if (!m_options.populationAutosaveFile.has_value())
|
if (!m_options.populationAutosaveFile.has_value())
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <tools/yulPhaser/Population.h>
|
#include <tools/yulPhaser/Population.h>
|
||||||
#include <tools/yulPhaser/ProgramCache.h>
|
#include <tools/yulPhaser/ProgramCache.h>
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
@ -47,6 +48,10 @@ public:
|
|||||||
bool randomiseDuplicates = false;
|
bool randomiseDuplicates = false;
|
||||||
std::optional<size_t> minChromosomeLength = std::nullopt;
|
std::optional<size_t> minChromosomeLength = std::nullopt;
|
||||||
std::optional<size_t> maxChromosomeLength = std::nullopt;
|
std::optional<size_t> maxChromosomeLength = std::nullopt;
|
||||||
|
bool showInitialPopulation = false;
|
||||||
|
bool showOnlyTopChromosome = false;
|
||||||
|
bool showRoundInfo = true;
|
||||||
|
bool showCacheStats = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
AlgorithmRunner(
|
AlgorithmRunner(
|
||||||
@ -66,6 +71,13 @@ public:
|
|||||||
Population const& population() const { return m_population; }
|
Population const& population() const { return m_population; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void printRoundSummary(
|
||||||
|
size_t _round,
|
||||||
|
clock_t _roundTimeStart,
|
||||||
|
clock_t _totalTimeStart
|
||||||
|
) const;
|
||||||
|
void printInitialPopulation() const;
|
||||||
|
void printCacheStats() const;
|
||||||
void populationAutosave() const;
|
void populationAutosave() const;
|
||||||
void randomiseDuplicates();
|
void randomiseDuplicates();
|
||||||
void cacheClear();
|
void cacheClear();
|
||||||
|
@ -46,6 +46,14 @@ namespace po = boost::program_options;
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
map<PhaserMode, string> const PhaserModeToStringMap =
|
||||||
|
{
|
||||||
|
{PhaserMode::RunAlgorithm, "run-algorithm"},
|
||||||
|
{PhaserMode::PrintOptimisedPrograms, "print-optimised-programs"},
|
||||||
|
{PhaserMode::PrintOptimisedASTs, "print-optimised-asts"},
|
||||||
|
};
|
||||||
|
map<string, PhaserMode> const StringToPhaserModeMap = invertMap(PhaserModeToStringMap);
|
||||||
|
|
||||||
map<Algorithm, string> const AlgorithmToStringMap =
|
map<Algorithm, string> const AlgorithmToStringMap =
|
||||||
{
|
{
|
||||||
{Algorithm::Random, "random"},
|
{Algorithm::Random, "random"},
|
||||||
@ -71,6 +79,8 @@ map<string, MetricAggregatorChoice> const StringToMetricAggregatorChoiceMap = in
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
istream& phaser::operator>>(istream& _inputStream, PhaserMode& _phaserMode) { return deserializeChoice(_inputStream, _phaserMode, StringToPhaserModeMap); }
|
||||||
|
ostream& phaser::operator<<(ostream& _outputStream, PhaserMode _phaserMode) { return serializeChoice(_outputStream, _phaserMode, PhaserModeToStringMap); }
|
||||||
istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) { return deserializeChoice(_inputStream, _algorithm, StringToAlgorithmMap); }
|
istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) { return deserializeChoice(_inputStream, _algorithm, StringToAlgorithmMap); }
|
||||||
ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) { return serializeChoice(_outputStream, _algorithm, AlgorithmToStringMap); }
|
ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) { return serializeChoice(_outputStream, _algorithm, AlgorithmToStringMap); }
|
||||||
istream& phaser::operator>>(istream& _inputStream, MetricChoice& _metric) { return deserializeChoice(_inputStream, _metric, StringToMetricChoiceMap); }
|
istream& phaser::operator>>(istream& _inputStream, MetricChoice& _metric) { return deserializeChoice(_inputStream, _metric, StringToMetricChoiceMap); }
|
||||||
@ -348,7 +358,7 @@ void Phaser::main(int _argc, char** _argv)
|
|||||||
|
|
||||||
initialiseRNG(arguments.value());
|
initialiseRNG(arguments.value());
|
||||||
|
|
||||||
runAlgorithm(arguments.value());
|
runPhaser(arguments.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
||||||
@ -392,6 +402,12 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
|||||||
po::value<size_t>()->value_name("<NUM>"),
|
po::value<size_t>()->value_name("<NUM>"),
|
||||||
"The number of rounds after which the algorithm should stop. (default=no limit)."
|
"The number of rounds after which the algorithm should stop. (default=no limit)."
|
||||||
)
|
)
|
||||||
|
(
|
||||||
|
"mode",
|
||||||
|
po::value<PhaserMode>()->value_name("<NAME>")->default_value(PhaserMode::RunAlgorithm),
|
||||||
|
"Mode of operation. The default is to run the algorithm but you can also tell phaser "
|
||||||
|
"to do something else with its parameters, e.g. just print the optimised programs and exit."
|
||||||
|
)
|
||||||
;
|
;
|
||||||
keywordDescription.add(generalDescription);
|
keywordDescription.add(generalDescription);
|
||||||
|
|
||||||
@ -543,6 +559,36 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription()
|
|||||||
;
|
;
|
||||||
keywordDescription.add(cacheDescription);
|
keywordDescription.add(cacheDescription);
|
||||||
|
|
||||||
|
po::options_description outputDescription("OUTPUT", lineLength, minDescriptionLength);
|
||||||
|
outputDescription.add_options()
|
||||||
|
(
|
||||||
|
"show-initial-population",
|
||||||
|
po::bool_switch(),
|
||||||
|
"Print the state of the population before the algorithm starts."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"show-only-top-chromosome",
|
||||||
|
po::bool_switch(),
|
||||||
|
"Print only the best chromosome found in each round rather than the whole population."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"hide-round",
|
||||||
|
po::bool_switch(),
|
||||||
|
"Hide information about the current round (round number and elapsed time)."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"show-cache-stats",
|
||||||
|
po::bool_switch(),
|
||||||
|
"Print information about cache size and effectiveness after each round."
|
||||||
|
)
|
||||||
|
(
|
||||||
|
"show-seed",
|
||||||
|
po::bool_switch(),
|
||||||
|
"Print the selected random seed."
|
||||||
|
)
|
||||||
|
;
|
||||||
|
keywordDescription.add(outputDescription);
|
||||||
|
|
||||||
po::positional_options_description positionalDescription;
|
po::positional_options_description positionalDescription;
|
||||||
positionalDescription.add("input-files", -1);
|
positionalDescription.add("input-files", -1);
|
||||||
|
|
||||||
@ -581,6 +627,7 @@ void Phaser::initialiseRNG(po::variables_map const& _arguments)
|
|||||||
seed = SimulationRNG::generateSeed();
|
seed = SimulationRNG::generateSeed();
|
||||||
|
|
||||||
SimulationRNG::reset(seed);
|
SimulationRNG::reset(seed);
|
||||||
|
if (_arguments["show-seed"].as<bool>())
|
||||||
cout << "Random seed: " << seed << endl;
|
cout << "Random seed: " << seed << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,28 +639,80 @@ AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map c
|
|||||||
!_arguments["no-randomise-duplicates"].as<bool>(),
|
!_arguments["no-randomise-duplicates"].as<bool>(),
|
||||||
_arguments["min-chromosome-length"].as<size_t>(),
|
_arguments["min-chromosome-length"].as<size_t>(),
|
||||||
_arguments["max-chromosome-length"].as<size_t>(),
|
_arguments["max-chromosome-length"].as<size_t>(),
|
||||||
|
_arguments["show-initial-population"].as<bool>(),
|
||||||
|
_arguments["show-only-top-chromosome"].as<bool>(),
|
||||||
|
!_arguments["hide-round"].as<bool>(),
|
||||||
|
_arguments["show-cache-stats"].as<bool>(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Phaser::runAlgorithm(po::variables_map const& _arguments)
|
void Phaser::runPhaser(po::variables_map const& _arguments)
|
||||||
{
|
{
|
||||||
auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments);
|
auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments);
|
||||||
auto cacheOptions = ProgramCacheFactory::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);
|
|
||||||
|
|
||||||
vector<Program> programs = ProgramFactory::build(programOptions);
|
vector<Program> programs = ProgramFactory::build(programOptions);
|
||||||
vector<shared_ptr<ProgramCache>> programCaches = ProgramCacheFactory::build(cacheOptions, programs);
|
vector<shared_ptr<ProgramCache>> programCaches = ProgramCacheFactory::build(cacheOptions, programs);
|
||||||
|
|
||||||
unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs), programCaches);
|
unique_ptr<FitnessMetric> fitnessMetric = FitnessMetricFactory::build(metricOptions, programs, programCaches);
|
||||||
Population population = PopulationFactory::build(populationOptions, move(fitnessMetric));
|
Population population = PopulationFactory::build(populationOptions, move(fitnessMetric));
|
||||||
|
|
||||||
|
if (_arguments["mode"].as<PhaserMode>() == PhaserMode::RunAlgorithm)
|
||||||
|
runAlgorithm(_arguments, move(population), move(programCaches));
|
||||||
|
else
|
||||||
|
printOptimisedProgramsOrASTs(_arguments, population, move(programs), _arguments["mode"].as<PhaserMode>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Phaser::runAlgorithm(
|
||||||
|
po::variables_map const& _arguments,
|
||||||
|
Population _population,
|
||||||
|
vector<shared_ptr<ProgramCache>> _programCaches
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments);
|
||||||
|
|
||||||
unique_ptr<GeneticAlgorithm> geneticAlgorithm = GeneticAlgorithmFactory::build(
|
unique_ptr<GeneticAlgorithm> geneticAlgorithm = GeneticAlgorithmFactory::build(
|
||||||
algorithmOptions,
|
algorithmOptions,
|
||||||
population.individuals().size()
|
_population.individuals().size()
|
||||||
);
|
);
|
||||||
|
|
||||||
AlgorithmRunner algorithmRunner(population, move(programCaches), buildAlgorithmRunnerOptions(_arguments), cout);
|
AlgorithmRunner algorithmRunner(move(_population), move(_programCaches), buildAlgorithmRunnerOptions(_arguments), cout);
|
||||||
algorithmRunner.run(*geneticAlgorithm);
|
algorithmRunner.run(*geneticAlgorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Phaser::printOptimisedProgramsOrASTs(
|
||||||
|
po::variables_map const& _arguments,
|
||||||
|
Population const& _population,
|
||||||
|
vector<Program> _programs,
|
||||||
|
PhaserMode phaserMode
|
||||||
|
)
|
||||||
|
{
|
||||||
|
assert(phaserMode == PhaserMode::PrintOptimisedPrograms || phaserMode == PhaserMode::PrintOptimisedASTs);
|
||||||
|
assert(_programs.size() == _arguments["input-files"].as<vector<string>>().size());
|
||||||
|
|
||||||
|
if (_population.individuals().size() == 0)
|
||||||
|
{
|
||||||
|
cout << "<EMPTY POPULATION>" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> const& paths = _arguments["input-files"].as<vector<string>>();
|
||||||
|
for (auto& individual: _population.individuals())
|
||||||
|
{
|
||||||
|
cout << "Chromosome: " << individual.chromosome << endl;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _programs.size(); ++i)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < _arguments["chromosome-repetitions"].as<size_t>(); ++j)
|
||||||
|
_programs[i].optimise(individual.chromosome.optimisationSteps());
|
||||||
|
|
||||||
|
cout << "Program: " << paths[i] << endl;
|
||||||
|
if (phaserMode == PhaserMode::PrintOptimisedPrograms)
|
||||||
|
cout << _programs[i] << endl;
|
||||||
|
else
|
||||||
|
cout << _programs[i].toJson() << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -47,6 +47,13 @@ class Population;
|
|||||||
class Program;
|
class Program;
|
||||||
class ProgramCache;
|
class ProgramCache;
|
||||||
|
|
||||||
|
enum class PhaserMode
|
||||||
|
{
|
||||||
|
RunAlgorithm,
|
||||||
|
PrintOptimisedPrograms,
|
||||||
|
PrintOptimisedASTs,
|
||||||
|
};
|
||||||
|
|
||||||
enum class Algorithm
|
enum class Algorithm
|
||||||
{
|
{
|
||||||
Random,
|
Random,
|
||||||
@ -67,6 +74,8 @@ enum class MetricAggregatorChoice
|
|||||||
Minimum,
|
Minimum,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::istream& operator>>(std::istream& _inputStream, solidity::phaser::PhaserMode& _phaserMode);
|
||||||
|
std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::PhaserMode _phaserMode);
|
||||||
std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm);
|
std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm);
|
||||||
std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm);
|
std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm);
|
||||||
std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricChoice& _metric);
|
std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricChoice& _metric);
|
||||||
@ -223,7 +232,18 @@ private:
|
|||||||
static void initialiseRNG(boost::program_options::variables_map const& _arguments);
|
static void initialiseRNG(boost::program_options::variables_map const& _arguments);
|
||||||
static AlgorithmRunner::Options buildAlgorithmRunnerOptions(boost::program_options::variables_map const& _arguments);
|
static AlgorithmRunner::Options buildAlgorithmRunnerOptions(boost::program_options::variables_map const& _arguments);
|
||||||
|
|
||||||
static void runAlgorithm(boost::program_options::variables_map const& _arguments);
|
static void runPhaser(boost::program_options::variables_map const& _arguments);
|
||||||
|
static void runAlgorithm(
|
||||||
|
boost::program_options::variables_map const& _arguments,
|
||||||
|
Population _population,
|
||||||
|
std::vector<std::shared_ptr<ProgramCache>> _programCaches
|
||||||
|
);
|
||||||
|
static void printOptimisedProgramsOrASTs(
|
||||||
|
boost::program_options::variables_map const& _arguments,
|
||||||
|
Population const& _population,
|
||||||
|
std::vector<Program> _programs,
|
||||||
|
PhaserMode phaserMode
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,7 @@ ostream& operator<<(ostream& _stream, Population const& _population);
|
|||||||
|
|
||||||
ostream& phaser::operator<<(ostream& _stream, Individual const& _individual)
|
ostream& phaser::operator<<(ostream& _stream, Individual const& _individual)
|
||||||
{
|
{
|
||||||
_stream << "Fitness: " << _individual.fitness;
|
_stream << _individual.fitness << " " << _individual.chromosome;
|
||||||
_stream << ", optimisations: " << _individual.chromosome;
|
|
||||||
|
|
||||||
return _stream;
|
return _stream;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,30 @@ using namespace std;
|
|||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace solidity::phaser;
|
using namespace solidity::phaser;
|
||||||
|
|
||||||
|
CacheStats& CacheStats::operator+=(CacheStats const& _other)
|
||||||
|
{
|
||||||
|
hits += _other.hits;
|
||||||
|
misses += _other.misses;
|
||||||
|
totalCodeSize += _other.totalCodeSize;
|
||||||
|
|
||||||
|
for (auto& [round, count]: _other.roundEntryCounts)
|
||||||
|
if (roundEntryCounts.find(round) != roundEntryCounts.end())
|
||||||
|
roundEntryCounts.at(round) += count;
|
||||||
|
else
|
||||||
|
roundEntryCounts.insert({round, count});
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CacheStats::operator==(CacheStats const& _other) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
hits == _other.hits &&
|
||||||
|
misses == _other.misses &&
|
||||||
|
totalCodeSize == _other.totalCodeSize &&
|
||||||
|
roundEntryCounts == _other.roundEntryCounts;
|
||||||
|
}
|
||||||
|
|
||||||
Program ProgramCache::optimiseProgram(
|
Program ProgramCache::optimiseProgram(
|
||||||
string const& _abbreviatedOptimisationSteps,
|
string const& _abbreviatedOptimisationSteps,
|
||||||
size_t _repetitionCount
|
size_t _repetitionCount
|
||||||
@ -40,6 +64,7 @@ Program ProgramCache::optimiseProgram(
|
|||||||
{
|
{
|
||||||
pair->second.roundNumber = m_currentRound;
|
pair->second.roundNumber = m_currentRound;
|
||||||
++prefixSize;
|
++prefixSize;
|
||||||
|
++m_hits;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
@ -57,6 +82,7 @@ Program ProgramCache::optimiseProgram(
|
|||||||
intermediateProgram.optimise({stepName});
|
intermediateProgram.optimise({stepName});
|
||||||
|
|
||||||
m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}});
|
m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}});
|
||||||
|
++m_misses;
|
||||||
}
|
}
|
||||||
|
|
||||||
return intermediateProgram;
|
return intermediateProgram;
|
||||||
@ -92,3 +118,34 @@ Program const* ProgramCache::find(string const& _abbreviatedOptimisationSteps) c
|
|||||||
|
|
||||||
return &(pair->second.program);
|
return &(pair->second.program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CacheStats ProgramCache::gatherStats() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
/* hits = */ m_hits,
|
||||||
|
/* misses = */ m_misses,
|
||||||
|
/* totalCodeSize = */ calculateTotalCachedCodeSize(),
|
||||||
|
/* roundEntryCounts = */ countRoundEntries(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ProgramCache::calculateTotalCachedCodeSize() const
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
for (auto const& pair: m_entries)
|
||||||
|
size += pair.second.program.codeSize();
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<size_t, size_t> ProgramCache::countRoundEntries() const
|
||||||
|
{
|
||||||
|
map<size_t, size_t> counts;
|
||||||
|
for (auto& pair: m_entries)
|
||||||
|
if (counts.find(pair.second.roundNumber) != counts.end())
|
||||||
|
++counts.at(pair.second.roundNumber);
|
||||||
|
else
|
||||||
|
counts.insert({pair.second.roundNumber, 1});
|
||||||
|
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
@ -39,6 +39,23 @@ struct CacheEntry
|
|||||||
roundNumber(_roundNumber) {}
|
roundNumber(_roundNumber) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores statistics about current cache usage.
|
||||||
|
*/
|
||||||
|
struct CacheStats
|
||||||
|
{
|
||||||
|
size_t hits;
|
||||||
|
size_t misses;
|
||||||
|
size_t totalCodeSize;
|
||||||
|
std::map<size_t, size_t> roundEntryCounts;
|
||||||
|
|
||||||
|
CacheStats& operator+=(CacheStats const& _other);
|
||||||
|
CacheStats operator+(CacheStats const& _other) const { return CacheStats(*this) += _other; }
|
||||||
|
|
||||||
|
bool operator==(CacheStats const& _other) const;
|
||||||
|
bool operator!=(CacheStats const& _other) const { return !(*this == _other); }
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that optimises programs one step at a time which allows it to store and later reuse the
|
* Class that optimises programs one step at a time which allows it to store and later reuse the
|
||||||
* results of the intermediate steps.
|
* results of the intermediate steps.
|
||||||
@ -49,6 +66,8 @@ struct CacheEntry
|
|||||||
* encountered in the current and the previous rounds. Entries older than that get removed to
|
* encountered in the current and the previous rounds. Entries older than that get removed to
|
||||||
* conserve memory.
|
* conserve memory.
|
||||||
*
|
*
|
||||||
|
* @a gatherStats() allows getting statistics useful for determining cache effectiveness.
|
||||||
|
*
|
||||||
* The current strategy does speed things up (about 4:1 hit:miss ratio observed in my limited
|
* 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
|
* 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.
|
* the cache by being more picky about which ones we choose.
|
||||||
@ -74,11 +93,16 @@ public:
|
|||||||
Program const* find(std::string const& _abbreviatedOptimisationSteps) const;
|
Program const* find(std::string const& _abbreviatedOptimisationSteps) const;
|
||||||
bool contains(std::string const& _abbreviatedOptimisationSteps) const { return find(_abbreviatedOptimisationSteps) != nullptr; }
|
bool contains(std::string const& _abbreviatedOptimisationSteps) const { return find(_abbreviatedOptimisationSteps) != nullptr; }
|
||||||
|
|
||||||
|
CacheStats gatherStats() const;
|
||||||
|
|
||||||
std::map<std::string, CacheEntry> const& entries() const { return m_entries; };
|
std::map<std::string, CacheEntry> const& entries() const { return m_entries; };
|
||||||
Program const& program() const { return m_program; }
|
Program const& program() const { return m_program; }
|
||||||
size_t currentRound() const { return m_currentRound; }
|
size_t currentRound() const { return m_currentRound; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
size_t calculateTotalCachedCodeSize() const;
|
||||||
|
std::map<size_t, size_t> countRoundEntries() const;
|
||||||
|
|
||||||
// The best matching data structure here would be a trie of chromosome prefixes but since
|
// 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.
|
// the programs are orders of magnitude larger than the prefixes, it does not really matter.
|
||||||
// A map should be good enough.
|
// A map should be good enough.
|
||||||
@ -86,6 +110,8 @@ private:
|
|||||||
|
|
||||||
Program m_program;
|
Program m_program;
|
||||||
size_t m_currentRound = 0;
|
size_t m_currentRound = 0;
|
||||||
|
size_t m_hits = 0;
|
||||||
|
size_t m_misses = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user