mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[yul-phaser] ProgramCache: Add ability to gather cache stats
This commit is contained in:
parent
d33ba54a38
commit
3e35decf2b
@ -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()
|
||||||
|
|
||||||
|
@ -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