github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/Pass/PassTiming.cpp (about) 1 //===- PassTiming.cpp -----------------------------------------------------===// 2 // 3 // Copyright 2019 The MLIR Authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 // ============================================================================= 17 18 #include "PassDetail.h" 19 #include "mlir/Pass/PassManager.h" 20 #include "llvm/ADT/MapVector.h" 21 #include "llvm/ADT/SmallVector.h" 22 #include "llvm/ADT/Statistic.h" 23 #include "llvm/Support/Format.h" 24 #include "llvm/Support/FormatVariadic.h" 25 #include "llvm/Support/Threading.h" 26 #include <chrono> 27 28 using namespace mlir; 29 using namespace mlir::detail; 30 31 constexpr llvm::StringLiteral kPassTimingDescription = 32 "... Pass execution timing report ..."; 33 34 namespace { 35 /// Simple record class to record timing information. 36 struct TimeRecord { 37 TimeRecord(double wall = 0.0, double user = 0.0) : wall(wall), user(user) {} 38 39 TimeRecord &operator+=(const TimeRecord &other) { 40 wall += other.wall; 41 user += other.user; 42 return *this; 43 } 44 45 /// Print the current time record to 'os', with a breakdown showing 46 /// contributions to the give 'total' time record. 47 void print(raw_ostream &os, const TimeRecord &total) { 48 if (total.user != total.wall) 49 os << llvm::format(" %7.4f (%5.1f%%) ", user, 50 100.0 * user / total.user); 51 os << llvm::format(" %7.4f (%5.1f%%) ", wall, 100.0 * wall / total.wall); 52 } 53 54 double wall, user; 55 }; 56 57 struct Timer { 58 explicit Timer(std::string &&name) : name(std::move(name)) {} 59 60 /// Start the timer. 61 void start() { startTime = std::chrono::system_clock::now(); } 62 63 /// Stop the timer. 64 void stop() { 65 auto newTime = std::chrono::system_clock::now() - startTime; 66 wallTime += newTime; 67 userTime += newTime; 68 } 69 70 /// Get or create a child timer with the provided name and id. 71 Timer *getChildTimer(const void *id, 72 std::function<std::string()> &&nameBuilder) { 73 auto &child = children[id]; 74 if (!child) 75 child.reset(new Timer(nameBuilder())); 76 return child.get(); 77 } 78 79 /// Returns the total time for this timer in seconds. 80 TimeRecord getTotalTime() { 81 // If we have a valid wall time, then we directly compute the seconds. 82 if (wallTime.count()) { 83 return TimeRecord( 84 std::chrono::duration_cast<std::chrono::duration<double>>(wallTime) 85 .count(), 86 std::chrono::duration_cast<std::chrono::duration<double>>(userTime) 87 .count()); 88 } 89 90 // Otheriwse, accumulate the timing from each of the children. 91 TimeRecord totalTime; 92 for (auto &child : children) 93 totalTime += child.second->getTotalTime(); 94 return totalTime; 95 } 96 97 /// A map of unique identifiers to child timers. 98 using ChildrenMap = llvm::MapVector<const void *, std::unique_ptr<Timer>>; 99 100 /// Merge the timing data from 'other' into this timer. 101 void merge(Timer &&other) { 102 if (wallTime < other.wallTime) 103 wallTime = other.wallTime; 104 userTime += other.userTime; 105 mergeChildren(std::move(other.children), /*isStructural=*/false); 106 } 107 108 /// Merge the timer chilren in 'otherChildren' with the children of this 109 /// timer. If 'isStructural' is true, the children are merged lexographically 110 /// and 'otherChildren' must have the same number of elements as the children 111 /// of this timer. Otherwise, the timer children are merged based upon the 112 /// given timer key. 113 void mergeChildren(ChildrenMap &&otherChildren, bool isStructural) { 114 // Check for an empty children list. 115 if (children.empty()) { 116 children = std::move(otherChildren); 117 return; 118 } 119 120 if (isStructural) { 121 // If this is a structural merge, the number of children must be the same. 122 assert(children.size() == otherChildren.size() && 123 "structural merge requires the same number of children"); 124 auto it = children.begin(), otherIt = otherChildren.begin(); 125 for (auto e = children.end(); it != e; ++it, ++otherIt) 126 it->second->merge(std::move(*otherIt->second)); 127 return; 128 } 129 130 // Otherwise, we merge based upon the child timers key. 131 for (auto &otherChild : otherChildren) { 132 auto &child = children[otherChild.first]; 133 if (!child) 134 child = std::move(otherChild.second); 135 else 136 child->merge(std::move(*otherChild.second)); 137 } 138 } 139 140 /// Raw timing information. 141 std::chrono::time_point<std::chrono::system_clock> startTime; 142 std::chrono::nanoseconds wallTime = std::chrono::nanoseconds(0); 143 std::chrono::nanoseconds userTime = std::chrono::nanoseconds(0); 144 145 /// A map of unique identifiers to child timers. 146 ChildrenMap children; 147 148 /// A descriptive name for this timer. 149 std::string name; 150 }; 151 152 struct PassTiming : public PassInstrumentation { 153 PassTiming(PassTimingDisplayMode displayMode) : displayMode(displayMode) {} 154 ~PassTiming() { print(); } 155 156 /// Setup the instrumentation hooks. 157 void runBeforePass(Pass *pass, Operation *) override { startPassTimer(pass); } 158 void runAfterPass(Pass *pass, Operation *) override; 159 void runAfterPassFailed(Pass *pass, Operation *op) override { 160 runAfterPass(pass, op); 161 } 162 void runBeforeAnalysis(llvm::StringRef name, AnalysisID *id, 163 Operation *) override { 164 startAnalysisTimer(name, id); 165 } 166 void runAfterAnalysis(llvm::StringRef, AnalysisID *, Operation *) override; 167 168 /// Print and clear the timing results. 169 void print(); 170 171 /// Start a new timer for the given pass. 172 void startPassTimer(Pass *pass); 173 174 /// Start a new timer for the given analysis. 175 void startAnalysisTimer(llvm::StringRef name, AnalysisID *id); 176 177 /// Stop a pass timer. 178 void stopPassTimer(Pass *pass); 179 180 /// Stop the last active timer. 181 void stopTimer(); 182 183 /// Print the timing result in list mode. 184 void printResultsAsList(raw_ostream &os, Timer *root, TimeRecord totalTime); 185 186 /// Print the timing result in pipeline mode. 187 void printResultsAsPipeline(raw_ostream &os, Timer *root, 188 TimeRecord totalTime); 189 190 /// Returns a timer for the provided identifier and name. 191 Timer *getTimer(const void *id, std::function<std::string()> &&nameBuilder) { 192 auto tid = llvm::get_threadid(); 193 194 // If there is no active timer then add to the root timer. 195 auto &activeTimers = activeThreadTimers[tid]; 196 if (activeTimers.empty()) { 197 auto &rootTimer = rootTimers[tid]; 198 if (!rootTimer) 199 rootTimer.reset(new Timer("root")); 200 auto *timer = rootTimer->getChildTimer(id, std::move(nameBuilder)); 201 activeTimers.push_back(timer); 202 return timer; 203 } 204 205 // Otherwise, add this to the active timer. 206 auto timer = activeTimers.back()->getChildTimer(id, std::move(nameBuilder)); 207 activeTimers.push_back(timer); 208 return timer; 209 } 210 211 /// The root top level timers for each thread. 212 DenseMap<uint64_t, std::unique_ptr<Timer>> rootTimers; 213 214 /// A stack of the currently active pass timers per thread. 215 DenseMap<uint64_t, SmallVector<Timer *, 4>> activeThreadTimers; 216 217 /// The display mode to use when printing the timing results. 218 PassTimingDisplayMode displayMode; 219 }; 220 } // end anonymous namespace 221 222 /// Start a new timer for the given pass. 223 void PassTiming::startPassTimer(Pass *pass) { 224 Timer *timer = getTimer(pass, [pass] { 225 if (isModuleToFunctionAdaptorPass(pass)) 226 return StringRef("Function Pipeline"); 227 return pass->getName(); 228 }); 229 230 // We don't actually want to time the adaptor passes, they gather their total 231 // from their held passes. 232 if (!isAdaptorPass(pass)) 233 timer->start(); 234 } 235 236 /// Start a new timer for the given analysis. 237 void PassTiming::startAnalysisTimer(llvm::StringRef name, AnalysisID *id) { 238 Timer *timer = getTimer(id, [name] { return "(A) " + name.str(); }); 239 timer->start(); 240 } 241 242 /// Stop a pass timer. 243 void PassTiming::runAfterPass(Pass *pass, Operation *) { 244 auto tid = llvm::get_threadid(); 245 auto &activeTimers = activeThreadTimers[tid]; 246 assert(!activeTimers.empty() && "expected active timer"); 247 Timer *timer = activeTimers.pop_back_val(); 248 249 // If this is an ModuleToFunctionPassAdaptorParallel, then we need to merge in 250 // the timing data for the other threads. 251 if (isa<ModuleToFunctionPassAdaptorParallel>(pass)) { 252 // The asychronous pipeline timers should exist as children of root timers 253 // for other threads. 254 for (auto &rootTimer : llvm::make_early_inc_range(rootTimers)) { 255 // Skip the current thread. 256 if (rootTimer.first == tid) 257 continue; 258 // Check that this thread has no active timers. 259 assert(activeThreadTimers[tid].empty() && "expected no active timers"); 260 261 // Structurally merge this timers children into the parallel 262 // module-to-function pass timer. 263 timer->mergeChildren(std::move(rootTimer.second->children), 264 /*isStructural=*/true); 265 rootTimers.erase(rootTimer.first); 266 } 267 return; 268 } 269 270 // Adapator passes aren't timed directly, so we don't need to stop their 271 // timers. 272 if (!isAdaptorPass(pass)) 273 timer->stop(); 274 } 275 276 /// Stop a timer. 277 void PassTiming::runAfterAnalysis(llvm::StringRef, AnalysisID *, Operation *) { 278 auto &activeTimers = activeThreadTimers[llvm::get_threadid()]; 279 assert(!activeTimers.empty() && "expected active timer"); 280 Timer *timer = activeTimers.pop_back_val(); 281 timer->stop(); 282 } 283 284 /// Utility to print the timer heading information. 285 static void printTimerHeader(llvm::raw_ostream &os, TimeRecord total) { 286 os << "===" << std::string(73, '-') << "===\n"; 287 // Figure out how many spaces to description name. 288 unsigned Padding = (80 - kPassTimingDescription.size()) / 2; 289 os.indent(Padding) << kPassTimingDescription << '\n'; 290 os << "===" << std::string(73, '-') << "===\n"; 291 292 // Print the total time followed by the section headers. 293 os << llvm::format(" Total Execution Time: %5.4f seconds\n\n", total.wall); 294 if (total.user != total.wall) 295 os << " ---User Time---"; 296 os << " ---Wall Time--- --- Name ---\n"; 297 } 298 299 /// Utility to print a single line entry in the timer output. 300 static void printTimeEntry(raw_ostream &os, unsigned indent, StringRef name, 301 TimeRecord time, TimeRecord totalTime) { 302 time.print(os, totalTime); 303 os.indent(indent) << name << "\n"; 304 } 305 306 /// Print out the current timing information. 307 void PassTiming::print() { 308 // Don't print anything if there is no timing data. 309 if (rootTimers.empty()) 310 return; 311 312 assert(rootTimers.size() == 1 && "expected one remaining root timer"); 313 auto &rootTimer = rootTimers.begin()->second; 314 auto os = llvm::CreateInfoOutputFile(); 315 316 // Print the timer header. 317 TimeRecord totalTime = rootTimer->getTotalTime(); 318 printTimerHeader(*os, totalTime); 319 320 // Defer to a specialized printer for each display mode. 321 switch (displayMode) { 322 case PassTimingDisplayMode::List: 323 printResultsAsList(*os, rootTimer.get(), totalTime); 324 break; 325 case PassTimingDisplayMode::Pipeline: 326 printResultsAsPipeline(*os, rootTimer.get(), totalTime); 327 break; 328 } 329 printTimeEntry(*os, 0, "Total", totalTime, totalTime); 330 os->flush(); 331 332 // Reset root timers. 333 rootTimers.clear(); 334 activeThreadTimers.clear(); 335 } 336 337 /// Print the timing result in list mode. 338 void PassTiming::printResultsAsList(raw_ostream &os, Timer *root, 339 TimeRecord totalTime) { 340 llvm::StringMap<TimeRecord> mergedTimings; 341 342 std::function<void(Timer *)> addTimer = [&](Timer *timer) { 343 // Check for timing information. 344 if (timer->wallTime.count()) 345 mergedTimings[timer->name] += timer->getTotalTime(); 346 for (auto &children : timer->children) 347 addTimer(children.second.get()); 348 }; 349 350 // Add each of the top level timers. 351 for (auto &topLevelTimer : root->children) 352 addTimer(topLevelTimer.second.get()); 353 354 // Sort the timing information by wall time. 355 std::vector<std::pair<StringRef, TimeRecord>> timerNameAndTime; 356 for (auto &it : mergedTimings) 357 timerNameAndTime.emplace_back(it.first(), it.second); 358 llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(), 359 [](const std::pair<StringRef, TimeRecord> *lhs, 360 const std::pair<StringRef, TimeRecord> *rhs) { 361 return llvm::array_pod_sort_comparator<double>( 362 &rhs->second.wall, &lhs->second.wall); 363 }); 364 365 // Print the timing information sequentially. 366 for (auto &timeData : timerNameAndTime) 367 printTimeEntry(os, 0, timeData.first, timeData.second, totalTime); 368 } 369 370 /// Print the timing result in pipeline mode. 371 void PassTiming::printResultsAsPipeline(raw_ostream &os, Timer *root, 372 TimeRecord totalTime) { 373 std::function<void(unsigned, Timer *)> printTimer = [&](unsigned indent, 374 Timer *timer) { 375 printTimeEntry(os, indent, timer->name, timer->getTotalTime(), totalTime); 376 for (auto &children : timer->children) 377 printTimer(indent + 2, children.second.get()); 378 }; 379 380 // Print each of the top level timers. 381 for (auto &topLevelTimer : root->children) 382 printTimer(0, topLevelTimer.second.get()); 383 } 384 385 //===----------------------------------------------------------------------===// 386 // PassManager 387 //===----------------------------------------------------------------------===// 388 389 /// Add an instrumentation to time the execution of passes and the computation 390 /// of analyses. 391 void PassManager::enableTiming(PassTimingDisplayMode displayMode) { 392 // Check if pass timing is already enabled. 393 if (passTiming) 394 return; 395 addInstrumentation(new PassTiming(displayMode)); 396 passTiming = true; 397 }