MLIR  19.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 <chrono>
15 #include <optional>
16 
17 using namespace mlir;
18 using namespace mlir::detail;
19 
20 //===----------------------------------------------------------------------===//
21 // PassTiming
22 //===----------------------------------------------------------------------===//
23 
24 namespace {
25 struct PassTiming : public PassInstrumentation {
26  PassTiming(TimingScope &timingScope) : rootScope(timingScope) {}
27  PassTiming(std::unique_ptr<TimingManager> tm)
28  : ownedTimingManager(std::move(tm)),
29  ownedTimingScope(ownedTimingManager->getRootScope()),
30  rootScope(ownedTimingScope) {}
31 
32  /// If a pass can spawn additional work on other threads, it records the
33  /// index to its currently active timer here. Passes that run on a
34  /// newly-forked thread will check this list to find the active timer of the
35  /// parent thread into which the new thread should be nested.
36  DenseMap<PipelineParentInfo, unsigned> parentTimerIndices;
37 
38  /// A stack of the currently active timing scopes per thread.
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  /// The root timing scope into which timing is reported.
50  TimingScope &rootScope;
51 
52  //===--------------------------------------------------------------------===//
53  // Pipeline
54  //===--------------------------------------------------------------------===//
55 
56  void runBeforePipeline(std::optional<OperationName> name,
57  const PipelineParentInfo &parentInfo) override {
58  auto tid = llvm::get_threadid();
59  auto &activeTimers = activeThreadTimers[tid];
60 
61  // Find the parent scope, either using the parent info or the root scope
62  // (e.g. in the case of the top-level pipeline).
63  TimingScope *parentScope;
64  auto it = parentTimerIndices.find(parentInfo);
65  if (it != parentTimerIndices.end())
66  parentScope = &activeThreadTimers[parentInfo.parentThreadID][it->second];
67  else
68  parentScope = &rootScope;
69 
70  // Use nullptr to anchor op-agnostic pipelines, otherwise use the name of
71  // the operation.
72  const void *timerId = name ? name->getAsOpaquePointer() : nullptr;
73  activeTimers.push_back(parentScope->nest(timerId, [name] {
74  return ("'" + (name ? name->getStringRef() : "any") + "' Pipeline").str();
75  }));
76  }
77 
78  void runAfterPipeline(std::optional<OperationName>,
79  const PipelineParentInfo &) override {
80  auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
81  assert(!activeTimers.empty() && "expected active timer");
82  activeTimers.pop_back();
83  }
84 
85  //===--------------------------------------------------------------------===//
86  // Pass
87  //===--------------------------------------------------------------------===//
88 
89  void runBeforePass(Pass *pass, Operation *) override {
90  auto tid = llvm::get_threadid();
91  auto &activeTimers = activeThreadTimers[tid];
92  auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
93 
94  if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(pass)) {
95  parentTimerIndices[{tid, pass}] = activeTimers.size();
96  auto scope =
97  parentScope.nest(pass->getThreadingSiblingOrThis(),
98  [adaptor]() { return adaptor->getAdaptorName(); });
99  if (adaptor->getPassManagers().size() <= 1)
100  scope.hide();
101  activeTimers.push_back(std::move(scope));
102  } else {
103  activeTimers.push_back(
104  parentScope.nest(pass->getThreadingSiblingOrThis(),
105  [pass]() { return std::string(pass->getName()); }));
106  }
107  }
108 
109  void runAfterPass(Pass *pass, Operation *) override {
110  auto tid = llvm::get_threadid();
111  if (isa<OpToOpPassAdaptor>(pass))
112  parentTimerIndices.erase({tid, pass});
113  auto &activeTimers = activeThreadTimers[tid];
114  assert(!activeTimers.empty() && "expected active timer");
115  activeTimers.pop_back();
116  }
117 
118  void runAfterPassFailed(Pass *pass, Operation *op) override {
119  runAfterPass(pass, op);
120  }
121 
122  //===--------------------------------------------------------------------===//
123  // Analysis
124  //===--------------------------------------------------------------------===//
125 
126  void runBeforeAnalysis(StringRef name, TypeID id, Operation *) override {
127  auto tid = llvm::get_threadid();
128  auto &activeTimers = activeThreadTimers[tid];
129  auto &parentScope = activeTimers.empty() ? rootScope : activeTimers.back();
130  activeTimers.push_back(parentScope.nest(
131  id.getAsOpaquePointer(), [name] { return "(A) " + name.str(); }));
132  }
133 
134  void runAfterAnalysis(StringRef, TypeID, Operation *) override {
135  auto &activeTimers = activeThreadTimers[llvm::get_threadid()];
136  assert(!activeTimers.empty() && "expected active timer");
137  activeTimers.pop_back();
138  }
139 };
140 } // namespace
141 
142 //===----------------------------------------------------------------------===//
143 // PassManager
144 //===----------------------------------------------------------------------===//
145 
146 /// Add an instrumentation to time the execution of passes and the computation
147 /// of analyses.
149  if (!timingScope)
150  return;
151  addInstrumentation(std::make_unique<PassTiming>(timingScope));
152 }
153 
154 /// Add an instrumentation to time the execution of passes and the computation
155 /// of analyses.
156 void PassManager::enableTiming(std::unique_ptr<TimingManager> tm) {
157  if (!tm->getRootTimer())
158  return; // no need to keep the timing manager around if it's disabled
159  addInstrumentation(std::make_unique<PassTiming>(std::move(tm)));
160 }
161 
162 /// Add an instrumentation to time the execution of passes and the computation
163 /// of analyses.
165  auto tm = std::make_unique<DefaultTimingManager>();
166  tm->setEnabled(true);
167  enableTiming(std::move(tm));
168 }
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:164
The abstract base pass class.
Definition: Pass.h:52
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:104
Detect if any of the given parameter types has a sub-element handler.
Include the generated interface declarations.