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(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)
|
||||
{
|
||||
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_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()
|
||||
|
||||
|
@ -23,6 +23,30 @@ using namespace std;
|
||||
using namespace solidity::yul;
|
||||
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(
|
||||
string const& _abbreviatedOptimisationSteps,
|
||||
size_t _repetitionCount
|
||||
@ -40,6 +64,7 @@ Program ProgramCache::optimiseProgram(
|
||||
{
|
||||
pair->second.roundNumber = m_currentRound;
|
||||
++prefixSize;
|
||||
++m_hits;
|
||||
}
|
||||
else
|
||||
break;
|
||||
@ -57,6 +82,7 @@ Program ProgramCache::optimiseProgram(
|
||||
intermediateProgram.optimise({stepName});
|
||||
|
||||
m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}});
|
||||
++m_misses;
|
||||
}
|
||||
|
||||
return intermediateProgram;
|
||||
@ -92,3 +118,34 @@ Program const* ProgramCache::find(string const& _abbreviatedOptimisationSteps) c
|
||||
|
||||
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) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* 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.
|
||||
@ -74,11 +93,16 @@ public:
|
||||
Program const* find(std::string const& _abbreviatedOptimisationSteps) const;
|
||||
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; };
|
||||
Program const& program() const { return m_program; }
|
||||
size_t currentRound() const { return m_currentRound; }
|
||||
|
||||
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 programs are orders of magnitude larger than the prefixes, it does not really matter.
|
||||
// A map should be good enough.
|
||||
@ -86,6 +110,8 @@ private:
|
||||
|
||||
Program m_program;
|
||||
size_t m_currentRound = 0;
|
||||
size_t m_hits = 0;
|
||||
size_t m_misses = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user