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__anon2e0843fe0111::PassTiming25 PassTiming(TimingScope &timingScope) : rootScope(timingScope) {}
PassTiming__anon2e0843fe0111::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__anon2e0843fe0111::PassTiming55 void runBeforePipeline(Optional<OperationName> 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
72 // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of
73 // the operation.
74 const void *timerId = name ? name->getAsOpaquePointer() : nullptr;
75 activeTimers.push_back(parentScope->nest(timerId, [name] {
76 return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str();
77 }));
78 }
79
runAfterPipeline__anon2e0843fe0111::PassTiming80 void runAfterPipeline(Optional<OperationName>,
81 const PipelineParentInfo &) override {
82 auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
83 assert(!activeTimers.empty() && "expected active timer");
84 activeTimers.pop_back();
85 }
86
87 //===--------------------------------------------------------------------===//
88 // Pass
89 //===--------------------------------------------------------------------===//
90
runBeforePass__anon2e0843fe0111::PassTiming91 void runBeforePass(Pass *pass, Operation *) override {
92 auto tid = llvm::get_threadid();
93 auto &activeTimers = activeThreadTimers[tid];
94 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
95
96 if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) {
97 parentTimerIndices[{tid, pass}] = activeTimers.size();
98 auto scope =
99 parentScope.nest(pass->getThreadingSiblingOrThis(),
100 [adaptor]() { return adaptor->getAdaptorName(); });
101 if (adaptor->getPassManagers().size() <= 1)
102 scope.hide();
103 activeTimers.push_back(std::move(scope));
104 } else {
105 activeTimers.push_back(
106 parentScope.nest(pass->getThreadingSiblingOrThis(),
107 [pass]() { return std::string(pass->getName()); }));
108 }
109 }
110
runAfterPass__anon2e0843fe0111::PassTiming111 void runAfterPass(Pass *pass, Operation *) override {
112 auto tid = llvm::get_threadid();
113 if (isa<OpToOpPassAdaptor>(pass))
114 parentTimerIndices.erase({tid, pass});
115 auto &activeTimers = activeThreadTimers[tid];
116 assert(!activeTimers.empty() && "expected active timer");
117 activeTimers.pop_back();
118 }
119
runAfterPassFailed__anon2e0843fe0111::PassTiming120 void runAfterPassFailed(Pass *pass, Operation *op) override {
121 runAfterPass(pass, op);
122 }
123
124 //===--------------------------------------------------------------------===//
125 // Analysis
126 //===--------------------------------------------------------------------===//
127
runBeforeAnalysis__anon2e0843fe0111::PassTiming128 void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override {
129 auto tid = llvm::get_threadid();
130 auto &activeTimers = activeThreadTimers[tid];
131 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
132 activeTimers.push_back(parentScope.nest(
133 id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); }));
134 }
135
runAfterAnalysis__anon2e0843fe0111::PassTiming136 void runAfterAnalysis(StringRef, TypeID, Operation *) override {
137 auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
138 assert(!activeTimers.empty() && "expected active timer");
139 activeTimers.pop_back();
140 }
141 };
142 } // namespace
143
144 //===----------------------------------------------------------------------===//
145 // PassManager
146 //===----------------------------------------------------------------------===//
147
148 /// Add an instrumentation to time the execution of passes and the computation
149 /// of analyses.
enableTiming(TimingScope & timingScope)150 void PassManager::enableTiming(TimingScope &timingScope) {
151 if (!timingScope)
152 return;
153 addInstrumentation(std::make_unique<PassTiming>(timingScope));
154 }
155
156 /// Add an instrumentation to time the execution of passes and the computation
157 /// of analyses.
enableTiming(std::unique_ptr<TimingManager> tm)158 void PassManager::enableTiming(std::unique_ptr<TimingManager> tm) {
159 if (!tm->getRootTimer())
160 return; // no need to keep the timing manager around if it's disabled
161 addInstrumentation(std::make_unique<PassTiming>(std::move(tm)));
162 }
163
164 /// Add an instrumentation to time the execution of passes and the computation
165 /// of analyses.
enableTiming()166 void PassManager::enableTiming() {
167 auto tm = std::make_unique<DefaultTimingManager>();
168 tm->setEnabled(true);
169 enableTiming(std::move(tm));
170 }
171