1*e46ba31cSRiver Riddle //===- PassTiming.cpp -----------------------------------------------------===// 2*e46ba31cSRiver Riddle // 3*e46ba31cSRiver Riddle // Copyright 2019 The MLIR Authors. 4*e46ba31cSRiver Riddle // 5*e46ba31cSRiver Riddle // Licensed under the Apache License, Version 2.0 (the "License"); 6*e46ba31cSRiver Riddle // you may not use this file except in compliance with the License. 7*e46ba31cSRiver Riddle // You may obtain a copy of the License at 8*e46ba31cSRiver Riddle // 9*e46ba31cSRiver Riddle // http://www.apache.org/licenses/LICENSE-2.0 10*e46ba31cSRiver Riddle // 11*e46ba31cSRiver Riddle // Unless required by applicable law or agreed to in writing, software 12*e46ba31cSRiver Riddle // distributed under the License is distributed on an "AS IS" BASIS, 13*e46ba31cSRiver Riddle // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*e46ba31cSRiver Riddle // See the License for the specific language governing permissions and 15*e46ba31cSRiver Riddle // limitations under the License. 16*e46ba31cSRiver Riddle // ============================================================================= 17*e46ba31cSRiver Riddle 18*e46ba31cSRiver Riddle #include "mlir/Pass/PassTiming.h" 19*e46ba31cSRiver Riddle #include "PassDetail.h" 20*e46ba31cSRiver Riddle #include "llvm/ADT/SmallVector.h" 21*e46ba31cSRiver Riddle #include "llvm/ADT/Statistic.h" 22*e46ba31cSRiver Riddle #include "llvm/Support/Format.h" 23*e46ba31cSRiver Riddle #include "llvm/Support/FormatVariadic.h" 24*e46ba31cSRiver Riddle #include "llvm/Support/Timer.h" 25*e46ba31cSRiver Riddle 26*e46ba31cSRiver Riddle using namespace mlir; 27*e46ba31cSRiver Riddle using namespace mlir::detail; 28*e46ba31cSRiver Riddle 29*e46ba31cSRiver Riddle constexpr llvm::StringLiteral kPassTimingDescription = 30*e46ba31cSRiver Riddle "... Pass execution timing report ..."; 31*e46ba31cSRiver Riddle 32*e46ba31cSRiver Riddle /// Utility function to return if a pass refers to an adaptor pass. Adaptor 33*e46ba31cSRiver Riddle /// passes are those that internally execute a pipeline, such as the 34*e46ba31cSRiver Riddle /// ModuleToFunctionPassAdaptor. 35*e46ba31cSRiver Riddle static bool isAdaptorPass(Pass *pass) { 36*e46ba31cSRiver Riddle return isa<ModuleToFunctionPassAdaptor>(pass); 37*e46ba31cSRiver Riddle } 38*e46ba31cSRiver Riddle 39*e46ba31cSRiver Riddle PassTiming::PassTiming(DisplayMode displayMode) : displayMode(displayMode) {} 40*e46ba31cSRiver Riddle 41*e46ba31cSRiver Riddle PassTiming::~PassTiming() { print(); } 42*e46ba31cSRiver Riddle 43*e46ba31cSRiver Riddle /// Print out the current timing information. 44*e46ba31cSRiver Riddle void PassTiming::print() { 45*e46ba31cSRiver Riddle // Don't print anything if there is no timing data. 46*e46ba31cSRiver Riddle if (passTimers.empty() && analysisTimers.empty()) 47*e46ba31cSRiver Riddle return; 48*e46ba31cSRiver Riddle 49*e46ba31cSRiver Riddle switch (displayMode) { 50*e46ba31cSRiver Riddle case DisplayMode::List: 51*e46ba31cSRiver Riddle printResultsAsList(*llvm::CreateInfoOutputFile()); 52*e46ba31cSRiver Riddle break; 53*e46ba31cSRiver Riddle case DisplayMode::Pipeline: 54*e46ba31cSRiver Riddle printResultsAsPipeline(*llvm::CreateInfoOutputFile()); 55*e46ba31cSRiver Riddle break; 56*e46ba31cSRiver Riddle } 57*e46ba31cSRiver Riddle 58*e46ba31cSRiver Riddle // Reset and clear the timers. 59*e46ba31cSRiver Riddle for (auto &timerPair : passTimers) 60*e46ba31cSRiver Riddle timerPair.second->clear(); 61*e46ba31cSRiver Riddle for (auto &timerPair : analysisTimers) 62*e46ba31cSRiver Riddle timerPair.second->clear(); 63*e46ba31cSRiver Riddle passTimers.clear(); 64*e46ba31cSRiver Riddle analysisTimers.clear(); 65*e46ba31cSRiver Riddle } 66*e46ba31cSRiver Riddle 67*e46ba31cSRiver Riddle /// Start a new timer for the given pass. 68*e46ba31cSRiver Riddle void PassTiming::startPassTimer(Pass *pass) { 69*e46ba31cSRiver Riddle std::unique_ptr<llvm::Timer> &timer = passTimers[pass]; 70*e46ba31cSRiver Riddle 71*e46ba31cSRiver Riddle /// If the timer doesn't exist then create a new one. 72*e46ba31cSRiver Riddle if (!timer) { 73*e46ba31cSRiver Riddle auto passName = pass->getName(); 74*e46ba31cSRiver Riddle timer.reset(new llvm::Timer(passName, passName)); 75*e46ba31cSRiver Riddle } 76*e46ba31cSRiver Riddle 77*e46ba31cSRiver Riddle activePass = pass; 78*e46ba31cSRiver Riddle timer->startTimer(); 79*e46ba31cSRiver Riddle } 80*e46ba31cSRiver Riddle 81*e46ba31cSRiver Riddle /// Stop a timer for the given pass. 82*e46ba31cSRiver Riddle void PassTiming::stopPassTimer(Pass *pass) { 83*e46ba31cSRiver Riddle std::unique_ptr<llvm::Timer> &timer = passTimers[pass]; 84*e46ba31cSRiver Riddle assert(timer && "expected valid timer to stop"); 85*e46ba31cSRiver Riddle timer->stopTimer(); 86*e46ba31cSRiver Riddle activePass = nullptr; 87*e46ba31cSRiver Riddle } 88*e46ba31cSRiver Riddle 89*e46ba31cSRiver Riddle /// Start a new timer for the given analysis. 90*e46ba31cSRiver Riddle void PassTiming::startAnalysisTimer(llvm::StringRef name, AnalysisID *id) { 91*e46ba31cSRiver Riddle auto &timer = analysisTimers[std::make_pair(id, activePass)]; 92*e46ba31cSRiver Riddle 93*e46ba31cSRiver Riddle /// If the timer doesn't exist then create a new one. 94*e46ba31cSRiver Riddle if (!timer) 95*e46ba31cSRiver Riddle timer.reset(new llvm::Timer(name, Twine("(A) " + name).str())); 96*e46ba31cSRiver Riddle timer->startTimer(); 97*e46ba31cSRiver Riddle } 98*e46ba31cSRiver Riddle 99*e46ba31cSRiver Riddle /// Stop a timer for the given analysis. 100*e46ba31cSRiver Riddle void PassTiming::stopAnalysisTimer(llvm::StringRef name, AnalysisID *id) { 101*e46ba31cSRiver Riddle auto &timer = analysisTimers[std::make_pair(id, activePass)]; 102*e46ba31cSRiver Riddle assert(timer && "expected a valid timer to stop"); 103*e46ba31cSRiver Riddle timer->stopTimer(); 104*e46ba31cSRiver Riddle } 105*e46ba31cSRiver Riddle 106*e46ba31cSRiver Riddle /// Print the timing result in list mode. 107*e46ba31cSRiver Riddle void PassTiming::printResultsAsList(llvm::raw_ostream &os) { 108*e46ba31cSRiver Riddle // Build a map of timer records uniqued by the timer name. 109*e46ba31cSRiver Riddle llvm::StringMap<llvm::TimeRecord> records; 110*e46ba31cSRiver Riddle auto addTimer = [&](llvm::Timer *timer) { 111*e46ba31cSRiver Riddle auto it = records.try_emplace(timer->getName(), timer->getTotalTime()); 112*e46ba31cSRiver Riddle if (!it.second) 113*e46ba31cSRiver Riddle it.first->second += timer->getTotalTime(); 114*e46ba31cSRiver Riddle }; 115*e46ba31cSRiver Riddle 116*e46ba31cSRiver Riddle // Add all non-adaptor classes to the time records. 117*e46ba31cSRiver Riddle for (auto &timerPair : passTimers) 118*e46ba31cSRiver Riddle if (!isAdaptorPass(timerPair.first)) 119*e46ba31cSRiver Riddle addTimer(timerPair.second.get()); 120*e46ba31cSRiver Riddle 121*e46ba31cSRiver Riddle // Add the analysis timers. 122*e46ba31cSRiver Riddle for (auto &timerPair : analysisTimers) 123*e46ba31cSRiver Riddle addTimer(timerPair.second.get()); 124*e46ba31cSRiver Riddle 125*e46ba31cSRiver Riddle // Create a timer group for the records and print it out. 126*e46ba31cSRiver Riddle llvm::TimerGroup timerGroup("pass", kPassTimingDescription, records); 127*e46ba31cSRiver Riddle timerGroup.print(os); 128*e46ba31cSRiver Riddle } 129*e46ba31cSRiver Riddle 130*e46ba31cSRiver Riddle /// Utility to print the heading information for the pipeline timer. 131*e46ba31cSRiver Riddle /// Note: This is a replication of the header generated by llvm::TimerGroup. 132*e46ba31cSRiver Riddle static void printPipelineTimerHeader(llvm::raw_ostream &os, 133*e46ba31cSRiver Riddle llvm::TimeRecord &pipelineTotal) { 134*e46ba31cSRiver Riddle os << "===" << std::string(73, '-') << "===\n"; 135*e46ba31cSRiver Riddle // Figure out how many spaces to description name. 136*e46ba31cSRiver Riddle unsigned Padding = (80 - kPassTimingDescription.size()) / 2; 137*e46ba31cSRiver Riddle os.indent(Padding) << kPassTimingDescription << '\n'; 138*e46ba31cSRiver Riddle os << "===" << std::string(73, '-') << "===\n"; 139*e46ba31cSRiver Riddle 140*e46ba31cSRiver Riddle // Print the total time. 141*e46ba31cSRiver Riddle os << llvm::format( 142*e46ba31cSRiver Riddle " Total Execution Time: %5.4f seconds (%5.4f wall clock)\n\n", 143*e46ba31cSRiver Riddle pipelineTotal.getProcessTime(), pipelineTotal.getWallTime()); 144*e46ba31cSRiver Riddle 145*e46ba31cSRiver Riddle // Add the headers for each time section. 146*e46ba31cSRiver Riddle if (pipelineTotal.getUserTime()) 147*e46ba31cSRiver Riddle os << " ---User Time---"; 148*e46ba31cSRiver Riddle if (pipelineTotal.getSystemTime()) 149*e46ba31cSRiver Riddle os << " --System Time--"; 150*e46ba31cSRiver Riddle if (pipelineTotal.getProcessTime()) 151*e46ba31cSRiver Riddle os << " --User+System--"; 152*e46ba31cSRiver Riddle os << " ---Wall Time---"; 153*e46ba31cSRiver Riddle if (pipelineTotal.getMemUsed()) 154*e46ba31cSRiver Riddle os << " ---Mem---"; 155*e46ba31cSRiver Riddle os << " --- Name ---\n"; 156*e46ba31cSRiver Riddle } 157*e46ba31cSRiver Riddle 158*e46ba31cSRiver Riddle /// Print the timing result in pipeline mode. 159*e46ba31cSRiver Riddle void PassTiming::printResultsAsPipeline(llvm::raw_ostream &os) { 160*e46ba31cSRiver Riddle // Collect the total time information for each of the non-adaptor passes. 161*e46ba31cSRiver Riddle llvm::TimeRecord pipelineTotal; 162*e46ba31cSRiver Riddle for (auto &timerPair : passTimers) 163*e46ba31cSRiver Riddle if (!isAdaptorPass(timerPair.first)) 164*e46ba31cSRiver Riddle pipelineTotal += timerPair.second->getTotalTime(); 165*e46ba31cSRiver Riddle 166*e46ba31cSRiver Riddle // Collect the analysis time records for each pass. 167*e46ba31cSRiver Riddle llvm::DenseMap<Pass *, std::vector<llvm::Timer *>> passAnalyses; 168*e46ba31cSRiver Riddle for (auto &timerPair : analysisTimers) { 169*e46ba31cSRiver Riddle passAnalyses[timerPair.first.second].push_back(timerPair.second.get()); 170*e46ba31cSRiver Riddle pipelineTotal += timerPair.second->getTotalTime(); 171*e46ba31cSRiver Riddle } 172*e46ba31cSRiver Riddle // Sort each of the analysis timers. 173*e46ba31cSRiver Riddle for (auto &analysisPair : passAnalyses) { 174*e46ba31cSRiver Riddle llvm::array_pod_sort( 175*e46ba31cSRiver Riddle analysisPair.second.begin(), analysisPair.second.end(), 176*e46ba31cSRiver Riddle [](llvm::Timer *const *lhsTimer, llvm::Timer *const *rhsTimer) -> int { 177*e46ba31cSRiver Riddle return (*lhsTimer)->getDescription().compare( 178*e46ba31cSRiver Riddle (*rhsTimer)->getDescription()); 179*e46ba31cSRiver Riddle }); 180*e46ba31cSRiver Riddle } 181*e46ba31cSRiver Riddle 182*e46ba31cSRiver Riddle // Print out timing header. 183*e46ba31cSRiver Riddle printPipelineTimerHeader(os, pipelineTotal); 184*e46ba31cSRiver Riddle 185*e46ba31cSRiver Riddle // Print the formatted timing record. 186*e46ba31cSRiver Riddle unsigned currentIndent = 0; 187*e46ba31cSRiver Riddle auto printTimer = [&](llvm::StringRef name, llvm::TimeRecord timeRecord) { 188*e46ba31cSRiver Riddle timeRecord.print(pipelineTotal, os); 189*e46ba31cSRiver Riddle os.indent(currentIndent) << name; 190*e46ba31cSRiver Riddle os << "\n"; 191*e46ba31cSRiver Riddle }; 192*e46ba31cSRiver Riddle 193*e46ba31cSRiver Riddle // Utility to print the timing information for a pass and its analyses. 194*e46ba31cSRiver Riddle auto printPassTimer = [&](Pass *pass, llvm::Timer *passTimer) { 195*e46ba31cSRiver Riddle printTimer(passTimer->getDescription(), passTimer->getTotalTime()); 196*e46ba31cSRiver Riddle 197*e46ba31cSRiver Riddle // Print the computed analyses for this pass. 198*e46ba31cSRiver Riddle currentIndent += 2; 199*e46ba31cSRiver Riddle for (llvm::Timer *timer : passAnalyses[pass]) 200*e46ba31cSRiver Riddle printTimer(timer->getDescription(), timer->getTotalTime()); 201*e46ba31cSRiver Riddle currentIndent -= 2; 202*e46ba31cSRiver Riddle }; 203*e46ba31cSRiver Riddle 204*e46ba31cSRiver Riddle // Print the total execution time. 205*e46ba31cSRiver Riddle for (auto it = passTimers.begin(), e = passTimers.end(); it != e;) { 206*e46ba31cSRiver Riddle // Handle a ModuleToFunctionAdaptor pass. 207*e46ba31cSRiver Riddle if (isa<ModuleToFunctionPassAdaptor>(it->first)) { 208*e46ba31cSRiver Riddle // Print the time for this adaptor as the accumulation of each of the 209*e46ba31cSRiver Riddle // nested function passes. 210*e46ba31cSRiver Riddle llvm::TimeRecord total; 211*e46ba31cSRiver Riddle for (auto fpIt = ++it; fpIt != e && isa<FunctionPassBase>(fpIt->first);) { 212*e46ba31cSRiver Riddle total += fpIt->second->getTotalTime(); 213*e46ba31cSRiver Riddle for (llvm::Timer *timer : passAnalyses[(fpIt++)->first]) 214*e46ba31cSRiver Riddle total += timer->getTotalTime(); 215*e46ba31cSRiver Riddle } 216*e46ba31cSRiver Riddle printTimer("Function Pipeline", total); 217*e46ba31cSRiver Riddle 218*e46ba31cSRiver Riddle // Update the indent and print the time for each of the function passes 219*e46ba31cSRiver Riddle // within the pipeline. 220*e46ba31cSRiver Riddle currentIndent += 2; 221*e46ba31cSRiver Riddle for (; it != e && isa<FunctionPassBase>(it->first); ++it) 222*e46ba31cSRiver Riddle printPassTimer(it->first, it->second.get()); 223*e46ba31cSRiver Riddle currentIndent -= 2; 224*e46ba31cSRiver Riddle continue; 225*e46ba31cSRiver Riddle } 226*e46ba31cSRiver Riddle 227*e46ba31cSRiver Riddle // Otherwise, we print the pass timer directly. 228*e46ba31cSRiver Riddle printPassTimer(it->first, it->second.get()); 229*e46ba31cSRiver Riddle ++it; 230*e46ba31cSRiver Riddle } 231*e46ba31cSRiver Riddle 232*e46ba31cSRiver Riddle printTimer("Total\n", pipelineTotal); 233*e46ba31cSRiver Riddle os.flush(); 234*e46ba31cSRiver Riddle } 235