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