MLIR  22.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"
10 #include "mlir/Pass/PassManager.h"
11 #include "llvm/ADT/SmallVector.h"
12 #include "llvm/Support/Threading.h"
13 
14 #include <optional>
15 
16 using namespace mlir;
17 using namespace mlir::detail;
18 
19 //===----------------------------------------------------------------------===//
20 // PassTiming
21 //===----------------------------------------------------------------------===//
22 
23 namespace {
24 struct PassTiming : public PassInstrumentation {
25  PassTiming(TimingScope &timingScope) : rootScope(timingScope) {}
26  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  /// The timing manager owned by this instrumentation (in case timing was
38  /// enabled by the user on the pass manager without providing an external
39  /// timing manager). This *must* appear before the `ownedTimingScope` to
40  /// ensure the timing manager is destroyed *after* the scope, since the latter
41  /// may hold a timer that points into the former.
42  std::unique_ptr<TimingManager> ownedTimingManager;
43  TimingScope ownedTimingScope;
44 
45  /// A stack of the currently active timing scopes per thread.
47 
48  /// The root timing scope into which timing is reported.
49  TimingScope &rootScope;
50 
51  //===--------------------------------------------------------------------===//
52  // Pipeline
53  //===--------------------------------------------------------------------===//
54 
55  void runBeforePipeline(std::optional<OperationName> name,
56  const PipelineParentInfo &parentInfo) override {
57  auto tid = llvm::get_threadid();
58  auto &activeTimers = activeThreadTimers[tid];
59 
60  // Find the parent scope, either using the parent info or the root scope
61  // (e.g. in the case of the top-level pipeline).
62  TimingScope *parentScope;
63  auto it = parentTimerIndices.find(parentInfo);
64  if (it != parentTimerIndices.end())
65  parentScope = &activeThreadTimers[parentInfo.parentThreadID][it->second];
66  else
67  parentScope = &rootScope;
68 
69  // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of
70  // the operation.
71  const void *timerId = name ? name->getAsOpaquePointer() : nullptr;
72  activeTimers.push_back(parentScope->nest(timerId, [name] {
73  return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str();
74  }));
75  }
76 
77  void runAfterPipeline(std::optional<OperationName>,
78  const PipelineParentInfo &) override {
79  auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
80  assert(!activeTimers.empty() && "expected active timer");
81  activeTimers.pop_back();
82  }
83 
84  //===--------------------------------------------------------------------===//
85  // Pass
86  //===--------------------------------------------------------------------===//
87 
88  void runBeforePass(Pass *pass, Operation *) override {
89  auto tid = llvm::get_threadid();
90  auto &activeTimers = activeThreadTimers[tid];
91  auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
92 
93  if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) {
94  parentTimerIndices[{tid, pass}] = activeTimers.size();
95  auto scope =
96  parentScope.nest(pass->getThreadingSiblingOrThis(),
97  [adaptor]() { return adaptor->getAdaptorName(); });
98  if (adaptor->getPassManagers().size() <= 1)
99  scope.hide();
100  activeTimers.push_back(std::move(scope));
101  } else {
102  activeTimers.push_back(
103  parentScope.nest(pass->getThreadingSiblingOrThis(),
104  [pass]() { return std::string(pass->getName()); }));
105  }
106  }
107 
108  void runAfterPass(Pass *pass, Operation *) override {
109  auto tid = llvm::get_threadid();
110  if (isa<OpToOpPassAdaptor>(pass))
111  parentTimerIndices.erase({tid, pass});
112  auto &activeTimers = activeThreadTimers[tid];
113  assert(!activeTimers.empty() && "expected active timer");
114  activeTimers.pop_back();
115  }
116 
117  void runAfterPassFailed(Pass *pass, Operation *op) override {
118  runAfterPass(pass, op);
119  }
120 
121  //===--------------------------------------------------------------------===//
122  // Analysis
123  //===--------------------------------------------------------------------===//
124 
125  void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override {
126  auto tid = llvm::get_threadid();
127  auto &activeTimers = activeThreadTimers[tid];
128  auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
129  activeTimers.push_back(parentScope.nest(
130  id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); }));
131  }
132 
133  void runAfterAnalysis(StringRef, TypeID, Operation *) override {
134  auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
135  assert(!activeTimers.empty() && "expected active timer");
136  activeTimers.pop_back();
137  }
138 };
139 } // namespace
140 
141 //===----------------------------------------------------------------------===//
142 // PassManager
143 //===----------------------------------------------------------------------===//
144 
145 /// Add an instrumentation to time the execution of passes and the computation
146 /// of analyses.
148  if (!timingScope)
149  return;
150  addInstrumentation(std::make_unique<PassTiming>(timingScope));
151 }
152 
153 /// Add an instrumentation to time the execution of passes and the computation
154 /// of analyses.
155 void PassManager::enableTiming(std::unique_ptr<TimingManager> tm) {
156  if (!tm->getRootTimer())
157  return; // no need to keep the timing manager around if it's disabled
158  addInstrumentation(std::make_unique<PassTiming>(std::move(tm)));
159 }
160 
161 /// Add an instrumentation to time the execution of passes and the computation
162 /// of analyses.
164  auto tm = std::make_unique<DefaultTimingManager>();
165  tm->setEnabled(true);
166  enableTiming(std::move(tm));
167 }
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
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.
Definition: PassTiming.cpp:163
The abstract base pass class.
Definition: Pass.h:51
const Pass * getThreadingSiblingOrThis() const
Returns the thread sibling of this pass, or the pass itself it has no sibling.
Definition: Pass.h:158
An RAII-style wrapper around a timer that ensures the timer is properly started and stopped.
Definition: Timing.h:272
TimingScope nest(Args... args)
Create a nested timing scope.
Definition: Timing.h:311
void hide()
Hide the timer in timing reports and directly show its children.
Definition: Timing.h:316
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:107
AttrTypeReplacer.
Include the generated interface declarations.