MLIR 24.0.0git
PassTiming.cpp
Go to the documentation of this file.
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"
12#include "llvm/Support/RWMutex.h"
13#include "llvm/Support/Threading.h"
14
15#include <deque>
16#include <optional>
17
18using namespace mlir;
19using namespace mlir::detail;
20
21//===----------------------------------------------------------------------===//
22// PassTiming
23//===----------------------------------------------------------------------===//
24
25namespace {
26struct PassTiming : public PassInstrumentation {
27 PassTiming(TimingScope &timingScope) : rootScope(timingScope) {}
28 PassTiming(std::unique_ptr<TimingManager> tm)
29 : ownedTimingManager(std::move(tm)),
30 ownedTimingScope(ownedTimingManager->getRootScope()),
31 rootScope(ownedTimingScope) {}
32
33 bool isThreadSafe() const override { return rootScope.isThreadSafe(); }
34
35 /// Published adaptor scopes remain alive until their nested pipelines have
36 /// joined. The deque below keeps their addresses stable when a worker helps
37 /// execute another task while waiting for nested parallel work.
39 llvm::sys::SmartRWMutex<true> parentScopesMutex;
40
41 /// The timing manager owned by this instrumentation (in case timing was
42 /// enabled by the user on the pass manager without providing an external
43 /// timing manager). This *must* appear before the `ownedTimingScope` to
44 /// ensure the timing manager is destroyed *after* the scope, since the latter
45 /// may hold a timer that points into the former.
46 std::unique_ptr<TimingManager> ownedTimingManager;
47 TimingScope ownedTimingScope;
48
49 /// A stack of the currently active timing scopes per thread.
50 ThreadLocalCache<std::deque<TimingScope>> activeThreadTimers;
51
52 /// The root timing scope into which timing is reported.
53 TimingScope &rootScope;
54
55 //===--------------------------------------------------------------------===//
56 // Pipeline
57 //===--------------------------------------------------------------------===//
58
59 void runBeforePipeline(std::optional<OperationName> name,
60 const PipelineParentInfo &parentInfo) override {
61 auto &activeTimers = *activeThreadTimers;
62
63 // Find the parent scope, either using the parent info or the root scope
64 // (e.g. in the case of the top-level pipeline).
65 TimingScope *parentScope = &rootScope;
66 {
67 llvm::sys::SmartScopedReader<true> lock(parentScopesMutex);
68 auto it = parentScopes.find(parentInfo);
69 if (it != parentScopes.end())
70 parentScope = it->second;
71 }
72
73 // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of
74 // the operation.
75 const void *timerId = name ? name->getAsOpaquePointer() : nullptr;
76 activeTimers.push_back(parentScope->nest(timerId, [name] {
77 return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str();
78 }));
79 }
80
81 void runAfterPipeline(std::optional<OperationName>,
82 const PipelineParentInfo &) override {
83 auto &activeTimers = *activeThreadTimers;
84 assert(!activeTimers.empty() && "expected active timer");
85 activeTimers.pop_back();
86 }
87
88 //===--------------------------------------------------------------------===//
89 // Pass
90 //===--------------------------------------------------------------------===//
91
92 void runBeforePass(Pass *pass, Operation *) override {
93 auto &activeTimers = *activeThreadTimers;
94 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
95
96 if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) {
97 auto scope =
98 parentScope.nest(pass->getThreadingSiblingOrThis(),
99 [adaptor]() { return adaptor->getAdaptorName(); });
100 if (adaptor->getPassManagers().size() <= 1)
101 scope.hide();
102 activeTimers.push_back(std::move(scope));
103 llvm::sys::SmartScopedWriter<true> lock(parentScopesMutex);
104 parentScopes[{llvm::get_threadid(), pass}] = &activeTimers.back();
105 } else {
106 activeTimers.push_back(
107 parentScope.nest(pass->getThreadingSiblingOrThis(),
108 [pass]() { return std::string(pass->getName()); }));
109 }
110 }
111
112 void runAfterPass(Pass *pass, Operation *) override {
113 if (isa<OpToOpPassAdaptor>(pass)) {
114 llvm::sys::SmartScopedWriter<true> lock(parentScopesMutex);
115 parentScopes.erase({llvm::get_threadid(), pass});
116 }
117 auto &activeTimers = *activeThreadTimers;
118 assert(!activeTimers.empty() && "expected active timer");
119 activeTimers.pop_back();
120 }
121
122 void runAfterPassFailed(Pass *pass, Operation *op) override {
123 runAfterPass(pass, op);
124 }
125
126 //===--------------------------------------------------------------------===//
127 // Analysis
128 //===--------------------------------------------------------------------===//
129
130 void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override {
131 auto &activeTimers = *activeThreadTimers;
132 auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
133 activeTimers.push_back(parentScope.nest(
134 id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); }));
135 }
136
137 void runAfterAnalysis(StringRef, TypeID, Operation *) override {
138 auto &activeTimers = *activeThreadTimers;
139 assert(!activeTimers.empty() && "expected active timer");
140 activeTimers.pop_back();
141 }
142};
143} // namespace
144
145//===----------------------------------------------------------------------===//
146// PassManager
147//===----------------------------------------------------------------------===//
148
149/// Add an instrumentation to time the execution of passes and the computation
150/// of analyses.
152 if (!timingScope)
153 return;
154 addInstrumentation(std::make_unique<PassTiming>(timingScope));
155}
156
157/// Add an instrumentation to time the execution of passes and the computation
158/// of analyses.
159void PassManager::enableTiming(std::unique_ptr<TimingManager> tm) {
160 if (!tm->getRootTimer())
161 return; // no need to keep the timing manager around if it's disabled
162 addInstrumentation(std::make_unique<PassTiming>(std::move(tm)));
163}
164
165/// Add an instrumentation to time the execution of passes and the computation
166/// of analyses.
168 auto tm = std::make_unique<DefaultTimingManager>();
169 tm->setEnabled(true);
170 enableTiming(std::move(tm));
171}
PassInstrumentation provides several entry points into the pass manager infrastructure.
void enableTiming()
Add an instrumentation to time the execution of passes and the computation of analyses.
void addInstrumentation(std::unique_ptr< PassInstrumentation > pi)
Add the provided instrumentation to the pass manager.
Definition Pass.cpp:1113
const Pass * getThreadingSiblingOrThis() const
Returns the thread sibling of this pass, or the pass itself it has no sibling.
Definition Pass.h:159
An RAII-style wrapper around a timer that ensures the timer is properly started and stopped.
Definition Timing.h:281
TimingScope nest(Args... args)
Create a nested timing scope.
Definition Timing.h:323
void hide()
Hide the timer in timing reports and directly show its children.
Definition Timing.h:328
AttrTypeReplacer.
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123