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 { 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 /// 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 55 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 80 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 91 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 111 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 120 void runAfterPassFailed(Pass *pass, Operation *op) override { 121 runAfterPass(pass, op); 122 } 123 124 //===--------------------------------------------------------------------===// 125 // Analysis 126 //===--------------------------------------------------------------------===// 127 128 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 136 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. 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. 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. 166 void PassManager::enableTiming() { 167 auto tm = std::make_unique<DefaultTimingManager>(); 168 tm->setEnabled(true); 169 enableTiming(std::move(tm)); 170 } 171