1e46ba31cSRiver Riddle //===- PassTiming.cpp -----------------------------------------------------===// 2e46ba31cSRiver Riddle // 330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information. 556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e46ba31cSRiver Riddle // 756222a06SMehdi Amini //===----------------------------------------------------------------------===// 8e46ba31cSRiver Riddle 9e46ba31cSRiver Riddle #include "PassDetail.h" 106558f80cSRiver Riddle #include "mlir/Pass/PassManager.h" 116558f80cSRiver Riddle #include "llvm/ADT/MapVector.h" 12c900d499SChristian Sigg #include "llvm/ADT/STLExtras.h" 13e46ba31cSRiver Riddle #include "llvm/ADT/SmallVector.h" 14e46ba31cSRiver Riddle #include "llvm/ADT/Statistic.h" 15e46ba31cSRiver Riddle #include "llvm/Support/Format.h" 16e46ba31cSRiver Riddle #include "llvm/Support/FormatVariadic.h" 173a845be7SRiver Riddle #include "llvm/Support/Threading.h" 188e7b683dSRiver Riddle #include <chrono> 19e46ba31cSRiver Riddle 20e46ba31cSRiver Riddle using namespace mlir; 21e46ba31cSRiver Riddle using namespace mlir::detail; 22e46ba31cSRiver Riddle 234562e389SRiver Riddle constexpr StringLiteral kPassTimingDescription = 24e46ba31cSRiver Riddle "... Pass execution timing report ..."; 25e46ba31cSRiver Riddle 266558f80cSRiver Riddle namespace { 273a845be7SRiver Riddle /// Simple record class to record timing information. 283a845be7SRiver Riddle struct TimeRecord { 293a845be7SRiver Riddle TimeRecord(double wall = 0.0, double user = 0.0) : wall(wall), user(user) {} 303a845be7SRiver Riddle 313a845be7SRiver Riddle TimeRecord &operator+=(const TimeRecord &other) { 323a845be7SRiver Riddle wall += other.wall; 333a845be7SRiver Riddle user += other.user; 343a845be7SRiver Riddle return *this; 353a845be7SRiver Riddle } 363a845be7SRiver Riddle 373a845be7SRiver Riddle /// Print the current time record to 'os', with a breakdown showing 383a845be7SRiver Riddle /// contributions to the give 'total' time record. 393a845be7SRiver Riddle void print(raw_ostream &os, const TimeRecord &total) { 403a845be7SRiver Riddle if (total.user != total.wall) 413a845be7SRiver Riddle os << llvm::format(" %7.4f (%5.1f%%) ", user, 423a845be7SRiver Riddle 100.0 * user / total.user); 433a845be7SRiver Riddle os << llvm::format(" %7.4f (%5.1f%%) ", wall, 100.0 * wall / total.wall); 443a845be7SRiver Riddle } 453a845be7SRiver Riddle 463a845be7SRiver Riddle double wall, user; 473a845be7SRiver Riddle }; 483a845be7SRiver Riddle 49120509a6SRiver Riddle /// An enumeration of the different types of timers. 50120509a6SRiver Riddle enum class TimerKind { 51120509a6SRiver Riddle /// This timer represents an ordered collection of pass timers, corresponding 52120509a6SRiver Riddle /// to a pass pipeline. 53120509a6SRiver Riddle Pipeline, 54120509a6SRiver Riddle 55120509a6SRiver Riddle /// This timer represents a collection of pipeline timers. 56120509a6SRiver Riddle PipelineCollection, 57120509a6SRiver Riddle 58120509a6SRiver Riddle /// This timer represents an analysis or pass timer. 59120509a6SRiver Riddle PassOrAnalysis 60120509a6SRiver Riddle }; 61120509a6SRiver Riddle 628e7b683dSRiver Riddle struct Timer { 63120509a6SRiver Riddle explicit Timer(std::string &&name, TimerKind kind) 64120509a6SRiver Riddle : name(std::move(name)), kind(kind) {} 658e7b683dSRiver Riddle 668e7b683dSRiver Riddle /// Start the timer. 678e7b683dSRiver Riddle void start() { startTime = std::chrono::system_clock::now(); } 688e7b683dSRiver Riddle 698e7b683dSRiver Riddle /// Stop the timer. 703a845be7SRiver Riddle void stop() { 713a845be7SRiver Riddle auto newTime = std::chrono::system_clock::now() - startTime; 723a845be7SRiver Riddle wallTime += newTime; 733a845be7SRiver Riddle userTime += newTime; 743a845be7SRiver Riddle } 758e7b683dSRiver Riddle 768e7b683dSRiver Riddle /// Get or create a child timer with the provided name and id. 77120509a6SRiver Riddle Timer *getChildTimer(const void *id, TimerKind kind, 788e7b683dSRiver Riddle std::function<std::string()> &&nameBuilder) { 798e7b683dSRiver Riddle auto &child = children[id]; 808e7b683dSRiver Riddle if (!child) 81c900d499SChristian Sigg child = std::make_unique<Timer>(nameBuilder(), kind); 828e7b683dSRiver Riddle return child.get(); 838e7b683dSRiver Riddle } 848e7b683dSRiver Riddle 858e7b683dSRiver Riddle /// Returns the total time for this timer in seconds. 863a845be7SRiver Riddle TimeRecord getTotalTime() { 871c649d57SRiver Riddle // If this is a pass or analysis timer, use the recorded time directly. 881c649d57SRiver Riddle if (kind == TimerKind::PassOrAnalysis) { 893a845be7SRiver Riddle return TimeRecord( 903a845be7SRiver Riddle std::chrono::duration_cast<std::chrono::duration<double>>(wallTime) 913a845be7SRiver Riddle .count(), 923a845be7SRiver Riddle std::chrono::duration_cast<std::chrono::duration<double>>(userTime) 933a845be7SRiver Riddle .count()); 948e7b683dSRiver Riddle } 958e7b683dSRiver Riddle 968bfedb3cSKazuaki Ishizaki // Otherwise, accumulate the timing from each of the children. 973a845be7SRiver Riddle TimeRecord totalTime; 988e7b683dSRiver Riddle for (auto &child : children) 998e7b683dSRiver Riddle totalTime += child.second->getTotalTime(); 1008e7b683dSRiver Riddle return totalTime; 1018e7b683dSRiver Riddle } 1028e7b683dSRiver Riddle 1033a845be7SRiver Riddle /// A map of unique identifiers to child timers. 1043a845be7SRiver Riddle using ChildrenMap = llvm::MapVector<const void *, std::unique_ptr<Timer>>; 1053a845be7SRiver Riddle 1063a845be7SRiver Riddle /// Merge the timing data from 'other' into this timer. 1073a845be7SRiver Riddle void merge(Timer &&other) { 1083a845be7SRiver Riddle if (wallTime < other.wallTime) 1093a845be7SRiver Riddle wallTime = other.wallTime; 1103a845be7SRiver Riddle userTime += other.userTime; 111120509a6SRiver Riddle mergeChildren(std::move(other.children)); 1123a845be7SRiver Riddle } 1133a845be7SRiver Riddle 1148bfedb3cSKazuaki Ishizaki /// Merge the timer children in 'otherChildren' with the children of this 115120509a6SRiver Riddle /// timer. 116120509a6SRiver Riddle void mergeChildren(ChildrenMap &&otherChildren) { 1173a845be7SRiver Riddle // Check for an empty children list. 1183a845be7SRiver Riddle if (children.empty()) { 1193a845be7SRiver Riddle children = std::move(otherChildren); 1203a845be7SRiver Riddle return; 1213a845be7SRiver Riddle } 1223a845be7SRiver Riddle 123120509a6SRiver Riddle // Pipeline merges are handled separately as the children are merged 1248bfedb3cSKazuaki Ishizaki // lexicographically. 125120509a6SRiver Riddle if (kind == TimerKind::Pipeline) { 1263a845be7SRiver Riddle assert(children.size() == otherChildren.size() && 127120509a6SRiver Riddle "pipeline merge requires the same number of children"); 128120509a6SRiver Riddle for (auto it : llvm::zip(children, otherChildren)) 129120509a6SRiver Riddle std::get<0>(it).second->merge(std::move(*std::get<1>(it).second)); 1303a845be7SRiver Riddle return; 1313a845be7SRiver Riddle } 1323a845be7SRiver Riddle 133120509a6SRiver Riddle // Otherwise, we merge children based upon their timer key. 134120509a6SRiver Riddle for (auto &otherChild : otherChildren) 135120509a6SRiver Riddle mergeChild(std::move(otherChild)); 1363a845be7SRiver Riddle } 137120509a6SRiver Riddle 138120509a6SRiver Riddle /// Merge in the given child timer and id into this timer. 139120509a6SRiver Riddle void mergeChild(ChildrenMap::value_type &&childIt) { 140120509a6SRiver Riddle auto &child = children[childIt.first]; 141120509a6SRiver Riddle if (!child) 142120509a6SRiver Riddle child = std::move(childIt.second); 143120509a6SRiver Riddle else 144120509a6SRiver Riddle child->merge(std::move(*childIt.second)); 1453a845be7SRiver Riddle } 1463a845be7SRiver Riddle 1478e7b683dSRiver Riddle /// Raw timing information. 148ff05e9daSRiver Riddle std::chrono::time_point<std::chrono::system_clock> startTime; 1493a845be7SRiver Riddle std::chrono::nanoseconds wallTime = std::chrono::nanoseconds(0); 1503a845be7SRiver Riddle std::chrono::nanoseconds userTime = std::chrono::nanoseconds(0); 1518e7b683dSRiver Riddle 1528e7b683dSRiver Riddle /// A map of unique identifiers to child timers. 1533a845be7SRiver Riddle ChildrenMap children; 1548e7b683dSRiver Riddle 1558e7b683dSRiver Riddle /// A descriptive name for this timer. 1568e7b683dSRiver Riddle std::string name; 157120509a6SRiver Riddle 158120509a6SRiver Riddle /// The type of timer this instance represents. 159120509a6SRiver Riddle TimerKind kind; 1608e7b683dSRiver Riddle }; 1618e7b683dSRiver Riddle 1626558f80cSRiver Riddle struct PassTiming : public PassInstrumentation { 163f65a3f7cSMehdi Amini PassTiming(std::unique_ptr<PassManager::PassTimingConfig> config) 164f65a3f7cSMehdi Amini : config(std::move(config)) {} 165c900d499SChristian Sigg ~PassTiming() override { print(); } 166e46ba31cSRiver Riddle 1676558f80cSRiver Riddle /// Setup the instrumentation hooks. 168*1284dc34SMehdi Amini void runBeforePipeline(Identifier name, 1691c649d57SRiver Riddle const PipelineParentInfo &parentInfo) override; 170*1284dc34SMehdi Amini void runAfterPipeline(Identifier name, 1711c649d57SRiver Riddle const PipelineParentInfo &parentInfo) override; 1724fb971a9SRiver Riddle void runBeforePass(Pass *pass, Operation *) override { startPassTimer(pass); } 1734fb971a9SRiver Riddle void runAfterPass(Pass *pass, Operation *) override; 1744fb971a9SRiver Riddle void runAfterPassFailed(Pass *pass, Operation *op) override { 1754fb971a9SRiver Riddle runAfterPass(pass, op); 1766558f80cSRiver Riddle } 177a517191aSRiver Riddle void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override { 1786558f80cSRiver Riddle startAnalysisTimer(name, id); 1796558f80cSRiver Riddle } 180a517191aSRiver Riddle void runAfterAnalysis(StringRef, TypeID, Operation *) override; 1816558f80cSRiver Riddle 1826558f80cSRiver Riddle /// Print and clear the timing results. 1836558f80cSRiver Riddle void print(); 1846558f80cSRiver Riddle 1856558f80cSRiver Riddle /// Start a new timer for the given pass. 1866558f80cSRiver Riddle void startPassTimer(Pass *pass); 1876558f80cSRiver Riddle 1886558f80cSRiver Riddle /// Start a new timer for the given analysis. 189a517191aSRiver Riddle void startAnalysisTimer(StringRef name, TypeID id); 1906558f80cSRiver Riddle 191120509a6SRiver Riddle /// Pop the last active timer for the current thread. 192120509a6SRiver Riddle Timer *popLastActiveTimer() { 193120509a6SRiver Riddle auto tid = llvm::get_threadid(); 194120509a6SRiver Riddle auto &activeTimers = activeThreadTimers[tid]; 195120509a6SRiver Riddle assert(!activeTimers.empty() && "expected active timer"); 196120509a6SRiver Riddle return activeTimers.pop_back_val(); 197120509a6SRiver Riddle } 1986558f80cSRiver Riddle 1996558f80cSRiver Riddle /// Print the timing result in list mode. 2003a845be7SRiver Riddle void printResultsAsList(raw_ostream &os, Timer *root, TimeRecord totalTime); 2016558f80cSRiver Riddle 2026558f80cSRiver Riddle /// Print the timing result in pipeline mode. 2033a845be7SRiver Riddle void printResultsAsPipeline(raw_ostream &os, Timer *root, 2043a845be7SRiver Riddle TimeRecord totalTime); 2056558f80cSRiver Riddle 2068e7b683dSRiver Riddle /// Returns a timer for the provided identifier and name. 207120509a6SRiver Riddle Timer *getTimer(const void *id, TimerKind kind, 208120509a6SRiver Riddle std::function<std::string()> &&nameBuilder) { 2093a845be7SRiver Riddle auto tid = llvm::get_threadid(); 2103a845be7SRiver Riddle 2113a845be7SRiver Riddle // If there is no active timer then add to the root timer. 2123a845be7SRiver Riddle auto &activeTimers = activeThreadTimers[tid]; 213120509a6SRiver Riddle Timer *parentTimer; 2143a845be7SRiver Riddle if (activeTimers.empty()) { 2153a845be7SRiver Riddle auto &rootTimer = rootTimers[tid]; 2163a845be7SRiver Riddle if (!rootTimer) 217c900d499SChristian Sigg rootTimer = std::make_unique<Timer>("root", TimerKind::Pipeline); 218120509a6SRiver Riddle parentTimer = rootTimer.get(); 219120509a6SRiver Riddle } else { 220120509a6SRiver Riddle // Otherwise, add this to the active timer. 221120509a6SRiver Riddle parentTimer = activeTimers.back(); 2228e7b683dSRiver Riddle } 2236558f80cSRiver Riddle 224120509a6SRiver Riddle auto timer = parentTimer->getChildTimer(id, kind, std::move(nameBuilder)); 2253a845be7SRiver Riddle activeTimers.push_back(timer); 2263a845be7SRiver Riddle return timer; 2273a845be7SRiver Riddle } 2286558f80cSRiver Riddle 2293a845be7SRiver Riddle /// The root top level timers for each thread. 2303a845be7SRiver Riddle DenseMap<uint64_t, std::unique_ptr<Timer>> rootTimers; 2313a845be7SRiver Riddle 2323a845be7SRiver Riddle /// A stack of the currently active pass timers per thread. 2333a845be7SRiver Riddle DenseMap<uint64_t, SmallVector<Timer *, 4>> activeThreadTimers; 2346558f80cSRiver Riddle 235f65a3f7cSMehdi Amini /// The configuration object to use when printing the timing results. 236f65a3f7cSMehdi Amini std::unique_ptr<PassManager::PassTimingConfig> config; 237120509a6SRiver Riddle 238120509a6SRiver Riddle /// A mapping of pipeline timers that need to be merged into the parent 2391c649d57SRiver Riddle /// collection. The timers are mapped to the parent info to merge into. 2401c649d57SRiver Riddle DenseMap<PipelineParentInfo, SmallVector<Timer::ChildrenMap::value_type, 4>> 241120509a6SRiver Riddle pipelinesToMerge; 2426558f80cSRiver Riddle }; 2436558f80cSRiver Riddle } // end anonymous namespace 244e46ba31cSRiver Riddle 245*1284dc34SMehdi Amini void PassTiming::runBeforePipeline(Identifier name, 2461c649d57SRiver Riddle const PipelineParentInfo &parentInfo) { 2475aacce3dSKazuaki Ishizaki // We don't actually want to time the pipelines, they gather their total 248120509a6SRiver Riddle // from their held passes. 249120509a6SRiver Riddle getTimer(name.getAsOpaquePointer(), TimerKind::Pipeline, 250*1284dc34SMehdi Amini [&] { return ("'" + name.strref() + "' Pipeline").str(); }); 251120509a6SRiver Riddle } 252120509a6SRiver Riddle 253*1284dc34SMehdi Amini void PassTiming::runAfterPipeline(Identifier name, 2541c649d57SRiver Riddle const PipelineParentInfo &parentInfo) { 255120509a6SRiver Riddle // Pop the timer for the pipeline. 256120509a6SRiver Riddle auto tid = llvm::get_threadid(); 257120509a6SRiver Riddle auto &activeTimers = activeThreadTimers[tid]; 258120509a6SRiver Riddle assert(!activeTimers.empty() && "expected active timer"); 259120509a6SRiver Riddle activeTimers.pop_back(); 260120509a6SRiver Riddle 261120509a6SRiver Riddle // If the current thread is the same as the parent, there is nothing left to 262120509a6SRiver Riddle // do. 2631c649d57SRiver Riddle if (tid == parentInfo.parentThreadID) 264120509a6SRiver Riddle return; 265120509a6SRiver Riddle 266120509a6SRiver Riddle // Otherwise, mark the pipeline timer for merging into the correct parent 267120509a6SRiver Riddle // thread. 268120509a6SRiver Riddle assert(activeTimers.empty() && "expected parent timer to be root"); 269120509a6SRiver Riddle auto *parentTimer = rootTimers[tid].get(); 270120509a6SRiver Riddle assert(parentTimer->children.size() == 1 && 271120509a6SRiver Riddle parentTimer->children.count(name.getAsOpaquePointer()) && 272120509a6SRiver Riddle "expected a single pipeline timer"); 2731c649d57SRiver Riddle pipelinesToMerge[parentInfo].push_back( 274120509a6SRiver Riddle std::move(*parentTimer->children.begin())); 275120509a6SRiver Riddle rootTimers.erase(tid); 276120509a6SRiver Riddle } 277120509a6SRiver Riddle 278e46ba31cSRiver Riddle /// Start a new timer for the given pass. 279e46ba31cSRiver Riddle void PassTiming::startPassTimer(Pass *pass) { 28056a69851SRiver Riddle auto kind = isa<OpToOpPassAdaptor>(pass) ? TimerKind::PipelineCollection 281120509a6SRiver Riddle : TimerKind::PassOrAnalysis; 282120509a6SRiver Riddle Timer *timer = getTimer(pass, kind, [pass]() -> std::string { 28356a69851SRiver Riddle if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) 28456a69851SRiver Riddle return adaptor->getAdaptorName(); 285adcd0268SBenjamin Kramer return std::string(pass->getName()); 2868e7b683dSRiver Riddle }); 287e46ba31cSRiver Riddle 2888e7b683dSRiver Riddle // We don't actually want to time the adaptor passes, they gather their total 2898e7b683dSRiver Riddle // from their held passes. 29056a69851SRiver Riddle if (!isa<OpToOpPassAdaptor>(pass)) 2918e7b683dSRiver Riddle timer->start(); 292e46ba31cSRiver Riddle } 293e46ba31cSRiver Riddle 294e46ba31cSRiver Riddle /// Start a new timer for the given analysis. 295a517191aSRiver Riddle void PassTiming::startAnalysisTimer(StringRef name, TypeID id) { 296a517191aSRiver Riddle Timer *timer = getTimer(id.getAsOpaquePointer(), TimerKind::PassOrAnalysis, 297120509a6SRiver Riddle [name] { return "(A) " + name.str(); }); 2988e7b683dSRiver Riddle timer->start(); 299e46ba31cSRiver Riddle } 300e46ba31cSRiver Riddle 3018e7b683dSRiver Riddle /// Stop a pass timer. 3024fb971a9SRiver Riddle void PassTiming::runAfterPass(Pass *pass, Operation *) { 303120509a6SRiver Riddle Timer *timer = popLastActiveTimer(); 3048e7b683dSRiver Riddle 30556a69851SRiver Riddle // If this is a pass adaptor, then we need to merge in the timing data for the 30656a69851SRiver Riddle // pipelines running on other threads. 30756a69851SRiver Riddle if (isa<OpToOpPassAdaptor>(pass)) { 3081c649d57SRiver Riddle auto toMerge = pipelinesToMerge.find({llvm::get_threadid(), pass}); 309120509a6SRiver Riddle if (toMerge != pipelinesToMerge.end()) { 310120509a6SRiver Riddle for (auto &it : toMerge->second) 311120509a6SRiver Riddle timer->mergeChild(std::move(it)); 312120509a6SRiver Riddle pipelinesToMerge.erase(toMerge); 3133a845be7SRiver Riddle } 3143a845be7SRiver Riddle return; 3153a845be7SRiver Riddle } 3163a845be7SRiver Riddle 3178e7b683dSRiver Riddle timer->stop(); 318e46ba31cSRiver Riddle } 319e46ba31cSRiver Riddle 3208e7b683dSRiver Riddle /// Stop a timer. 321a517191aSRiver Riddle void PassTiming::runAfterAnalysis(StringRef, TypeID, Operation *) { 322120509a6SRiver Riddle popLastActiveTimer()->stop(); 323e46ba31cSRiver Riddle } 324e46ba31cSRiver Riddle 3258e7b683dSRiver Riddle /// Utility to print the timer heading information. 3264562e389SRiver Riddle static void printTimerHeader(raw_ostream &os, TimeRecord total) { 327e46ba31cSRiver Riddle os << "===" << std::string(73, '-') << "===\n"; 328e46ba31cSRiver Riddle // Figure out how many spaces to description name. 32933a64540SRiver Riddle unsigned padding = (80 - kPassTimingDescription.size()) / 2; 33033a64540SRiver Riddle os.indent(padding) << kPassTimingDescription << '\n'; 331e46ba31cSRiver Riddle os << "===" << std::string(73, '-') << "===\n"; 332e46ba31cSRiver Riddle 3338e7b683dSRiver Riddle // Print the total time followed by the section headers. 3343a845be7SRiver Riddle os << llvm::format(" Total Execution Time: %5.4f seconds\n\n", total.wall); 3353a845be7SRiver Riddle if (total.user != total.wall) 3363a845be7SRiver Riddle os << " ---User Time---"; 3378e7b683dSRiver Riddle os << " ---Wall Time--- --- Name ---\n"; 3388e7b683dSRiver Riddle } 339e46ba31cSRiver Riddle 3408e7b683dSRiver Riddle /// Utility to print a single line entry in the timer output. 3418e7b683dSRiver Riddle static void printTimeEntry(raw_ostream &os, unsigned indent, StringRef name, 3423a845be7SRiver Riddle TimeRecord time, TimeRecord totalTime) { 3433a845be7SRiver Riddle time.print(os, totalTime); 3448e7b683dSRiver Riddle os.indent(indent) << name << "\n"; 3458e7b683dSRiver Riddle } 3468e7b683dSRiver Riddle 3478e7b683dSRiver Riddle /// Print out the current timing information. 3488e7b683dSRiver Riddle void PassTiming::print() { 3498e7b683dSRiver Riddle // Don't print anything if there is no timing data. 3503a845be7SRiver Riddle if (rootTimers.empty()) 3518e7b683dSRiver Riddle return; 3523a845be7SRiver Riddle 3533a845be7SRiver Riddle assert(rootTimers.size() == 1 && "expected one remaining root timer"); 3548e7b683dSRiver Riddle 355f65a3f7cSMehdi Amini auto printCallback = [&](raw_ostream &os) { 356f65a3f7cSMehdi Amini auto &rootTimer = rootTimers.begin()->second; 3578e7b683dSRiver Riddle // Print the timer header. 3583a845be7SRiver Riddle TimeRecord totalTime = rootTimer->getTotalTime(); 359f65a3f7cSMehdi Amini printTimerHeader(os, totalTime); 3608e7b683dSRiver Riddle // Defer to a specialized printer for each display mode. 361f65a3f7cSMehdi Amini switch (config->getDisplayMode()) { 36233a64540SRiver Riddle case PassDisplayMode::List: 363f65a3f7cSMehdi Amini printResultsAsList(os, rootTimer.get(), totalTime); 3648e7b683dSRiver Riddle break; 36533a64540SRiver Riddle case PassDisplayMode::Pipeline: 366f65a3f7cSMehdi Amini printResultsAsPipeline(os, rootTimer.get(), totalTime); 3678e7b683dSRiver Riddle break; 3688e7b683dSRiver Riddle } 369f65a3f7cSMehdi Amini printTimeEntry(os, 0, "Total", totalTime, totalTime); 370f65a3f7cSMehdi Amini os.flush(); 3718e7b683dSRiver Riddle 3723a845be7SRiver Riddle // Reset root timers. 3733a845be7SRiver Riddle rootTimers.clear(); 3743a845be7SRiver Riddle activeThreadTimers.clear(); 375f65a3f7cSMehdi Amini }; 376f65a3f7cSMehdi Amini 377f65a3f7cSMehdi Amini config->printTiming(printCallback); 378f65a3f7cSMehdi Amini } 379f65a3f7cSMehdi Amini 380f65a3f7cSMehdi Amini // The default implementation for printTiming uses 381f65a3f7cSMehdi Amini // `llvm::CreateInfoOutputFile()` as stream, it can be overridden by clients 382f65a3f7cSMehdi Amini // to customize the output. 383f65a3f7cSMehdi Amini void PassManager::PassTimingConfig::printTiming(PrintCallbackFn printCallback) { 384f65a3f7cSMehdi Amini printCallback(*llvm::CreateInfoOutputFile()); 3858e7b683dSRiver Riddle } 3868e7b683dSRiver Riddle 3878e7b683dSRiver Riddle /// Print the timing result in list mode. 3883a845be7SRiver Riddle void PassTiming::printResultsAsList(raw_ostream &os, Timer *root, 3893a845be7SRiver Riddle TimeRecord totalTime) { 3903a845be7SRiver Riddle llvm::StringMap<TimeRecord> mergedTimings; 3918e7b683dSRiver Riddle 3928e7b683dSRiver Riddle std::function<void(Timer *)> addTimer = [&](Timer *timer) { 393120509a6SRiver Riddle // Only add timing information for passes and analyses. 394120509a6SRiver Riddle if (timer->kind == TimerKind::PassOrAnalysis) 3958e7b683dSRiver Riddle mergedTimings[timer->name] += timer->getTotalTime(); 3968e7b683dSRiver Riddle for (auto &children : timer->children) 3978e7b683dSRiver Riddle addTimer(children.second.get()); 3988e7b683dSRiver Riddle }; 3998e7b683dSRiver Riddle 4008e7b683dSRiver Riddle // Add each of the top level timers. 4013a845be7SRiver Riddle for (auto &topLevelTimer : root->children) 4028e7b683dSRiver Riddle addTimer(topLevelTimer.second.get()); 4038e7b683dSRiver Riddle 4043a845be7SRiver Riddle // Sort the timing information by wall time. 4053a845be7SRiver Riddle std::vector<std::pair<StringRef, TimeRecord>> timerNameAndTime; 4068e7b683dSRiver Riddle for (auto &it : mergedTimings) 4078e7b683dSRiver Riddle timerNameAndTime.emplace_back(it.first(), it.second); 4088e7b683dSRiver Riddle llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(), 4093a845be7SRiver Riddle [](const std::pair<StringRef, TimeRecord> *lhs, 4103a845be7SRiver Riddle const std::pair<StringRef, TimeRecord> *rhs) { 4118e7b683dSRiver Riddle return llvm::array_pod_sort_comparator<double>( 4123a845be7SRiver Riddle &rhs->second.wall, &lhs->second.wall); 4138e7b683dSRiver Riddle }); 4148e7b683dSRiver Riddle 4158e7b683dSRiver Riddle // Print the timing information sequentially. 4168e7b683dSRiver Riddle for (auto &timeData : timerNameAndTime) 4178e7b683dSRiver Riddle printTimeEntry(os, 0, timeData.first, timeData.second, totalTime); 418e46ba31cSRiver Riddle } 419e46ba31cSRiver Riddle 420e46ba31cSRiver Riddle /// Print the timing result in pipeline mode. 4213a845be7SRiver Riddle void PassTiming::printResultsAsPipeline(raw_ostream &os, Timer *root, 4223a845be7SRiver Riddle TimeRecord totalTime) { 4238e7b683dSRiver Riddle std::function<void(unsigned, Timer *)> printTimer = [&](unsigned indent, 4248e7b683dSRiver Riddle Timer *timer) { 425120509a6SRiver Riddle // If this is a timer for a pipeline collection and the collection only has 426120509a6SRiver Riddle // one pipeline child, then only print the child. 427120509a6SRiver Riddle if (timer->kind == TimerKind::PipelineCollection && 428120509a6SRiver Riddle timer->children.size() == 1) 429120509a6SRiver Riddle return printTimer(indent, timer->children.begin()->second.get()); 430120509a6SRiver Riddle 4318e7b683dSRiver Riddle printTimeEntry(os, indent, timer->name, timer->getTotalTime(), totalTime); 432120509a6SRiver Riddle 433120509a6SRiver Riddle // If this timer is a pipeline, then print the children in-order. 434120509a6SRiver Riddle if (timer->kind == TimerKind::Pipeline) { 435120509a6SRiver Riddle for (auto &child : timer->children) 436120509a6SRiver Riddle printTimer(indent + 2, child.second.get()); 437120509a6SRiver Riddle return; 438120509a6SRiver Riddle } 439120509a6SRiver Riddle 440120509a6SRiver Riddle // Otherwise, sort the children by name to give a deterministic ordering 441120509a6SRiver Riddle // when emitting the time. 442120509a6SRiver Riddle SmallVector<Timer *, 4> children; 443120509a6SRiver Riddle children.reserve(timer->children.size()); 444120509a6SRiver Riddle for (auto &child : timer->children) 445120509a6SRiver Riddle children.push_back(child.second.get()); 446120509a6SRiver Riddle llvm::array_pod_sort(children.begin(), children.end(), 447120509a6SRiver Riddle [](Timer *const *lhs, Timer *const *rhs) { 448120509a6SRiver Riddle return (*lhs)->name.compare((*rhs)->name); 449120509a6SRiver Riddle }); 450120509a6SRiver Riddle for (auto &child : children) 451120509a6SRiver Riddle printTimer(indent + 2, child); 452e46ba31cSRiver Riddle }; 453e46ba31cSRiver Riddle 4548e7b683dSRiver Riddle // Print each of the top level timers. 4553a845be7SRiver Riddle for (auto &topLevelTimer : root->children) 4568e7b683dSRiver Riddle printTimer(0, topLevelTimer.second.get()); 457e46ba31cSRiver Riddle } 4586558f80cSRiver Riddle 459f65a3f7cSMehdi Amini // Out-of-line as key function. 460f65a3f7cSMehdi Amini PassManager::PassTimingConfig::~PassTimingConfig() {} 461f65a3f7cSMehdi Amini 4626558f80cSRiver Riddle //===----------------------------------------------------------------------===// 4636558f80cSRiver Riddle // PassManager 4646558f80cSRiver Riddle //===----------------------------------------------------------------------===// 4656558f80cSRiver Riddle 4666558f80cSRiver Riddle /// Add an instrumentation to time the execution of passes and the computation 4676558f80cSRiver Riddle /// of analyses. 468f65a3f7cSMehdi Amini void PassManager::enableTiming(std::unique_ptr<PassTimingConfig> config) { 4696558f80cSRiver Riddle // Check if pass timing is already enabled. 4706558f80cSRiver Riddle if (passTiming) 4716558f80cSRiver Riddle return; 472f65a3f7cSMehdi Amini if (!config) 473f65a3f7cSMehdi Amini config = std::make_unique<PassManager::PassTimingConfig>(); 474f65a3f7cSMehdi Amini addInstrumentation(std::make_unique<PassTiming>(std::move(config))); 4756558f80cSRiver Riddle passTiming = true; 4766558f80cSRiver Riddle } 477