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" 258e7b683dSRiver 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 { 348e7b683dSRiver Riddle struct Timer { 358e7b683dSRiver Riddle explicit Timer(std::string &&name) : name(std::move(name)) {} 368e7b683dSRiver Riddle 378e7b683dSRiver Riddle /// Start the timer. 388e7b683dSRiver Riddle void start() { startTime = std::chrono::system_clock::now(); } 398e7b683dSRiver Riddle 408e7b683dSRiver Riddle /// Stop the timer. 418e7b683dSRiver Riddle void stop() { total += (std::chrono::system_clock::now() - startTime); } 428e7b683dSRiver Riddle 438e7b683dSRiver Riddle /// Get or create a child timer with the provided name and id. 448e7b683dSRiver Riddle Timer *getChildTimer(const void *id, 458e7b683dSRiver Riddle std::function<std::string()> &&nameBuilder) { 468e7b683dSRiver Riddle auto &child = children[id]; 478e7b683dSRiver Riddle if (!child) 488e7b683dSRiver Riddle child.reset(new Timer(nameBuilder())); 498e7b683dSRiver Riddle return child.get(); 508e7b683dSRiver Riddle } 518e7b683dSRiver Riddle 528e7b683dSRiver Riddle /// Returns the total time for this timer in seconds. 538e7b683dSRiver Riddle double getTotalTime() { 548e7b683dSRiver Riddle // If the total has a count, then we directly compute the seconds. 558e7b683dSRiver Riddle if (total.count()) { 568e7b683dSRiver Riddle return std::chrono::duration_cast<std::chrono::duration<double>>(total) 578e7b683dSRiver Riddle .count(); 588e7b683dSRiver Riddle } 598e7b683dSRiver Riddle 608e7b683dSRiver Riddle // Otheriwse, accumulate the timing from each of the children. 618e7b683dSRiver Riddle double totalTime = 0.0; 628e7b683dSRiver Riddle for (auto &child : children) 638e7b683dSRiver Riddle totalTime += child.second->getTotalTime(); 648e7b683dSRiver Riddle return totalTime; 658e7b683dSRiver Riddle } 668e7b683dSRiver Riddle 678e7b683dSRiver Riddle /// Raw timing information. 688e7b683dSRiver Riddle std::chrono::time_point<std::chrono::high_resolution_clock> startTime; 698e7b683dSRiver Riddle std::chrono::nanoseconds total = std::chrono::nanoseconds(0); 708e7b683dSRiver Riddle 718e7b683dSRiver Riddle /// A map of unique identifiers to child timers. 728e7b683dSRiver Riddle llvm::MapVector<const void *, std::unique_ptr<Timer>> children; 738e7b683dSRiver Riddle 748e7b683dSRiver Riddle /// A descriptive name for this timer. 758e7b683dSRiver Riddle std::string name; 768e7b683dSRiver Riddle }; 778e7b683dSRiver 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 } 868e7b683dSRiver Riddle void runAfterPass(Pass *pass, const llvm::Any &) override; 878e7b683dSRiver Riddle void runAfterPassFailed(Pass *pass, const llvm::Any &ir) override { 888e7b683dSRiver 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 } 948e7b683dSRiver Riddle void runAfterAnalysis(llvm::StringRef, AnalysisID *, 958e7b683dSRiver 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 1068e7b683dSRiver Riddle /// Stop a pass timer. 1078e7b683dSRiver Riddle void stopPassTimer(Pass *pass); 1088e7b683dSRiver Riddle 1098e7b683dSRiver Riddle /// Stop the last active timer. 1108e7b683dSRiver Riddle void stopTimer(); 1116558f80cSRiver Riddle 1126558f80cSRiver Riddle /// Print the timing result in list mode. 1138e7b683dSRiver Riddle void printResultsAsList(llvm::raw_ostream &os, double totalTime); 1146558f80cSRiver Riddle 1156558f80cSRiver Riddle /// Print the timing result in pipeline mode. 1168e7b683dSRiver Riddle void printResultsAsPipeline(llvm::raw_ostream &os, double totalTime); 1176558f80cSRiver Riddle 1188e7b683dSRiver Riddle /// Returns a timer for the provided identifier and name. 1198e7b683dSRiver Riddle Timer *getTimer(const void *id, std::function<std::string()> &&nameBuilder) { 1208e7b683dSRiver Riddle if (activeTimers.empty()) 1218e7b683dSRiver Riddle return rootTimer.getChildTimer(id, std::move(nameBuilder)); 1228e7b683dSRiver Riddle return activeTimers.back()->getChildTimer(id, std::move(nameBuilder)); 1238e7b683dSRiver Riddle } 1246558f80cSRiver Riddle 1258e7b683dSRiver Riddle /// The root top level timer. 1268e7b683dSRiver Riddle Timer rootTimer = Timer("root"); 1276558f80cSRiver Riddle 1288e7b683dSRiver Riddle /// A stack of the currently active pass timers. 1298e7b683dSRiver 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) { 1388e7b683dSRiver Riddle Timer *timer = getTimer(pass, [pass] { 139*af45236cSRiver Riddle if (isModuleToFunctionAdaptorPass(pass)) 1408e7b683dSRiver Riddle return StringRef("Function Pipeline"); 1418e7b683dSRiver Riddle return pass->getName(); 1428e7b683dSRiver Riddle }); 1438e7b683dSRiver Riddle activeTimers.push_back(timer); 144e46ba31cSRiver Riddle 1458e7b683dSRiver Riddle // We don't actually want to time the adaptor passes, they gather their total 1468e7b683dSRiver Riddle // from their held passes. 1478e7b683dSRiver Riddle if (!isAdaptorPass(pass)) 1488e7b683dSRiver 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) { 1538e7b683dSRiver Riddle Timer *timer = getTimer(id, [name] { return "(A) " + name.str(); }); 1548e7b683dSRiver Riddle activeTimers.push_back(timer); 1558e7b683dSRiver Riddle timer->start(); 156e46ba31cSRiver Riddle } 157e46ba31cSRiver Riddle 1588e7b683dSRiver Riddle /// Stop a pass timer. 1598e7b683dSRiver Riddle void PassTiming::runAfterPass(Pass *pass, const llvm::Any &) { 1608e7b683dSRiver Riddle assert(!activeTimers.empty() && "expected active timer"); 1618e7b683dSRiver Riddle Timer *timer = activeTimers.pop_back_val(); 1628e7b683dSRiver Riddle 1638e7b683dSRiver Riddle // Adapator passes aren't timed directly, so we don't need to stop their 1648e7b683dSRiver Riddle // timers. 1658e7b683dSRiver Riddle if (!isAdaptorPass(pass)) 1668e7b683dSRiver Riddle timer->stop(); 167e46ba31cSRiver Riddle } 168e46ba31cSRiver Riddle 1698e7b683dSRiver Riddle /// Stop a timer. 1708e7b683dSRiver Riddle void PassTiming::runAfterAnalysis(llvm::StringRef, AnalysisID *, 1718e7b683dSRiver Riddle const llvm::Any &) { 1728e7b683dSRiver Riddle assert(!activeTimers.empty() && "expected active timer"); 1738e7b683dSRiver Riddle Timer *timer = activeTimers.pop_back_val(); 1748e7b683dSRiver Riddle timer->stop(); 175e46ba31cSRiver Riddle } 176e46ba31cSRiver Riddle 1778e7b683dSRiver Riddle /// Utility to print the timer heading information. 1788e7b683dSRiver 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 1858e7b683dSRiver Riddle // Print the total time followed by the section headers. 1868e7b683dSRiver Riddle os << llvm::format(" Total Execution Time: %5.4f seconds\n\n", total); 1878e7b683dSRiver Riddle os << " ---Wall Time--- --- Name ---\n"; 1888e7b683dSRiver Riddle } 189e46ba31cSRiver Riddle 1908e7b683dSRiver Riddle /// Utility to print a single line entry in the timer output. 1918e7b683dSRiver Riddle static void printTimeEntry(raw_ostream &os, unsigned indent, StringRef name, 1928e7b683dSRiver Riddle double time, double totalTime) { 1938e7b683dSRiver Riddle os << llvm::format(" %7.4f (%5.1f%%) ", time, 100.0 * time / totalTime); 1948e7b683dSRiver Riddle os.indent(indent) << name << "\n"; 1958e7b683dSRiver Riddle } 1968e7b683dSRiver Riddle 1978e7b683dSRiver Riddle /// Print out the current timing information. 1988e7b683dSRiver Riddle void PassTiming::print() { 1998e7b683dSRiver Riddle // Don't print anything if there is no timing data. 2008e7b683dSRiver Riddle if (rootTimer.children.empty()) 2018e7b683dSRiver Riddle return; 2028e7b683dSRiver Riddle auto os = llvm::CreateInfoOutputFile(); 2038e7b683dSRiver Riddle 2048e7b683dSRiver Riddle // Print the timer header. 2058e7b683dSRiver Riddle double totalTime = rootTimer.getTotalTime(); 2068e7b683dSRiver Riddle printTimerHeader(*os, totalTime); 2078e7b683dSRiver Riddle 2088e7b683dSRiver Riddle // Defer to a specialized printer for each display mode. 2098e7b683dSRiver Riddle switch (displayMode) { 2108e7b683dSRiver Riddle case PassTimingDisplayMode::List: 2118e7b683dSRiver Riddle printResultsAsList(*os, totalTime); 2128e7b683dSRiver Riddle break; 2138e7b683dSRiver Riddle case PassTimingDisplayMode::Pipeline: 2148e7b683dSRiver Riddle printResultsAsPipeline(*os, totalTime); 2158e7b683dSRiver Riddle break; 2168e7b683dSRiver Riddle } 2178e7b683dSRiver Riddle printTimeEntry(*os, 0, "Total", totalTime, totalTime); 2188e7b683dSRiver Riddle os->flush(); 2198e7b683dSRiver Riddle 2208e7b683dSRiver Riddle // Reset root timer. 2218e7b683dSRiver Riddle rootTimer.children.clear(); 2228e7b683dSRiver Riddle } 2238e7b683dSRiver Riddle 2248e7b683dSRiver Riddle /// Print the timing result in list mode. 2258e7b683dSRiver Riddle void PassTiming::printResultsAsList(llvm::raw_ostream &os, double totalTime) { 2268e7b683dSRiver Riddle llvm::StringMap<double> mergedTimings; 2278e7b683dSRiver Riddle 2288e7b683dSRiver Riddle std::function<void(Timer *)> addTimer = [&](Timer *timer) { 2298e7b683dSRiver Riddle // Check for timing information. 2308e7b683dSRiver Riddle if (timer->total.count()) 2318e7b683dSRiver Riddle mergedTimings[timer->name] += timer->getTotalTime(); 2328e7b683dSRiver Riddle for (auto &children : timer->children) 2338e7b683dSRiver Riddle addTimer(children.second.get()); 2348e7b683dSRiver Riddle }; 2358e7b683dSRiver Riddle 2368e7b683dSRiver Riddle // Add each of the top level timers. 2378e7b683dSRiver Riddle for (auto &topLevelTimer : rootTimer.children) 2388e7b683dSRiver Riddle addTimer(topLevelTimer.second.get()); 2398e7b683dSRiver Riddle 2408e7b683dSRiver Riddle // Sort the timing information. 2418e7b683dSRiver Riddle std::vector<std::pair<StringRef, double>> timerNameAndTime; 2428e7b683dSRiver Riddle for (auto &it : mergedTimings) 2438e7b683dSRiver Riddle timerNameAndTime.emplace_back(it.first(), it.second); 2448e7b683dSRiver Riddle llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(), 2458e7b683dSRiver Riddle [](const std::pair<StringRef, double> *lhs, 2468e7b683dSRiver Riddle const std::pair<StringRef, double> *rhs) { 2478e7b683dSRiver Riddle return llvm::array_pod_sort_comparator<double>( 2488e7b683dSRiver Riddle &rhs->second, &lhs->second); 2498e7b683dSRiver Riddle }); 2508e7b683dSRiver Riddle 2518e7b683dSRiver Riddle // Print the timing information sequentially. 2528e7b683dSRiver Riddle for (auto &timeData : timerNameAndTime) 2538e7b683dSRiver Riddle printTimeEntry(os, 0, timeData.first, timeData.second, totalTime); 254e46ba31cSRiver Riddle } 255e46ba31cSRiver Riddle 256e46ba31cSRiver Riddle /// Print the timing result in pipeline mode. 2578e7b683dSRiver Riddle void PassTiming::printResultsAsPipeline(llvm::raw_ostream &os, 2588e7b683dSRiver Riddle double totalTime) { 2598e7b683dSRiver Riddle std::function<void(unsigned, Timer *)> printTimer = [&](unsigned indent, 2608e7b683dSRiver Riddle Timer *timer) { 2618e7b683dSRiver Riddle // Check for timing information. 2628e7b683dSRiver Riddle printTimeEntry(os, indent, timer->name, timer->getTotalTime(), totalTime); 2638e7b683dSRiver Riddle for (auto &children : timer->children) 2648e7b683dSRiver Riddle printTimer(indent + 2, children.second.get()); 265e46ba31cSRiver Riddle }; 266e46ba31cSRiver Riddle 2678e7b683dSRiver Riddle // Print each of the top level timers. 2688e7b683dSRiver Riddle for (auto &topLevelTimer : rootTimer.children) 2698e7b683dSRiver 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