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" 196558f80cSRiver Riddle #include "mlir/Pass/PassManager.h" 206558f80cSRiver 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" 25*8e7b683dSRiver Riddle #include <chrono> 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 336558f80cSRiver Riddle namespace { 34*8e7b683dSRiver Riddle struct Timer { 35*8e7b683dSRiver Riddle explicit Timer(std::string &&name) : name(std::move(name)) {} 36*8e7b683dSRiver Riddle 37*8e7b683dSRiver Riddle /// Start the timer. 38*8e7b683dSRiver Riddle void start() { startTime = std::chrono::system_clock::now(); } 39*8e7b683dSRiver Riddle 40*8e7b683dSRiver Riddle /// Stop the timer. 41*8e7b683dSRiver Riddle void stop() { total += (std::chrono::system_clock::now() - startTime); } 42*8e7b683dSRiver Riddle 43*8e7b683dSRiver Riddle /// Get or create a child timer with the provided name and id. 44*8e7b683dSRiver Riddle Timer *getChildTimer(const void *id, 45*8e7b683dSRiver Riddle std::function<std::string()> &&nameBuilder) { 46*8e7b683dSRiver Riddle auto &child = children[id]; 47*8e7b683dSRiver Riddle if (!child) 48*8e7b683dSRiver Riddle child.reset(new Timer(nameBuilder())); 49*8e7b683dSRiver Riddle return child.get(); 50*8e7b683dSRiver Riddle } 51*8e7b683dSRiver Riddle 52*8e7b683dSRiver Riddle /// Returns the total time for this timer in seconds. 53*8e7b683dSRiver Riddle double getTotalTime() { 54*8e7b683dSRiver Riddle // If the total has a count, then we directly compute the seconds. 55*8e7b683dSRiver Riddle if (total.count()) { 56*8e7b683dSRiver Riddle return std::chrono::duration_cast<std::chrono::duration<double>>(total) 57*8e7b683dSRiver Riddle .count(); 58*8e7b683dSRiver Riddle } 59*8e7b683dSRiver Riddle 60*8e7b683dSRiver Riddle // Otheriwse, accumulate the timing from each of the children. 61*8e7b683dSRiver Riddle double totalTime = 0.0; 62*8e7b683dSRiver Riddle for (auto &child : children) 63*8e7b683dSRiver Riddle totalTime += child.second->getTotalTime(); 64*8e7b683dSRiver Riddle return totalTime; 65*8e7b683dSRiver Riddle } 66*8e7b683dSRiver Riddle 67*8e7b683dSRiver Riddle /// Raw timing information. 68*8e7b683dSRiver Riddle std::chrono::time_point<std::chrono::high_resolution_clock> startTime; 69*8e7b683dSRiver Riddle std::chrono::nanoseconds total = std::chrono::nanoseconds(0); 70*8e7b683dSRiver Riddle 71*8e7b683dSRiver Riddle /// A map of unique identifiers to child timers. 72*8e7b683dSRiver Riddle llvm::MapVector<const void *, std::unique_ptr<Timer>> children; 73*8e7b683dSRiver Riddle 74*8e7b683dSRiver Riddle /// A descriptive name for this timer. 75*8e7b683dSRiver Riddle std::string name; 76*8e7b683dSRiver Riddle }; 77*8e7b683dSRiver Riddle 786558f80cSRiver Riddle struct PassTiming : public PassInstrumentation { 796558f80cSRiver Riddle PassTiming(PassTimingDisplayMode displayMode) : displayMode(displayMode) {} 806558f80cSRiver Riddle ~PassTiming() { print(); } 81e46ba31cSRiver Riddle 826558f80cSRiver Riddle /// Setup the instrumentation hooks. 836558f80cSRiver Riddle void runBeforePass(Pass *pass, const llvm::Any &) override { 846558f80cSRiver Riddle startPassTimer(pass); 856558f80cSRiver Riddle } 86*8e7b683dSRiver Riddle void runAfterPass(Pass *pass, const llvm::Any &) override; 87*8e7b683dSRiver Riddle void runAfterPassFailed(Pass *pass, const llvm::Any &ir) override { 88*8e7b683dSRiver Riddle runAfterPass(pass, ir); 896558f80cSRiver Riddle } 906558f80cSRiver Riddle void runBeforeAnalysis(llvm::StringRef name, AnalysisID *id, 916558f80cSRiver Riddle const llvm::Any &) override { 926558f80cSRiver Riddle startAnalysisTimer(name, id); 936558f80cSRiver Riddle } 94*8e7b683dSRiver Riddle void runAfterAnalysis(llvm::StringRef, AnalysisID *, 95*8e7b683dSRiver Riddle const llvm::Any &) override; 966558f80cSRiver Riddle 976558f80cSRiver Riddle /// Print and clear the timing results. 986558f80cSRiver Riddle void print(); 996558f80cSRiver Riddle 1006558f80cSRiver Riddle /// Start a new timer for the given pass. 1016558f80cSRiver Riddle void startPassTimer(Pass *pass); 1026558f80cSRiver Riddle 1036558f80cSRiver Riddle /// Start a new timer for the given analysis. 1046558f80cSRiver Riddle void startAnalysisTimer(llvm::StringRef name, AnalysisID *id); 1056558f80cSRiver Riddle 106*8e7b683dSRiver Riddle /// Stop a pass timer. 107*8e7b683dSRiver Riddle void stopPassTimer(Pass *pass); 108*8e7b683dSRiver Riddle 109*8e7b683dSRiver Riddle /// Stop the last active timer. 110*8e7b683dSRiver Riddle void stopTimer(); 1116558f80cSRiver Riddle 1126558f80cSRiver Riddle /// Print the timing result in list mode. 113*8e7b683dSRiver Riddle void printResultsAsList(llvm::raw_ostream &os, double totalTime); 1146558f80cSRiver Riddle 1156558f80cSRiver Riddle /// Print the timing result in pipeline mode. 116*8e7b683dSRiver Riddle void printResultsAsPipeline(llvm::raw_ostream &os, double totalTime); 1176558f80cSRiver Riddle 118*8e7b683dSRiver Riddle /// Returns a timer for the provided identifier and name. 119*8e7b683dSRiver Riddle Timer *getTimer(const void *id, std::function<std::string()> &&nameBuilder) { 120*8e7b683dSRiver Riddle if (activeTimers.empty()) 121*8e7b683dSRiver Riddle return rootTimer.getChildTimer(id, std::move(nameBuilder)); 122*8e7b683dSRiver Riddle return activeTimers.back()->getChildTimer(id, std::move(nameBuilder)); 123*8e7b683dSRiver Riddle } 1246558f80cSRiver Riddle 125*8e7b683dSRiver Riddle /// The root top level timer. 126*8e7b683dSRiver Riddle Timer rootTimer = Timer("root"); 1276558f80cSRiver Riddle 128*8e7b683dSRiver Riddle /// A stack of the currently active pass timers. 129*8e7b683dSRiver Riddle SmallVector<Timer *, 4> activeTimers; 1306558f80cSRiver Riddle 1316558f80cSRiver Riddle /// The display mode to use when printing the timing results. 1326558f80cSRiver Riddle PassTimingDisplayMode displayMode; 1336558f80cSRiver Riddle }; 1346558f80cSRiver Riddle } // end anonymous namespace 135e46ba31cSRiver Riddle 136e46ba31cSRiver Riddle /// Start a new timer for the given pass. 137e46ba31cSRiver Riddle void PassTiming::startPassTimer(Pass *pass) { 138*8e7b683dSRiver Riddle Timer *timer = getTimer(pass, [pass] { 139*8e7b683dSRiver Riddle if (isa<ModuleToFunctionPassAdaptor>(pass)) 140*8e7b683dSRiver Riddle return StringRef("Function Pipeline"); 141*8e7b683dSRiver Riddle return pass->getName(); 142*8e7b683dSRiver Riddle }); 143*8e7b683dSRiver Riddle activeTimers.push_back(timer); 144e46ba31cSRiver Riddle 145*8e7b683dSRiver Riddle // We don't actually want to time the adaptor passes, they gather their total 146*8e7b683dSRiver Riddle // from their held passes. 147*8e7b683dSRiver Riddle if (!isAdaptorPass(pass)) 148*8e7b683dSRiver Riddle timer->start(); 149e46ba31cSRiver Riddle } 150e46ba31cSRiver Riddle 151e46ba31cSRiver Riddle /// Start a new timer for the given analysis. 152e46ba31cSRiver Riddle void PassTiming::startAnalysisTimer(llvm::StringRef name, AnalysisID *id) { 153*8e7b683dSRiver Riddle Timer *timer = getTimer(id, [name] { return "(A) " + name.str(); }); 154*8e7b683dSRiver Riddle activeTimers.push_back(timer); 155*8e7b683dSRiver Riddle timer->start(); 156e46ba31cSRiver Riddle } 157e46ba31cSRiver Riddle 158*8e7b683dSRiver Riddle /// Stop a pass timer. 159*8e7b683dSRiver Riddle void PassTiming::runAfterPass(Pass *pass, const llvm::Any &) { 160*8e7b683dSRiver Riddle assert(!activeTimers.empty() && "expected active timer"); 161*8e7b683dSRiver Riddle Timer *timer = activeTimers.pop_back_val(); 162*8e7b683dSRiver Riddle 163*8e7b683dSRiver Riddle // Adapator passes aren't timed directly, so we don't need to stop their 164*8e7b683dSRiver Riddle // timers. 165*8e7b683dSRiver Riddle if (!isAdaptorPass(pass)) 166*8e7b683dSRiver Riddle timer->stop(); 167e46ba31cSRiver Riddle } 168e46ba31cSRiver Riddle 169*8e7b683dSRiver Riddle /// Stop a timer. 170*8e7b683dSRiver Riddle void PassTiming::runAfterAnalysis(llvm::StringRef, AnalysisID *, 171*8e7b683dSRiver Riddle const llvm::Any &) { 172*8e7b683dSRiver Riddle assert(!activeTimers.empty() && "expected active timer"); 173*8e7b683dSRiver Riddle Timer *timer = activeTimers.pop_back_val(); 174*8e7b683dSRiver Riddle timer->stop(); 175e46ba31cSRiver Riddle } 176e46ba31cSRiver Riddle 177*8e7b683dSRiver Riddle /// Utility to print the timer heading information. 178*8e7b683dSRiver Riddle static void printTimerHeader(llvm::raw_ostream &os, double total) { 179e46ba31cSRiver Riddle os << "===" << std::string(73, '-') << "===\n"; 180e46ba31cSRiver Riddle // Figure out how many spaces to description name. 181e46ba31cSRiver Riddle unsigned Padding = (80 - kPassTimingDescription.size()) / 2; 182e46ba31cSRiver Riddle os.indent(Padding) << kPassTimingDescription << '\n'; 183e46ba31cSRiver Riddle os << "===" << std::string(73, '-') << "===\n"; 184e46ba31cSRiver Riddle 185*8e7b683dSRiver Riddle // Print the total time followed by the section headers. 186*8e7b683dSRiver Riddle os << llvm::format(" Total Execution Time: %5.4f seconds\n\n", total); 187*8e7b683dSRiver Riddle os << " ---Wall Time--- --- Name ---\n"; 188*8e7b683dSRiver Riddle } 189e46ba31cSRiver Riddle 190*8e7b683dSRiver Riddle /// Utility to print a single line entry in the timer output. 191*8e7b683dSRiver Riddle static void printTimeEntry(raw_ostream &os, unsigned indent, StringRef name, 192*8e7b683dSRiver Riddle double time, double totalTime) { 193*8e7b683dSRiver Riddle os << llvm::format(" %7.4f (%5.1f%%) ", time, 100.0 * time / totalTime); 194*8e7b683dSRiver Riddle os.indent(indent) << name << "\n"; 195*8e7b683dSRiver Riddle } 196*8e7b683dSRiver Riddle 197*8e7b683dSRiver Riddle /// Print out the current timing information. 198*8e7b683dSRiver Riddle void PassTiming::print() { 199*8e7b683dSRiver Riddle // Don't print anything if there is no timing data. 200*8e7b683dSRiver Riddle if (rootTimer.children.empty()) 201*8e7b683dSRiver Riddle return; 202*8e7b683dSRiver Riddle auto os = llvm::CreateInfoOutputFile(); 203*8e7b683dSRiver Riddle 204*8e7b683dSRiver Riddle // Print the timer header. 205*8e7b683dSRiver Riddle double totalTime = rootTimer.getTotalTime(); 206*8e7b683dSRiver Riddle printTimerHeader(*os, totalTime); 207*8e7b683dSRiver Riddle 208*8e7b683dSRiver Riddle // Defer to a specialized printer for each display mode. 209*8e7b683dSRiver Riddle switch (displayMode) { 210*8e7b683dSRiver Riddle case PassTimingDisplayMode::List: 211*8e7b683dSRiver Riddle printResultsAsList(*os, totalTime); 212*8e7b683dSRiver Riddle break; 213*8e7b683dSRiver Riddle case PassTimingDisplayMode::Pipeline: 214*8e7b683dSRiver Riddle printResultsAsPipeline(*os, totalTime); 215*8e7b683dSRiver Riddle break; 216*8e7b683dSRiver Riddle } 217*8e7b683dSRiver Riddle printTimeEntry(*os, 0, "Total", totalTime, totalTime); 218*8e7b683dSRiver Riddle os->flush(); 219*8e7b683dSRiver Riddle 220*8e7b683dSRiver Riddle // Reset root timer. 221*8e7b683dSRiver Riddle rootTimer.children.clear(); 222*8e7b683dSRiver Riddle } 223*8e7b683dSRiver Riddle 224*8e7b683dSRiver Riddle /// Print the timing result in list mode. 225*8e7b683dSRiver Riddle void PassTiming::printResultsAsList(llvm::raw_ostream &os, double totalTime) { 226*8e7b683dSRiver Riddle llvm::StringMap<double> mergedTimings; 227*8e7b683dSRiver Riddle 228*8e7b683dSRiver Riddle std::function<void(Timer *)> addTimer = [&](Timer *timer) { 229*8e7b683dSRiver Riddle // Check for timing information. 230*8e7b683dSRiver Riddle if (timer->total.count()) 231*8e7b683dSRiver Riddle mergedTimings[timer->name] += timer->getTotalTime(); 232*8e7b683dSRiver Riddle for (auto &children : timer->children) 233*8e7b683dSRiver Riddle addTimer(children.second.get()); 234*8e7b683dSRiver Riddle }; 235*8e7b683dSRiver Riddle 236*8e7b683dSRiver Riddle // Add each of the top level timers. 237*8e7b683dSRiver Riddle for (auto &topLevelTimer : rootTimer.children) 238*8e7b683dSRiver Riddle addTimer(topLevelTimer.second.get()); 239*8e7b683dSRiver Riddle 240*8e7b683dSRiver Riddle // Sort the timing information. 241*8e7b683dSRiver Riddle std::vector<std::pair<StringRef, double>> timerNameAndTime; 242*8e7b683dSRiver Riddle for (auto &it : mergedTimings) 243*8e7b683dSRiver Riddle timerNameAndTime.emplace_back(it.first(), it.second); 244*8e7b683dSRiver Riddle llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(), 245*8e7b683dSRiver Riddle [](const std::pair<StringRef, double> *lhs, 246*8e7b683dSRiver Riddle const std::pair<StringRef, double> *rhs) { 247*8e7b683dSRiver Riddle return llvm::array_pod_sort_comparator<double>( 248*8e7b683dSRiver Riddle &rhs->second, &lhs->second); 249*8e7b683dSRiver Riddle }); 250*8e7b683dSRiver Riddle 251*8e7b683dSRiver Riddle // Print the timing information sequentially. 252*8e7b683dSRiver Riddle for (auto &timeData : timerNameAndTime) 253*8e7b683dSRiver Riddle printTimeEntry(os, 0, timeData.first, timeData.second, totalTime); 254e46ba31cSRiver Riddle } 255e46ba31cSRiver Riddle 256e46ba31cSRiver Riddle /// Print the timing result in pipeline mode. 257*8e7b683dSRiver Riddle void PassTiming::printResultsAsPipeline(llvm::raw_ostream &os, 258*8e7b683dSRiver Riddle double totalTime) { 259*8e7b683dSRiver Riddle std::function<void(unsigned, Timer *)> printTimer = [&](unsigned indent, 260*8e7b683dSRiver Riddle Timer *timer) { 261*8e7b683dSRiver Riddle // Check for timing information. 262*8e7b683dSRiver Riddle printTimeEntry(os, indent, timer->name, timer->getTotalTime(), totalTime); 263*8e7b683dSRiver Riddle for (auto &children : timer->children) 264*8e7b683dSRiver Riddle printTimer(indent + 2, children.second.get()); 265e46ba31cSRiver Riddle }; 266e46ba31cSRiver Riddle 267*8e7b683dSRiver Riddle // Print each of the top level timers. 268*8e7b683dSRiver Riddle for (auto &topLevelTimer : rootTimer.children) 269*8e7b683dSRiver Riddle printTimer(0, topLevelTimer.second.get()); 270e46ba31cSRiver Riddle } 2716558f80cSRiver Riddle 2726558f80cSRiver Riddle //===----------------------------------------------------------------------===// 2736558f80cSRiver Riddle // PassManager 2746558f80cSRiver Riddle //===----------------------------------------------------------------------===// 2756558f80cSRiver Riddle 2766558f80cSRiver Riddle /// Add an instrumentation to time the execution of passes and the computation 2776558f80cSRiver Riddle /// of analyses. 2786558f80cSRiver Riddle void PassManager::enableTiming(PassTimingDisplayMode displayMode) { 2796558f80cSRiver Riddle // Check if pass timing is already enabled. 2806558f80cSRiver Riddle if (passTiming) 2816558f80cSRiver Riddle return; 2826558f80cSRiver Riddle addInstrumentation(new PassTiming(displayMode)); 2836558f80cSRiver Riddle passTiming = true; 2846558f80cSRiver Riddle } 285