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" 253a845be7SRiver Riddle #include "llvm/Support/Threading.h" 268e7b683dSRiver Riddle #include <chrono> 27e46ba31cSRiver Riddle 28e46ba31cSRiver Riddle using namespace mlir; 29e46ba31cSRiver Riddle using namespace mlir::detail; 30e46ba31cSRiver Riddle 31e46ba31cSRiver Riddle constexpr llvm::StringLiteral kPassTimingDescription = 32e46ba31cSRiver Riddle "... Pass execution timing report ..."; 33e46ba31cSRiver Riddle 346558f80cSRiver Riddle namespace { 353a845be7SRiver Riddle /// Simple record class to record timing information. 363a845be7SRiver Riddle struct TimeRecord { 373a845be7SRiver Riddle TimeRecord(double wall = 0.0, double user = 0.0) : wall(wall), user(user) {} 383a845be7SRiver Riddle 393a845be7SRiver Riddle TimeRecord &operator+=(const TimeRecord &other) { 403a845be7SRiver Riddle wall += other.wall; 413a845be7SRiver Riddle user += other.user; 423a845be7SRiver Riddle return *this; 433a845be7SRiver Riddle } 443a845be7SRiver Riddle 453a845be7SRiver Riddle /// Print the current time record to 'os', with a breakdown showing 463a845be7SRiver Riddle /// contributions to the give 'total' time record. 473a845be7SRiver Riddle void print(raw_ostream &os, const TimeRecord &total) { 483a845be7SRiver Riddle if (total.user != total.wall) 493a845be7SRiver Riddle os << llvm::format(" %7.4f (%5.1f%%) ", user, 503a845be7SRiver Riddle 100.0 * user / total.user); 513a845be7SRiver Riddle os << llvm::format(" %7.4f (%5.1f%%) ", wall, 100.0 * wall / total.wall); 523a845be7SRiver Riddle } 533a845be7SRiver Riddle 543a845be7SRiver Riddle double wall, user; 553a845be7SRiver Riddle }; 563a845be7SRiver Riddle 57*120509a6SRiver Riddle /// An enumeration of the different types of timers. 58*120509a6SRiver Riddle enum class TimerKind { 59*120509a6SRiver Riddle /// This timer represents an ordered collection of pass timers, corresponding 60*120509a6SRiver Riddle /// to a pass pipeline. 61*120509a6SRiver Riddle Pipeline, 62*120509a6SRiver Riddle 63*120509a6SRiver Riddle /// This timer represents a collection of pipeline timers. 64*120509a6SRiver Riddle PipelineCollection, 65*120509a6SRiver Riddle 66*120509a6SRiver Riddle /// This timer represents an analysis or pass timer. 67*120509a6SRiver Riddle PassOrAnalysis 68*120509a6SRiver Riddle }; 69*120509a6SRiver Riddle 708e7b683dSRiver Riddle struct Timer { 71*120509a6SRiver Riddle explicit Timer(std::string &&name, TimerKind kind) 72*120509a6SRiver Riddle : name(std::move(name)), kind(kind) {} 738e7b683dSRiver Riddle 748e7b683dSRiver Riddle /// Start the timer. 758e7b683dSRiver Riddle void start() { startTime = std::chrono::system_clock::now(); } 768e7b683dSRiver Riddle 778e7b683dSRiver Riddle /// Stop the timer. 783a845be7SRiver Riddle void stop() { 793a845be7SRiver Riddle auto newTime = std::chrono::system_clock::now() - startTime; 803a845be7SRiver Riddle wallTime += newTime; 813a845be7SRiver Riddle userTime += newTime; 823a845be7SRiver Riddle } 838e7b683dSRiver Riddle 848e7b683dSRiver Riddle /// Get or create a child timer with the provided name and id. 85*120509a6SRiver Riddle Timer *getChildTimer(const void *id, TimerKind kind, 868e7b683dSRiver Riddle std::function<std::string()> &&nameBuilder) { 878e7b683dSRiver Riddle auto &child = children[id]; 888e7b683dSRiver Riddle if (!child) 89*120509a6SRiver Riddle child.reset(new Timer(nameBuilder(), kind)); 908e7b683dSRiver Riddle return child.get(); 918e7b683dSRiver Riddle } 928e7b683dSRiver Riddle 938e7b683dSRiver Riddle /// Returns the total time for this timer in seconds. 943a845be7SRiver Riddle TimeRecord getTotalTime() { 953a845be7SRiver Riddle // If we have a valid wall time, then we directly compute the seconds. 963a845be7SRiver Riddle if (wallTime.count()) { 973a845be7SRiver Riddle return TimeRecord( 983a845be7SRiver Riddle std::chrono::duration_cast<std::chrono::duration<double>>(wallTime) 993a845be7SRiver Riddle .count(), 1003a845be7SRiver Riddle std::chrono::duration_cast<std::chrono::duration<double>>(userTime) 1013a845be7SRiver Riddle .count()); 1028e7b683dSRiver Riddle } 1038e7b683dSRiver Riddle 1048e7b683dSRiver Riddle // Otheriwse, accumulate the timing from each of the children. 1053a845be7SRiver Riddle TimeRecord totalTime; 1068e7b683dSRiver Riddle for (auto &child : children) 1078e7b683dSRiver Riddle totalTime += child.second->getTotalTime(); 1088e7b683dSRiver Riddle return totalTime; 1098e7b683dSRiver Riddle } 1108e7b683dSRiver Riddle 1113a845be7SRiver Riddle /// A map of unique identifiers to child timers. 1123a845be7SRiver Riddle using ChildrenMap = llvm::MapVector<const void *, std::unique_ptr<Timer>>; 1133a845be7SRiver Riddle 1143a845be7SRiver Riddle /// Merge the timing data from 'other' into this timer. 1153a845be7SRiver Riddle void merge(Timer &&other) { 1163a845be7SRiver Riddle if (wallTime < other.wallTime) 1173a845be7SRiver Riddle wallTime = other.wallTime; 1183a845be7SRiver Riddle userTime += other.userTime; 119*120509a6SRiver Riddle mergeChildren(std::move(other.children)); 1203a845be7SRiver Riddle } 1213a845be7SRiver Riddle 1223a845be7SRiver Riddle /// Merge the timer chilren in 'otherChildren' with the children of this 123*120509a6SRiver Riddle /// timer. 124*120509a6SRiver Riddle void mergeChildren(ChildrenMap &&otherChildren) { 1253a845be7SRiver Riddle // Check for an empty children list. 1263a845be7SRiver Riddle if (children.empty()) { 1273a845be7SRiver Riddle children = std::move(otherChildren); 1283a845be7SRiver Riddle return; 1293a845be7SRiver Riddle } 1303a845be7SRiver Riddle 131*120509a6SRiver Riddle // Pipeline merges are handled separately as the children are merged 132*120509a6SRiver Riddle // lexographically. 133*120509a6SRiver Riddle if (kind == TimerKind::Pipeline) { 1343a845be7SRiver Riddle assert(children.size() == otherChildren.size() && 135*120509a6SRiver Riddle "pipeline merge requires the same number of children"); 136*120509a6SRiver Riddle for (auto it : llvm::zip(children, otherChildren)) 137*120509a6SRiver Riddle std::get<0>(it).second->merge(std::move(*std::get<1>(it).second)); 1383a845be7SRiver Riddle return; 1393a845be7SRiver Riddle } 1403a845be7SRiver Riddle 141*120509a6SRiver Riddle // Otherwise, we merge children based upon their timer key. 142*120509a6SRiver Riddle for (auto &otherChild : otherChildren) 143*120509a6SRiver Riddle mergeChild(std::move(otherChild)); 1443a845be7SRiver Riddle } 145*120509a6SRiver Riddle 146*120509a6SRiver Riddle /// Merge in the given child timer and id into this timer. 147*120509a6SRiver Riddle void mergeChild(ChildrenMap::value_type &&childIt) { 148*120509a6SRiver Riddle auto &child = children[childIt.first]; 149*120509a6SRiver Riddle if (!child) 150*120509a6SRiver Riddle child = std::move(childIt.second); 151*120509a6SRiver Riddle else 152*120509a6SRiver Riddle child->merge(std::move(*childIt.second)); 1533a845be7SRiver Riddle } 1543a845be7SRiver Riddle 1558e7b683dSRiver Riddle /// Raw timing information. 156ff05e9daSRiver Riddle std::chrono::time_point<std::chrono::system_clock> startTime; 1573a845be7SRiver Riddle std::chrono::nanoseconds wallTime = std::chrono::nanoseconds(0); 1583a845be7SRiver Riddle std::chrono::nanoseconds userTime = std::chrono::nanoseconds(0); 1598e7b683dSRiver Riddle 1608e7b683dSRiver Riddle /// A map of unique identifiers to child timers. 1613a845be7SRiver Riddle ChildrenMap children; 1628e7b683dSRiver Riddle 1638e7b683dSRiver Riddle /// A descriptive name for this timer. 1648e7b683dSRiver Riddle std::string name; 165*120509a6SRiver Riddle 166*120509a6SRiver Riddle /// The type of timer this instance represents. 167*120509a6SRiver Riddle TimerKind kind; 1688e7b683dSRiver Riddle }; 1698e7b683dSRiver Riddle 1706558f80cSRiver Riddle struct PassTiming : public PassInstrumentation { 1716558f80cSRiver Riddle PassTiming(PassTimingDisplayMode displayMode) : displayMode(displayMode) {} 1726558f80cSRiver Riddle ~PassTiming() { print(); } 173e46ba31cSRiver Riddle 1746558f80cSRiver Riddle /// Setup the instrumentation hooks. 175*120509a6SRiver Riddle void runBeforePipeline(const OperationName &name, 176*120509a6SRiver Riddle uint64_t parentThreadID) override; 177*120509a6SRiver Riddle void runAfterPipeline(const OperationName &name, 178*120509a6SRiver Riddle uint64_t parentThreadID) override; 1794fb971a9SRiver Riddle void runBeforePass(Pass *pass, Operation *) override { startPassTimer(pass); } 1804fb971a9SRiver Riddle void runAfterPass(Pass *pass, Operation *) override; 1814fb971a9SRiver Riddle void runAfterPassFailed(Pass *pass, Operation *op) override { 1824fb971a9SRiver Riddle runAfterPass(pass, op); 1836558f80cSRiver Riddle } 1846558f80cSRiver Riddle void runBeforeAnalysis(llvm::StringRef name, AnalysisID *id, 1854fb971a9SRiver Riddle Operation *) override { 1866558f80cSRiver Riddle startAnalysisTimer(name, id); 1876558f80cSRiver Riddle } 1884fb971a9SRiver Riddle void runAfterAnalysis(llvm::StringRef, AnalysisID *, Operation *) override; 1896558f80cSRiver Riddle 1906558f80cSRiver Riddle /// Print and clear the timing results. 1916558f80cSRiver Riddle void print(); 1926558f80cSRiver Riddle 1936558f80cSRiver Riddle /// Start a new timer for the given pass. 1946558f80cSRiver Riddle void startPassTimer(Pass *pass); 1956558f80cSRiver Riddle 1966558f80cSRiver Riddle /// Start a new timer for the given analysis. 1976558f80cSRiver Riddle void startAnalysisTimer(llvm::StringRef name, AnalysisID *id); 1986558f80cSRiver Riddle 199*120509a6SRiver Riddle /// Pop the last active timer for the current thread. 200*120509a6SRiver Riddle Timer *popLastActiveTimer() { 201*120509a6SRiver Riddle auto tid = llvm::get_threadid(); 202*120509a6SRiver Riddle auto &activeTimers = activeThreadTimers[tid]; 203*120509a6SRiver Riddle assert(!activeTimers.empty() && "expected active timer"); 204*120509a6SRiver Riddle return activeTimers.pop_back_val(); 205*120509a6SRiver Riddle } 2066558f80cSRiver Riddle 2076558f80cSRiver Riddle /// Print the timing result in list mode. 2083a845be7SRiver Riddle void printResultsAsList(raw_ostream &os, Timer *root, TimeRecord totalTime); 2096558f80cSRiver Riddle 2106558f80cSRiver Riddle /// Print the timing result in pipeline mode. 2113a845be7SRiver Riddle void printResultsAsPipeline(raw_ostream &os, Timer *root, 2123a845be7SRiver Riddle TimeRecord totalTime); 2136558f80cSRiver Riddle 2148e7b683dSRiver Riddle /// Returns a timer for the provided identifier and name. 215*120509a6SRiver Riddle Timer *getTimer(const void *id, TimerKind kind, 216*120509a6SRiver Riddle std::function<std::string()> &&nameBuilder) { 2173a845be7SRiver Riddle auto tid = llvm::get_threadid(); 2183a845be7SRiver Riddle 2193a845be7SRiver Riddle // If there is no active timer then add to the root timer. 2203a845be7SRiver Riddle auto &activeTimers = activeThreadTimers[tid]; 221*120509a6SRiver Riddle Timer *parentTimer; 2223a845be7SRiver Riddle if (activeTimers.empty()) { 2233a845be7SRiver Riddle auto &rootTimer = rootTimers[tid]; 2243a845be7SRiver Riddle if (!rootTimer) 225*120509a6SRiver Riddle rootTimer.reset(new Timer("root", TimerKind::Pipeline)); 226*120509a6SRiver Riddle parentTimer = rootTimer.get(); 227*120509a6SRiver Riddle } else { 228*120509a6SRiver Riddle // Otherwise, add this to the active timer. 229*120509a6SRiver Riddle parentTimer = activeTimers.back(); 2308e7b683dSRiver Riddle } 2316558f80cSRiver Riddle 232*120509a6SRiver Riddle auto timer = parentTimer->getChildTimer(id, kind, std::move(nameBuilder)); 2333a845be7SRiver Riddle activeTimers.push_back(timer); 2343a845be7SRiver Riddle return timer; 2353a845be7SRiver Riddle } 2366558f80cSRiver Riddle 2373a845be7SRiver Riddle /// The root top level timers for each thread. 2383a845be7SRiver Riddle DenseMap<uint64_t, std::unique_ptr<Timer>> rootTimers; 2393a845be7SRiver Riddle 2403a845be7SRiver Riddle /// A stack of the currently active pass timers per thread. 2413a845be7SRiver Riddle DenseMap<uint64_t, SmallVector<Timer *, 4>> activeThreadTimers; 2426558f80cSRiver Riddle 2436558f80cSRiver Riddle /// The display mode to use when printing the timing results. 2446558f80cSRiver Riddle PassTimingDisplayMode displayMode; 245*120509a6SRiver Riddle 246*120509a6SRiver Riddle /// A mapping of pipeline timers that need to be merged into the parent 247*120509a6SRiver Riddle /// collection. The timers are mapped to the thread id of the parent thread to 248*120509a6SRiver Riddle /// merge into. 249*120509a6SRiver Riddle DenseMap<uint64_t, SmallVector<Timer::ChildrenMap::value_type, 4>> 250*120509a6SRiver Riddle pipelinesToMerge; 2516558f80cSRiver Riddle }; 2526558f80cSRiver Riddle } // end anonymous namespace 253e46ba31cSRiver Riddle 254*120509a6SRiver Riddle void PassTiming::runBeforePipeline(const OperationName &name, 255*120509a6SRiver Riddle uint64_t parentThreadID) { 256*120509a6SRiver Riddle // We don't actually want to time the piplelines, they gather their total 257*120509a6SRiver Riddle // from their held passes. 258*120509a6SRiver Riddle getTimer(name.getAsOpaquePointer(), TimerKind::Pipeline, 259*120509a6SRiver Riddle [&] { return ("'" + name.getStringRef() + "' Pipeline").str(); }); 260*120509a6SRiver Riddle } 261*120509a6SRiver Riddle 262*120509a6SRiver Riddle void PassTiming::runAfterPipeline(const OperationName &name, 263*120509a6SRiver Riddle uint64_t parentThreadID) { 264*120509a6SRiver Riddle // Pop the timer for the pipeline. 265*120509a6SRiver Riddle auto tid = llvm::get_threadid(); 266*120509a6SRiver Riddle auto &activeTimers = activeThreadTimers[tid]; 267*120509a6SRiver Riddle assert(!activeTimers.empty() && "expected active timer"); 268*120509a6SRiver Riddle activeTimers.pop_back(); 269*120509a6SRiver Riddle 270*120509a6SRiver Riddle // If the current thread is the same as the parent, there is nothing left to 271*120509a6SRiver Riddle // do. 272*120509a6SRiver Riddle if (tid == parentThreadID) 273*120509a6SRiver Riddle return; 274*120509a6SRiver Riddle 275*120509a6SRiver Riddle // Otherwise, mark the pipeline timer for merging into the correct parent 276*120509a6SRiver Riddle // thread. 277*120509a6SRiver Riddle assert(activeTimers.empty() && "expected parent timer to be root"); 278*120509a6SRiver Riddle auto *parentTimer = rootTimers[tid].get(); 279*120509a6SRiver Riddle assert(parentTimer->children.size() == 1 && 280*120509a6SRiver Riddle parentTimer->children.count(name.getAsOpaquePointer()) && 281*120509a6SRiver Riddle "expected a single pipeline timer"); 282*120509a6SRiver Riddle pipelinesToMerge[parentThreadID].push_back( 283*120509a6SRiver Riddle std::move(*parentTimer->children.begin())); 284*120509a6SRiver Riddle rootTimers.erase(tid); 285*120509a6SRiver Riddle } 286*120509a6SRiver Riddle 287e46ba31cSRiver Riddle /// Start a new timer for the given pass. 288e46ba31cSRiver Riddle void PassTiming::startPassTimer(Pass *pass) { 289*120509a6SRiver Riddle auto kind = isAdaptorPass(pass) ? TimerKind::PipelineCollection 290*120509a6SRiver Riddle : TimerKind::PassOrAnalysis; 291*120509a6SRiver Riddle Timer *timer = getTimer(pass, kind, [pass]() -> std::string { 2925c036e68SRiver Riddle if (auto pipelineName = getAdaptorPassOpName(pass)) 293*120509a6SRiver Riddle return ("Pipeline Collection : ['" + *pipelineName + "']").str(); 2948e7b683dSRiver Riddle return pass->getName(); 2958e7b683dSRiver Riddle }); 296e46ba31cSRiver Riddle 2978e7b683dSRiver Riddle // We don't actually want to time the adaptor passes, they gather their total 2988e7b683dSRiver Riddle // from their held passes. 2998e7b683dSRiver Riddle if (!isAdaptorPass(pass)) 3008e7b683dSRiver Riddle timer->start(); 301e46ba31cSRiver Riddle } 302e46ba31cSRiver Riddle 303e46ba31cSRiver Riddle /// Start a new timer for the given analysis. 304e46ba31cSRiver Riddle void PassTiming::startAnalysisTimer(llvm::StringRef name, AnalysisID *id) { 305*120509a6SRiver Riddle Timer *timer = getTimer(id, TimerKind::PassOrAnalysis, 306*120509a6SRiver Riddle [name] { return "(A) " + name.str(); }); 3078e7b683dSRiver Riddle timer->start(); 308e46ba31cSRiver Riddle } 309e46ba31cSRiver Riddle 3108e7b683dSRiver Riddle /// Stop a pass timer. 3114fb971a9SRiver Riddle void PassTiming::runAfterPass(Pass *pass, Operation *) { 312*120509a6SRiver Riddle Timer *timer = popLastActiveTimer(); 3138e7b683dSRiver Riddle 3145c036e68SRiver Riddle // If this is an OpToOpPassAdaptorParallel, then we need to merge in the 315*120509a6SRiver Riddle // timing data for the pipelines running on other threads. 3165c036e68SRiver Riddle if (isa<OpToOpPassAdaptorParallel>(pass)) { 317*120509a6SRiver Riddle auto toMerge = pipelinesToMerge.find(llvm::get_threadid()); 318*120509a6SRiver Riddle if (toMerge != pipelinesToMerge.end()) { 319*120509a6SRiver Riddle for (auto &it : toMerge->second) 320*120509a6SRiver Riddle timer->mergeChild(std::move(it)); 321*120509a6SRiver Riddle pipelinesToMerge.erase(toMerge); 3223a845be7SRiver Riddle } 3233a845be7SRiver Riddle return; 3243a845be7SRiver Riddle } 3253a845be7SRiver Riddle 3268e7b683dSRiver Riddle // Adapator passes aren't timed directly, so we don't need to stop their 3278e7b683dSRiver Riddle // timers. 3288e7b683dSRiver Riddle if (!isAdaptorPass(pass)) 3298e7b683dSRiver Riddle timer->stop(); 330e46ba31cSRiver Riddle } 331e46ba31cSRiver Riddle 3328e7b683dSRiver Riddle /// Stop a timer. 3334fb971a9SRiver Riddle void PassTiming::runAfterAnalysis(llvm::StringRef, AnalysisID *, Operation *) { 334*120509a6SRiver Riddle popLastActiveTimer()->stop(); 335e46ba31cSRiver Riddle } 336e46ba31cSRiver Riddle 3378e7b683dSRiver Riddle /// Utility to print the timer heading information. 3383a845be7SRiver Riddle static void printTimerHeader(llvm::raw_ostream &os, TimeRecord total) { 339e46ba31cSRiver Riddle os << "===" << std::string(73, '-') << "===\n"; 340e46ba31cSRiver Riddle // Figure out how many spaces to description name. 341e46ba31cSRiver Riddle unsigned Padding = (80 - kPassTimingDescription.size()) / 2; 342e46ba31cSRiver Riddle os.indent(Padding) << kPassTimingDescription << '\n'; 343e46ba31cSRiver Riddle os << "===" << std::string(73, '-') << "===\n"; 344e46ba31cSRiver Riddle 3458e7b683dSRiver Riddle // Print the total time followed by the section headers. 3463a845be7SRiver Riddle os << llvm::format(" Total Execution Time: %5.4f seconds\n\n", total.wall); 3473a845be7SRiver Riddle if (total.user != total.wall) 3483a845be7SRiver Riddle os << " ---User Time---"; 3498e7b683dSRiver Riddle os << " ---Wall Time--- --- Name ---\n"; 3508e7b683dSRiver Riddle } 351e46ba31cSRiver Riddle 3528e7b683dSRiver Riddle /// Utility to print a single line entry in the timer output. 3538e7b683dSRiver Riddle static void printTimeEntry(raw_ostream &os, unsigned indent, StringRef name, 3543a845be7SRiver Riddle TimeRecord time, TimeRecord totalTime) { 3553a845be7SRiver Riddle time.print(os, totalTime); 3568e7b683dSRiver Riddle os.indent(indent) << name << "\n"; 3578e7b683dSRiver Riddle } 3588e7b683dSRiver Riddle 3598e7b683dSRiver Riddle /// Print out the current timing information. 3608e7b683dSRiver Riddle void PassTiming::print() { 3618e7b683dSRiver Riddle // Don't print anything if there is no timing data. 3623a845be7SRiver Riddle if (rootTimers.empty()) 3638e7b683dSRiver Riddle return; 3643a845be7SRiver Riddle 3653a845be7SRiver Riddle assert(rootTimers.size() == 1 && "expected one remaining root timer"); 3663a845be7SRiver Riddle auto &rootTimer = rootTimers.begin()->second; 3678e7b683dSRiver Riddle auto os = llvm::CreateInfoOutputFile(); 3688e7b683dSRiver Riddle 3698e7b683dSRiver Riddle // Print the timer header. 3703a845be7SRiver Riddle TimeRecord totalTime = rootTimer->getTotalTime(); 3718e7b683dSRiver Riddle printTimerHeader(*os, totalTime); 3728e7b683dSRiver Riddle 3738e7b683dSRiver Riddle // Defer to a specialized printer for each display mode. 3748e7b683dSRiver Riddle switch (displayMode) { 3758e7b683dSRiver Riddle case PassTimingDisplayMode::List: 3763a845be7SRiver Riddle printResultsAsList(*os, rootTimer.get(), totalTime); 3778e7b683dSRiver Riddle break; 3788e7b683dSRiver Riddle case PassTimingDisplayMode::Pipeline: 3793a845be7SRiver Riddle printResultsAsPipeline(*os, rootTimer.get(), totalTime); 3808e7b683dSRiver Riddle break; 3818e7b683dSRiver Riddle } 3828e7b683dSRiver Riddle printTimeEntry(*os, 0, "Total", totalTime, totalTime); 3838e7b683dSRiver Riddle os->flush(); 3848e7b683dSRiver Riddle 3853a845be7SRiver Riddle // Reset root timers. 3863a845be7SRiver Riddle rootTimers.clear(); 3873a845be7SRiver Riddle activeThreadTimers.clear(); 3888e7b683dSRiver Riddle } 3898e7b683dSRiver Riddle 3908e7b683dSRiver Riddle /// Print the timing result in list mode. 3913a845be7SRiver Riddle void PassTiming::printResultsAsList(raw_ostream &os, Timer *root, 3923a845be7SRiver Riddle TimeRecord totalTime) { 3933a845be7SRiver Riddle llvm::StringMap<TimeRecord> mergedTimings; 3948e7b683dSRiver Riddle 3958e7b683dSRiver Riddle std::function<void(Timer *)> addTimer = [&](Timer *timer) { 396*120509a6SRiver Riddle // Only add timing information for passes and analyses. 397*120509a6SRiver Riddle if (timer->kind == TimerKind::PassOrAnalysis) 3988e7b683dSRiver Riddle mergedTimings[timer->name] += timer->getTotalTime(); 3998e7b683dSRiver Riddle for (auto &children : timer->children) 4008e7b683dSRiver Riddle addTimer(children.second.get()); 4018e7b683dSRiver Riddle }; 4028e7b683dSRiver Riddle 4038e7b683dSRiver Riddle // Add each of the top level timers. 4043a845be7SRiver Riddle for (auto &topLevelTimer : root->children) 4058e7b683dSRiver Riddle addTimer(topLevelTimer.second.get()); 4068e7b683dSRiver Riddle 4073a845be7SRiver Riddle // Sort the timing information by wall time. 4083a845be7SRiver Riddle std::vector<std::pair<StringRef, TimeRecord>> timerNameAndTime; 4098e7b683dSRiver Riddle for (auto &it : mergedTimings) 4108e7b683dSRiver Riddle timerNameAndTime.emplace_back(it.first(), it.second); 4118e7b683dSRiver Riddle llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(), 4123a845be7SRiver Riddle [](const std::pair<StringRef, TimeRecord> *lhs, 4133a845be7SRiver Riddle const std::pair<StringRef, TimeRecord> *rhs) { 4148e7b683dSRiver Riddle return llvm::array_pod_sort_comparator<double>( 4153a845be7SRiver Riddle &rhs->second.wall, &lhs->second.wall); 4168e7b683dSRiver Riddle }); 4178e7b683dSRiver Riddle 4188e7b683dSRiver Riddle // Print the timing information sequentially. 4198e7b683dSRiver Riddle for (auto &timeData : timerNameAndTime) 4208e7b683dSRiver Riddle printTimeEntry(os, 0, timeData.first, timeData.second, totalTime); 421e46ba31cSRiver Riddle } 422e46ba31cSRiver Riddle 423e46ba31cSRiver Riddle /// Print the timing result in pipeline mode. 4243a845be7SRiver Riddle void PassTiming::printResultsAsPipeline(raw_ostream &os, Timer *root, 4253a845be7SRiver Riddle TimeRecord totalTime) { 4268e7b683dSRiver Riddle std::function<void(unsigned, Timer *)> printTimer = [&](unsigned indent, 4278e7b683dSRiver Riddle Timer *timer) { 428*120509a6SRiver Riddle // If this is a timer for a pipeline collection and the collection only has 429*120509a6SRiver Riddle // one pipeline child, then only print the child. 430*120509a6SRiver Riddle if (timer->kind == TimerKind::PipelineCollection && 431*120509a6SRiver Riddle timer->children.size() == 1) 432*120509a6SRiver Riddle return printTimer(indent, timer->children.begin()->second.get()); 433*120509a6SRiver Riddle 4348e7b683dSRiver Riddle printTimeEntry(os, indent, timer->name, timer->getTotalTime(), totalTime); 435*120509a6SRiver Riddle 436*120509a6SRiver Riddle // If this timer is a pipeline, then print the children in-order. 437*120509a6SRiver Riddle if (timer->kind == TimerKind::Pipeline) { 438*120509a6SRiver Riddle for (auto &child : timer->children) 439*120509a6SRiver Riddle printTimer(indent + 2, child.second.get()); 440*120509a6SRiver Riddle return; 441*120509a6SRiver Riddle } 442*120509a6SRiver Riddle 443*120509a6SRiver Riddle // Otherwise, sort the children by name to give a deterministic ordering 444*120509a6SRiver Riddle // when emitting the time. 445*120509a6SRiver Riddle SmallVector<Timer *, 4> children; 446*120509a6SRiver Riddle children.reserve(timer->children.size()); 447*120509a6SRiver Riddle for (auto &child : timer->children) 448*120509a6SRiver Riddle children.push_back(child.second.get()); 449*120509a6SRiver Riddle llvm::array_pod_sort(children.begin(), children.end(), 450*120509a6SRiver Riddle [](Timer *const *lhs, Timer *const *rhs) { 451*120509a6SRiver Riddle return (*lhs)->name.compare((*rhs)->name); 452*120509a6SRiver Riddle }); 453*120509a6SRiver Riddle for (auto &child : children) 454*120509a6SRiver Riddle printTimer(indent + 2, child); 455e46ba31cSRiver Riddle }; 456e46ba31cSRiver Riddle 4578e7b683dSRiver Riddle // Print each of the top level timers. 4583a845be7SRiver Riddle for (auto &topLevelTimer : root->children) 4598e7b683dSRiver Riddle printTimer(0, topLevelTimer.second.get()); 460e46ba31cSRiver Riddle } 4616558f80cSRiver Riddle 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. 4686558f80cSRiver Riddle void PassManager::enableTiming(PassTimingDisplayMode displayMode) { 4696558f80cSRiver Riddle // Check if pass timing is already enabled. 4706558f80cSRiver Riddle if (passTiming) 4716558f80cSRiver Riddle return; 4726558f80cSRiver Riddle addInstrumentation(new PassTiming(displayMode)); 4736558f80cSRiver Riddle passTiming = true; 4746558f80cSRiver Riddle } 475