1 //===- PassTiming.cpp -----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "PassDetail.h"
10 #include "mlir/Pass/PassManager.h"
11 #include "llvm/ADT/SmallVector.h"
12 #include "llvm/Support/Threading.h"
13
14 #include <chrono>
15
16 using namespace mlir;
17 using namespace mlir::detail;
18
19 //===----------------------------------------------------------------------===//
20 // PassTiming
21 //===----------------------------------------------------------------------===//
22
23 namespace {
24 struct PassTiming : public PassInstrumentation {
PassTiming__anon38759e540111::PassTiming25 PassTiming(TimingScope &timingScope) : rootScope(timingScope) {}
PassTiming__anon38759e540111::PassTiming26 PassTiming(std::unique_ptr<TimingManager> tm)
27 : ownedTimingManager(std::move(tm)),
28 ownedTimingScope(ownedTimingManager->getRootScope()),
29 rootScope(ownedTimingScope) {}
30
31 /// If a pass can spawn additional work on other threads, it records the
32 /// index to its currently active timer here. Passes that run on a
33 /// newly-forked thread will check this list to find the active timer of the
34 /// parent thread into which the new thread should be nested.
35 DenseMap<PipelineParentInfo, unsigned> parentTimerIndices;
36
37 /// A stack of the currently active timing scopes per thread.
38 DenseMap<uint64_t, SmallVector<TimingScope, 4>> activeThreadTimers;
39
40 /// The timing manager owned by this instrumentation (in case timing was
41 /// enabled by the user on the pass manager without providing an external
42 /// timing manager). This *must* appear before the `ownedTimingScope` to
43 /// ensure the timing manager is destroyed *after* the scope, since the latter
44 /// may hold a timer that points into the former.
45 std::unique_ptr<TimingManager> ownedTimingManager;
46 TimingScope ownedTimingScope;
47
48 /// The root timing scope into which timing is reported.
49 TimingScope &rootScope;
50
51 //===--------------------------------------------------------------------===//
52 // Pipeline
53 //===--------------------------------------------------------------------===//
54
runBeforePipeline__anon38759e540111::PassTiming55 void runBeforePipeline(Identifier name,
56 const PipelineParentInfo &parentInfo) override {
57 auto tid = llvm::get_threadid();
58 auto &activeTimers = activeThreadTimers[tid];
59
60 TimingScope *parentScope;
61 if (activeTimers.empty()) {
62 auto it = parentTimerIndices.find(parentInfo);
63 if (it != parentTimerIndices.end())
64 parentScope =
65 &activeThreadTimers[parentInfo.parentThreadID][it->second];
66 else
67 parentScope = &rootScope;
68 } else {
69 parentScope = &activeTimers.back();
70 }
71 activeTimers.push_back(parentScope->nest(name.getAsOpaquePointer(), [name] {
72 return ("'" + name.strref() + "' Pipeline").str();
73 }));
74 }
75
runAfterPipeline__anon38759e540111::PassTiming76 void runAfterPipeline(Identifier, const PipelineParentInfo &) override {
77 auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
78 assert(!activeTimers.empty() && "expected active timer");
79 activeTimers.pop_back();
80 }
81
82 //===--------------------------------------------------------------------===//
83 // Pass
84 //===--------------------------------------------------------------------===//
85
runBeforePass__anon38759e540111::PassTiming86 void runBeforePass(Pass *pass, Operation *) override {
87 auto tid = llvm::get_threadid();
88 auto &activeTimers = activeThreadTimers[tid];
89 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
90
91 if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) {
92 parentTimerIndices[{tid, pass}] = activeTimers.size();
93 auto scope =
94 parentScope.nest(pass->getThreadingSiblingOrThis(),
95 [adaptor]() { return adaptor->getAdaptorName(); });
96 if (adaptor->getPassManagers().size() <= 1)
97 scope.hide();
98 activeTimers.push_back(std::move(scope));
99 } else {
100 activeTimers.push_back(
101 parentScope.nest(pass->getThreadingSiblingOrThis(),
102 [pass]() { return std::string(pass->getName()); }));
103 }
104 }
105
runAfterPass__anon38759e540111::PassTiming106 void runAfterPass(Pass *pass, Operation *) override {
107 auto tid = llvm::get_threadid();
108 if (isa<OpToOpPassAdaptor>(pass))
109 parentTimerIndices.erase({tid, pass});
110 auto &activeTimers = activeThreadTimers[tid];
111 assert(!activeTimers.empty() && "expected active timer");
112 activeTimers.pop_back();
113 }
114
runAfterPassFailed__anon38759e540111::PassTiming115 void runAfterPassFailed(Pass *pass, Operation *op) override {
116 runAfterPass(pass, op);
117 }
118
119 //===--------------------------------------------------------------------===//
120 // Analysis
121 //===--------------------------------------------------------------------===//
122
runBeforeAnalysis__anon38759e540111::PassTiming123 void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override {
124 auto tid = llvm::get_threadid();
125 auto &activeTimers = activeThreadTimers[tid];
126 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
127 activeTimers.push_back(parentScope.nest(
128 id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); }));
129 }
130
runAfterAnalysis__anon38759e540111::PassTiming131 void runAfterAnalysis(StringRef, TypeID, Operation *) override {
132 auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
133 assert(!activeTimers.empty() && "expected active timer");
134 activeTimers.pop_back();
135 }
136 };
137 } // namespace
138
139 //===----------------------------------------------------------------------===//
140 // PassManager
141 //===----------------------------------------------------------------------===//
142
143 /// Add an instrumentation to time the execution of passes and the computation
144 /// of analyses.
enableTiming(TimingScope & timingScope)145 void PassManager::enableTiming(TimingScope &timingScope) {
146 if (!timingScope)
147 return;
148 addInstrumentation(std::make_unique<PassTiming>(timingScope));
149 }
150
151 /// Add an instrumentation to time the execution of passes and the computation
152 /// of analyses.
enableTiming(std::unique_ptr<TimingManager> tm)153 void PassManager::enableTiming(std::unique_ptr<TimingManager> tm) {
154 if (!tm->getRootTimer())
155 return; // no need to keep the timing manager around if it's disabled
156 addInstrumentation(std::make_unique<PassTiming>(std::move(tm)));
157 }
158
159 /// Add an instrumentation to time the execution of passes and the computation
160 /// of analyses.
enableTiming()161 void PassManager::enableTiming() {
162 auto tm = std::make_unique<DefaultTimingManager>();
163 tm->setEnabled(true);
164 enableTiming(std::move(tm));
165 }
166