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