1*e46ba31cSRiver Riddle //===- PassTiming.cpp -----------------------------------------------------===//
2*e46ba31cSRiver Riddle //
3*e46ba31cSRiver Riddle // Copyright 2019 The MLIR Authors.
4*e46ba31cSRiver Riddle //
5*e46ba31cSRiver Riddle // Licensed under the Apache License, Version 2.0 (the "License");
6*e46ba31cSRiver Riddle // you may not use this file except in compliance with the License.
7*e46ba31cSRiver Riddle // You may obtain a copy of the License at
8*e46ba31cSRiver Riddle //
9*e46ba31cSRiver Riddle //   http://www.apache.org/licenses/LICENSE-2.0
10*e46ba31cSRiver Riddle //
11*e46ba31cSRiver Riddle // Unless required by applicable law or agreed to in writing, software
12*e46ba31cSRiver Riddle // distributed under the License is distributed on an "AS IS" BASIS,
13*e46ba31cSRiver Riddle // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*e46ba31cSRiver Riddle // See the License for the specific language governing permissions and
15*e46ba31cSRiver Riddle // limitations under the License.
16*e46ba31cSRiver Riddle // =============================================================================
17*e46ba31cSRiver Riddle 
18*e46ba31cSRiver Riddle #include "mlir/Pass/PassTiming.h"
19*e46ba31cSRiver Riddle #include "PassDetail.h"
20*e46ba31cSRiver Riddle #include "llvm/ADT/SmallVector.h"
21*e46ba31cSRiver Riddle #include "llvm/ADT/Statistic.h"
22*e46ba31cSRiver Riddle #include "llvm/Support/Format.h"
23*e46ba31cSRiver Riddle #include "llvm/Support/FormatVariadic.h"
24*e46ba31cSRiver Riddle #include "llvm/Support/Timer.h"
25*e46ba31cSRiver Riddle 
26*e46ba31cSRiver Riddle using namespace mlir;
27*e46ba31cSRiver Riddle using namespace mlir::detail;
28*e46ba31cSRiver Riddle 
29*e46ba31cSRiver Riddle constexpr llvm::StringLiteral kPassTimingDescription =
30*e46ba31cSRiver Riddle     "... Pass execution timing report ...";
31*e46ba31cSRiver Riddle 
32*e46ba31cSRiver Riddle /// Utility function to return if a pass refers to an adaptor pass. Adaptor
33*e46ba31cSRiver Riddle /// passes are those that internally execute a pipeline, such as the
34*e46ba31cSRiver Riddle /// ModuleToFunctionPassAdaptor.
35*e46ba31cSRiver Riddle static bool isAdaptorPass(Pass *pass) {
36*e46ba31cSRiver Riddle   return isa<ModuleToFunctionPassAdaptor>(pass);
37*e46ba31cSRiver Riddle }
38*e46ba31cSRiver Riddle 
39*e46ba31cSRiver Riddle PassTiming::PassTiming(DisplayMode displayMode) : displayMode(displayMode) {}
40*e46ba31cSRiver Riddle 
41*e46ba31cSRiver Riddle PassTiming::~PassTiming() { print(); }
42*e46ba31cSRiver Riddle 
43*e46ba31cSRiver Riddle /// Print out the current timing information.
44*e46ba31cSRiver Riddle void PassTiming::print() {
45*e46ba31cSRiver Riddle   // Don't print anything if there is no timing data.
46*e46ba31cSRiver Riddle   if (passTimers.empty() && analysisTimers.empty())
47*e46ba31cSRiver Riddle     return;
48*e46ba31cSRiver Riddle 
49*e46ba31cSRiver Riddle   switch (displayMode) {
50*e46ba31cSRiver Riddle   case DisplayMode::List:
51*e46ba31cSRiver Riddle     printResultsAsList(*llvm::CreateInfoOutputFile());
52*e46ba31cSRiver Riddle     break;
53*e46ba31cSRiver Riddle   case DisplayMode::Pipeline:
54*e46ba31cSRiver Riddle     printResultsAsPipeline(*llvm::CreateInfoOutputFile());
55*e46ba31cSRiver Riddle     break;
56*e46ba31cSRiver Riddle   }
57*e46ba31cSRiver Riddle 
58*e46ba31cSRiver Riddle   // Reset and clear the timers.
59*e46ba31cSRiver Riddle   for (auto &timerPair : passTimers)
60*e46ba31cSRiver Riddle     timerPair.second->clear();
61*e46ba31cSRiver Riddle   for (auto &timerPair : analysisTimers)
62*e46ba31cSRiver Riddle     timerPair.second->clear();
63*e46ba31cSRiver Riddle   passTimers.clear();
64*e46ba31cSRiver Riddle   analysisTimers.clear();
65*e46ba31cSRiver Riddle }
66*e46ba31cSRiver Riddle 
67*e46ba31cSRiver Riddle /// Start a new timer for the given pass.
68*e46ba31cSRiver Riddle void PassTiming::startPassTimer(Pass *pass) {
69*e46ba31cSRiver Riddle   std::unique_ptr<llvm::Timer> &timer = passTimers[pass];
70*e46ba31cSRiver Riddle 
71*e46ba31cSRiver Riddle   /// If the timer doesn't exist then create a new one.
72*e46ba31cSRiver Riddle   if (!timer) {
73*e46ba31cSRiver Riddle     auto passName = pass->getName();
74*e46ba31cSRiver Riddle     timer.reset(new llvm::Timer(passName, passName));
75*e46ba31cSRiver Riddle   }
76*e46ba31cSRiver Riddle 
77*e46ba31cSRiver Riddle   activePass = pass;
78*e46ba31cSRiver Riddle   timer->startTimer();
79*e46ba31cSRiver Riddle }
80*e46ba31cSRiver Riddle 
81*e46ba31cSRiver Riddle /// Stop a timer for the given pass.
82*e46ba31cSRiver Riddle void PassTiming::stopPassTimer(Pass *pass) {
83*e46ba31cSRiver Riddle   std::unique_ptr<llvm::Timer> &timer = passTimers[pass];
84*e46ba31cSRiver Riddle   assert(timer && "expected valid timer to stop");
85*e46ba31cSRiver Riddle   timer->stopTimer();
86*e46ba31cSRiver Riddle   activePass = nullptr;
87*e46ba31cSRiver Riddle }
88*e46ba31cSRiver Riddle 
89*e46ba31cSRiver Riddle /// Start a new timer for the given analysis.
90*e46ba31cSRiver Riddle void PassTiming::startAnalysisTimer(llvm::StringRef name, AnalysisID *id) {
91*e46ba31cSRiver Riddle   auto &timer = analysisTimers[std::make_pair(id, activePass)];
92*e46ba31cSRiver Riddle 
93*e46ba31cSRiver Riddle   /// If the timer doesn't exist then create a new one.
94*e46ba31cSRiver Riddle   if (!timer)
95*e46ba31cSRiver Riddle     timer.reset(new llvm::Timer(name, Twine("(A) " + name).str()));
96*e46ba31cSRiver Riddle   timer->startTimer();
97*e46ba31cSRiver Riddle }
98*e46ba31cSRiver Riddle 
99*e46ba31cSRiver Riddle /// Stop a timer for the given analysis.
100*e46ba31cSRiver Riddle void PassTiming::stopAnalysisTimer(llvm::StringRef name, AnalysisID *id) {
101*e46ba31cSRiver Riddle   auto &timer = analysisTimers[std::make_pair(id, activePass)];
102*e46ba31cSRiver Riddle   assert(timer && "expected a valid timer to stop");
103*e46ba31cSRiver Riddle   timer->stopTimer();
104*e46ba31cSRiver Riddle }
105*e46ba31cSRiver Riddle 
106*e46ba31cSRiver Riddle /// Print the timing result in list mode.
107*e46ba31cSRiver Riddle void PassTiming::printResultsAsList(llvm::raw_ostream &os) {
108*e46ba31cSRiver Riddle   // Build a map of timer records uniqued by the timer name.
109*e46ba31cSRiver Riddle   llvm::StringMap<llvm::TimeRecord> records;
110*e46ba31cSRiver Riddle   auto addTimer = [&](llvm::Timer *timer) {
111*e46ba31cSRiver Riddle     auto it = records.try_emplace(timer->getName(), timer->getTotalTime());
112*e46ba31cSRiver Riddle     if (!it.second)
113*e46ba31cSRiver Riddle       it.first->second += timer->getTotalTime();
114*e46ba31cSRiver Riddle   };
115*e46ba31cSRiver Riddle 
116*e46ba31cSRiver Riddle   // Add all non-adaptor classes to the time records.
117*e46ba31cSRiver Riddle   for (auto &timerPair : passTimers)
118*e46ba31cSRiver Riddle     if (!isAdaptorPass(timerPair.first))
119*e46ba31cSRiver Riddle       addTimer(timerPair.second.get());
120*e46ba31cSRiver Riddle 
121*e46ba31cSRiver Riddle   // Add the analysis timers.
122*e46ba31cSRiver Riddle   for (auto &timerPair : analysisTimers)
123*e46ba31cSRiver Riddle     addTimer(timerPair.second.get());
124*e46ba31cSRiver Riddle 
125*e46ba31cSRiver Riddle   // Create a timer group for the records and print it out.
126*e46ba31cSRiver Riddle   llvm::TimerGroup timerGroup("pass", kPassTimingDescription, records);
127*e46ba31cSRiver Riddle   timerGroup.print(os);
128*e46ba31cSRiver Riddle }
129*e46ba31cSRiver Riddle 
130*e46ba31cSRiver Riddle /// Utility to print the heading information for the pipeline timer.
131*e46ba31cSRiver Riddle /// Note: This is a replication of the header generated by llvm::TimerGroup.
132*e46ba31cSRiver Riddle static void printPipelineTimerHeader(llvm::raw_ostream &os,
133*e46ba31cSRiver Riddle                                      llvm::TimeRecord &pipelineTotal) {
134*e46ba31cSRiver Riddle   os << "===" << std::string(73, '-') << "===\n";
135*e46ba31cSRiver Riddle   // Figure out how many spaces to description name.
136*e46ba31cSRiver Riddle   unsigned Padding = (80 - kPassTimingDescription.size()) / 2;
137*e46ba31cSRiver Riddle   os.indent(Padding) << kPassTimingDescription << '\n';
138*e46ba31cSRiver Riddle   os << "===" << std::string(73, '-') << "===\n";
139*e46ba31cSRiver Riddle 
140*e46ba31cSRiver Riddle   // Print the total time.
141*e46ba31cSRiver Riddle   os << llvm::format(
142*e46ba31cSRiver Riddle       "  Total Execution Time: %5.4f seconds (%5.4f wall clock)\n\n",
143*e46ba31cSRiver Riddle       pipelineTotal.getProcessTime(), pipelineTotal.getWallTime());
144*e46ba31cSRiver Riddle 
145*e46ba31cSRiver Riddle   // Add the headers for each time section.
146*e46ba31cSRiver Riddle   if (pipelineTotal.getUserTime())
147*e46ba31cSRiver Riddle     os << "   ---User Time---";
148*e46ba31cSRiver Riddle   if (pipelineTotal.getSystemTime())
149*e46ba31cSRiver Riddle     os << "   --System Time--";
150*e46ba31cSRiver Riddle   if (pipelineTotal.getProcessTime())
151*e46ba31cSRiver Riddle     os << "   --User+System--";
152*e46ba31cSRiver Riddle   os << "   ---Wall Time---";
153*e46ba31cSRiver Riddle   if (pipelineTotal.getMemUsed())
154*e46ba31cSRiver Riddle     os << "  ---Mem---";
155*e46ba31cSRiver Riddle   os << "  --- Name ---\n";
156*e46ba31cSRiver Riddle }
157*e46ba31cSRiver Riddle 
158*e46ba31cSRiver Riddle /// Print the timing result in pipeline mode.
159*e46ba31cSRiver Riddle void PassTiming::printResultsAsPipeline(llvm::raw_ostream &os) {
160*e46ba31cSRiver Riddle   // Collect the total time information for each of the non-adaptor passes.
161*e46ba31cSRiver Riddle   llvm::TimeRecord pipelineTotal;
162*e46ba31cSRiver Riddle   for (auto &timerPair : passTimers)
163*e46ba31cSRiver Riddle     if (!isAdaptorPass(timerPair.first))
164*e46ba31cSRiver Riddle       pipelineTotal += timerPair.second->getTotalTime();
165*e46ba31cSRiver Riddle 
166*e46ba31cSRiver Riddle   // Collect the analysis time records for each pass.
167*e46ba31cSRiver Riddle   llvm::DenseMap<Pass *, std::vector<llvm::Timer *>> passAnalyses;
168*e46ba31cSRiver Riddle   for (auto &timerPair : analysisTimers) {
169*e46ba31cSRiver Riddle     passAnalyses[timerPair.first.second].push_back(timerPair.second.get());
170*e46ba31cSRiver Riddle     pipelineTotal += timerPair.second->getTotalTime();
171*e46ba31cSRiver Riddle   }
172*e46ba31cSRiver Riddle   // Sort each of the analysis timers.
173*e46ba31cSRiver Riddle   for (auto &analysisPair : passAnalyses) {
174*e46ba31cSRiver Riddle     llvm::array_pod_sort(
175*e46ba31cSRiver Riddle         analysisPair.second.begin(), analysisPair.second.end(),
176*e46ba31cSRiver Riddle         [](llvm::Timer *const *lhsTimer, llvm::Timer *const *rhsTimer) -> int {
177*e46ba31cSRiver Riddle           return (*lhsTimer)->getDescription().compare(
178*e46ba31cSRiver Riddle               (*rhsTimer)->getDescription());
179*e46ba31cSRiver Riddle         });
180*e46ba31cSRiver Riddle   }
181*e46ba31cSRiver Riddle 
182*e46ba31cSRiver Riddle   // Print out timing header.
183*e46ba31cSRiver Riddle   printPipelineTimerHeader(os, pipelineTotal);
184*e46ba31cSRiver Riddle 
185*e46ba31cSRiver Riddle   // Print the formatted timing record.
186*e46ba31cSRiver Riddle   unsigned currentIndent = 0;
187*e46ba31cSRiver Riddle   auto printTimer = [&](llvm::StringRef name, llvm::TimeRecord timeRecord) {
188*e46ba31cSRiver Riddle     timeRecord.print(pipelineTotal, os);
189*e46ba31cSRiver Riddle     os.indent(currentIndent) << name;
190*e46ba31cSRiver Riddle     os << "\n";
191*e46ba31cSRiver Riddle   };
192*e46ba31cSRiver Riddle 
193*e46ba31cSRiver Riddle   // Utility to print the timing information for a pass and its analyses.
194*e46ba31cSRiver Riddle   auto printPassTimer = [&](Pass *pass, llvm::Timer *passTimer) {
195*e46ba31cSRiver Riddle     printTimer(passTimer->getDescription(), passTimer->getTotalTime());
196*e46ba31cSRiver Riddle 
197*e46ba31cSRiver Riddle     // Print the computed analyses for this pass.
198*e46ba31cSRiver Riddle     currentIndent += 2;
199*e46ba31cSRiver Riddle     for (llvm::Timer *timer : passAnalyses[pass])
200*e46ba31cSRiver Riddle       printTimer(timer->getDescription(), timer->getTotalTime());
201*e46ba31cSRiver Riddle     currentIndent -= 2;
202*e46ba31cSRiver Riddle   };
203*e46ba31cSRiver Riddle 
204*e46ba31cSRiver Riddle   // Print the total execution time.
205*e46ba31cSRiver Riddle   for (auto it = passTimers.begin(), e = passTimers.end(); it != e;) {
206*e46ba31cSRiver Riddle     // Handle a ModuleToFunctionAdaptor pass.
207*e46ba31cSRiver Riddle     if (isa<ModuleToFunctionPassAdaptor>(it->first)) {
208*e46ba31cSRiver Riddle       // Print the time for this adaptor as the accumulation of each of the
209*e46ba31cSRiver Riddle       // nested function passes.
210*e46ba31cSRiver Riddle       llvm::TimeRecord total;
211*e46ba31cSRiver Riddle       for (auto fpIt = ++it; fpIt != e && isa<FunctionPassBase>(fpIt->first);) {
212*e46ba31cSRiver Riddle         total += fpIt->second->getTotalTime();
213*e46ba31cSRiver Riddle         for (llvm::Timer *timer : passAnalyses[(fpIt++)->first])
214*e46ba31cSRiver Riddle           total += timer->getTotalTime();
215*e46ba31cSRiver Riddle       }
216*e46ba31cSRiver Riddle       printTimer("Function Pipeline", total);
217*e46ba31cSRiver Riddle 
218*e46ba31cSRiver Riddle       // Update the indent and print the time for each of the function passes
219*e46ba31cSRiver Riddle       // within the pipeline.
220*e46ba31cSRiver Riddle       currentIndent += 2;
221*e46ba31cSRiver Riddle       for (; it != e && isa<FunctionPassBase>(it->first); ++it)
222*e46ba31cSRiver Riddle         printPassTimer(it->first, it->second.get());
223*e46ba31cSRiver Riddle       currentIndent -= 2;
224*e46ba31cSRiver Riddle       continue;
225*e46ba31cSRiver Riddle     }
226*e46ba31cSRiver Riddle 
227*e46ba31cSRiver Riddle     // Otherwise, we print the pass timer directly.
228*e46ba31cSRiver Riddle     printPassTimer(it->first, it->second.get());
229*e46ba31cSRiver Riddle     ++it;
230*e46ba31cSRiver Riddle   }
231*e46ba31cSRiver Riddle 
232*e46ba31cSRiver Riddle   printTimer("Total\n", pipelineTotal);
233*e46ba31cSRiver Riddle   os.flush();
234*e46ba31cSRiver Riddle }
235