diff --git a/CMakeLists.txt b/CMakeLists.txt index 81c5f96c2..c82ad7e48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" option(SOLC_STATIC_STDLIBS "Link solc against static versions of libgcc and libstdc++ on supported platforms" OFF) option(STRICT_Z3_VERSION "Use the latest version of Z3" ON) option(PEDANTIC "Enable extra warnings and pedantic build flags. Treat all warnings as errors." ON) +option(PROFILE_OPTIMIZER_STEPS "Output performance metrics for the optimiser steps." OFF) # Setup cccache. include(EthCcache) @@ -52,6 +53,11 @@ find_package(Threads) if(NOT PEDANTIC) message(WARNING "-- Pedantic build flags turned off. Warnings will not make compilation fail. This is NOT recommended in development builds.") endif() + +if (PROFILE_OPTIMIZER_STEPS) + add_definitions(-DPROFILE_OPTIMIZER_STEPS) +endif() + # Figure out what compiler and system are we using include(EthCompilerSettings) diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index c71a44001..0641328f1 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -83,9 +83,55 @@ #include #include +#ifdef PROFILE_OPTIMIZER_STEPS +#include +#include +#endif + using namespace std; using namespace solidity; using namespace solidity::yul; +#ifdef PROFILE_OPTIMIZER_STEPS +using namespace std::chrono; +#endif + +namespace +{ + +#ifdef PROFILE_OPTIMIZER_STEPS +void outputPerformanceMetrics(map const& _metrics) +{ + vector> durations(_metrics.begin(), _metrics.end()); + sort( + durations.begin(), + durations.end(), + [](pair const& _lhs, pair const& _rhs) -> bool + { + return _lhs.second < _rhs.second; + } + ); + + int64_t totalDurationInMicroseconds = 0; + for (auto&& [step, durationInMicroseconds]: durations) + totalDurationInMicroseconds += durationInMicroseconds; + + cerr << "Performance metrics of optimizer steps" << endl; + cerr << "======================================" << endl; + constexpr double microsecondsInSecond = 1000000; + for (auto&& [step, durationInMicroseconds]: durations) + { + double percentage = 100.0 * static_cast(durationInMicroseconds) / static_cast(totalDurationInMicroseconds); + double sec = static_cast(durationInMicroseconds) / microsecondsInSecond; + cerr << fmt::format("{:>7.3f}% ({} s): {}", percentage, sec, step) << endl; + } + double totalDurationInSeconds = static_cast(totalDurationInMicroseconds) / microsecondsInSecond; + cerr << "--------------------------------------" << endl; + cerr << fmt::format("{:>7}% ({:.3f} s)", 100, totalDurationInSeconds) << endl; +} +#endif + +} + void OptimiserSuite::run( Dialect const& _dialect, @@ -178,13 +224,16 @@ void OptimiserSuite::run( NameSimplifier::run(suite.m_context, ast); VarNameCleaner::run(suite.m_context, ast); +#ifdef PROFILE_OPTIMIZER_STEPS + outputPerformanceMetrics(suite.m_durationPerStepInMicroseconds); +#endif + *_object.analysisInfo = AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object); } namespace { - template map> optimiserStepCollection() { @@ -445,7 +494,14 @@ void OptimiserSuite::runSequence(std::vector const& _steps, Block& _ast) { if (m_debug == Debug::PrintStep) cout << "Running " << step << endl; +#ifdef PROFILE_OPTIMIZER_STEPS + steady_clock::time_point startTime = steady_clock::now(); +#endif allSteps().at(step)->run(m_context, _ast); +#ifdef PROFILE_OPTIMIZER_STEPS + steady_clock::time_point endTime = steady_clock::now(); + m_durationPerStepInMicroseconds[step] += duration_cast(endTime - startTime).count(); +#endif if (m_debug == Debug::PrintChanges) { // TODO should add switch to also compare variable names! diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index 2db6288c0..abed2720e 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -87,6 +87,9 @@ public: private: OptimiserStepContext& m_context; Debug m_debug; +#ifdef PROFILE_OPTIMIZER_STEPS + std::map m_durationPerStepInMicroseconds; +#endif }; }