xref: /llvm-project/mlir/lib/Pass/PassTiming.cpp (revision 008999ee8c3259ad1d7280eb4350e59ce57eb585)
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   /// The timing manager owned by this instrumentation (in case timing was
39   /// enabled by the user on the pass manager without providing an external
40   /// timing manager). This *must* appear before the `ownedTimingScope` to
41   /// ensure the timing manager is destroyed *after* the scope, since the latter
42   /// may hold a timer that points into the former.
43   std::unique_ptr<TimingManager> ownedTimingManager;
44   TimingScope ownedTimingScope;
45 
46   /// A stack of the currently active timing scopes per thread.
47   DenseMap<uint64_t, SmallVector<TimingScope, 4>> activeThreadTimers;
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.
148 void PassManager::enableTiming(TimingScope &timingScope) {
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.
164 void PassManager::enableTiming() {
165   auto tm = std::make_unique<DefaultTimingManager>();
166   tm->setEnabled(true);
167   enableTiming(std::move(tm));
168 }
169