1 //===- Timing.h - Execution time measurement facilities ---------*- C++ -*-===// 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 // Facilities to measure and provide statistics on execution time. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef MLIR_SUPPORT_TIMING_H 14 #define MLIR_SUPPORT_TIMING_H 15 16 #include "mlir/Support/LLVM.h" 17 #include "llvm/ADT/STLExtras.h" 18 #include "llvm/ADT/StringMapEntry.h" 19 #include "llvm/Support/raw_ostream.h" 20 21 namespace mlir { 22 23 class Timer; 24 class TimingManager; 25 class TimingScope; 26 class DefaultTimingManager; 27 namespace detail { 28 class TimingManagerImpl; 29 class DefaultTimingManagerImpl; 30 } // namespace detail 31 32 //===----------------------------------------------------------------------===// 33 // TimingIdentifier 34 //===----------------------------------------------------------------------===// 35 36 /// This class represesents a uniqued string owned by a `TimingManager`. Most 37 /// importantly, instances of this class provide a stable opaque pointer that 38 /// is guaranteed to be reproduced by later interning of the same string. The 39 /// `TimingManager` uses this mechanism to provide timers with an opaque id 40 /// even when the user of the API merely provided a string as identification 41 /// (instead of a pass for example). 42 /// 43 /// This is a POD type with pointer size, so it should be passed around by 44 /// value. The underlying data is owned by the `TimingManager`. 45 class TimingIdentifier { 46 using EntryType = llvm::StringMapEntry<llvm::NoneType>; 47 48 public: 49 TimingIdentifier(const TimingIdentifier &) = default; 50 TimingIdentifier &operator=(const TimingIdentifier &other) = default; 51 52 /// Return an identifier for the specified string. 53 static TimingIdentifier get(StringRef str, TimingManager &tm); 54 55 /// Return a `StringRef` for the string. strref()56 StringRef strref() const { return entry->first(); } 57 58 /// Return an `std::string`. str()59 std::string str() const { return strref().str(); } 60 61 /// Return the opaque pointer that corresponds to this identifier. getAsOpaquePointer()62 const void *getAsOpaquePointer() const { 63 return static_cast<const void *>(entry); 64 } 65 66 private: 67 const EntryType *entry; TimingIdentifier(const EntryType * entry)68 explicit TimingIdentifier(const EntryType *entry) : entry(entry) {} 69 }; 70 71 //===----------------------------------------------------------------------===// 72 // TimingManager 73 //===----------------------------------------------------------------------===// 74 75 /// This class represents facilities to measure execution time. 76 /// 77 /// Libraries and infrastructure code operate on opque `Timer` handles returned 78 /// by various functions of this manager. Timers are started and stopped to 79 /// demarcate regions in the code where execution time is of interest, and they 80 /// can be nested to provide more detailed timing resolution. Calls to the timer 81 /// start, stop, and nesting functions must be balanced. To facilitate this, 82 /// users are encouraged to leverage the `TimingScope` RAII-style wrapper around 83 /// `Timer`s. 84 /// 85 /// Users can provide their own implementation of `TimingManager`, or use the 86 /// default `DefaultTimingManager` implementation in MLIR. Implementations 87 /// override the various protected virtual functions to create, nest, start, and 88 /// stop timers. A common pattern is for subclasses to provide a custom timer 89 /// class and simply pass pointers to instances of this class around as the 90 /// opaque timer handle. The manager itself can then forward callbacks to the 91 /// this class. Alternatively, external timing libraries may return their own 92 /// opaque handles for timing scopes. 93 /// 94 /// For example: 95 /// ``` 96 /// void doWork(TimingManager &tm) { 97 /// auto root = tm.getRootScope(); 98 /// 99 /// { 100 /// auto scope = root.nest("First"); 101 /// doSomeWork(); 102 /// // <-- "First" timer stops here 103 /// } 104 /// 105 /// auto scope = root.nest("Second"); 106 /// doEvenMoreWork(); 107 /// scope.stop(); // <-- "Second" timer stops here 108 /// 109 /// // <-- Root timer stops here 110 /// } 111 /// ``` 112 class TimingManager { 113 public: 114 explicit TimingManager(); 115 virtual ~TimingManager(); 116 117 /// Get the root timer of this timing manager. The returned timer must be 118 /// started and stopped manually. Execution time can be measured by nesting 119 /// timers within this root timer and starting/stopping them as appropriate. 120 /// Use this function only if you need access to the timer itself. Otherwise 121 /// consider the more convenient `getRootScope()` which offers an RAII-style 122 /// wrapper around the timer. 123 Timer getRootTimer(); 124 125 /// Get the root timer of this timing manager wrapped in a `TimingScope` for 126 /// convenience. Automatically starts the timer and stops it as soon as the 127 /// `TimingScope` is destroyed, e.g. when it goes out of scope. 128 TimingScope getRootScope(); 129 130 protected: 131 // Allow `Timer` access to the protected callbacks. 132 friend class Timer; 133 134 //===--------------------------------------------------------------------===// 135 // Callbacks 136 // 137 // See the corresponding functions in `Timer` for additional details. 138 139 /// Return the root timer. Implementations should return `llvm::None` if the 140 /// collection of timing samples is disabled. This will cause the timers 141 /// constructed from the manager to be tombstones which can be skipped 142 /// quickly. 143 virtual Optional<void *> rootTimer() = 0; 144 145 /// Start the timer with the given handle. 146 virtual void startTimer(void *handle) = 0; 147 148 /// Stop the timer with the given handle. 149 virtual void stopTimer(void *handle) = 0; 150 151 /// Create a child timer nested within the one with the given handle. The `id` 152 /// parameter is used to uniquely identify the timer within its parent. 153 /// Multiple calls to this function with the same `handle` and `id` should 154 /// return the same timer, or at least cause the samples of the returned 155 /// timers to be combined for the final timing results. 156 virtual void *nestTimer(void *handle, const void *id, 157 function_ref<std::string()> nameBuilder) = 0; 158 159 /// Hide the timer in timing reports and directly show its children. This is 160 /// merely a hint that implementations are free to ignore. hideTimer(void * handle)161 virtual void hideTimer(void *handle) {} 162 163 protected: 164 const std::unique_ptr<detail::TimingManagerImpl> impl; 165 166 // Allow `TimingIdentifier::get` access to the private impl details. 167 friend class TimingIdentifier; 168 169 private: 170 // Disallow copying the manager. 171 TimingManager(const TimingManager &) = delete; 172 void operator=(const TimingManager &) = delete; 173 }; 174 175 //===----------------------------------------------------------------------===// 176 // Timer 177 //===----------------------------------------------------------------------===// 178 179 /// A handle for a timer in a `TimingManager`. 180 /// 181 /// This class encapsulates a pointer to a `TimingManager` and an opaque handle 182 /// to a timer running within that manager. Libraries and infrastructure code 183 /// operate on `Timer` rather than any concrete classes handed out by custom 184 /// manager implementations. 185 class Timer { 186 public: Timer()187 Timer() {} Timer(const Timer & other)188 Timer(const Timer &other) : tm(other.tm), handle(other.handle) {} Timer(Timer && other)189 Timer(Timer &&other) : Timer(other) { 190 other.tm = nullptr; 191 other.handle = nullptr; 192 } 193 194 Timer &operator=(Timer &&other) { 195 tm = other.tm; 196 handle = other.handle; 197 other.tm = nullptr; 198 other.handle = nullptr; 199 return *this; 200 } 201 202 /// Returns whether this is a valid timer handle. Invalid timer handles are 203 /// used when timing is disabled in the `TimingManager` to keep the impact on 204 /// performance low. 205 explicit operator bool() const { return tm != nullptr; } 206 207 /// Start the timer. This must be accompanied by a corresponding call to 208 /// `stop()` at a later point. start()209 void start() { 210 if (tm) 211 tm->startTimer(handle); 212 } 213 214 /// Stop the timer. This must have been preceded by a corresponding call to 215 /// `start()` at an earlier point. stop()216 void stop() { 217 if (tm) 218 tm->stopTimer(handle); 219 } 220 221 /// Create a child timer nested within this one. Multiple calls to this 222 /// function with the same unique identifier `id` will return the same child 223 /// timer. The timer must have been started when calling this function. 224 /// 225 /// This function can be called from other threads, as long as this timer 226 /// is not stopped before any uses of the child timer on the other thread are 227 /// stopped. 228 /// 229 /// The `nameBuilder` function is not guaranteed to be called. nest(const void * id,function_ref<std::string ()> nameBuilder)230 Timer nest(const void *id, function_ref<std::string()> nameBuilder) { 231 return tm ? Timer(*tm, tm->nestTimer(handle, id, std::move(nameBuilder))) 232 : Timer(); 233 } 234 235 /// See above. nest(TimingIdentifier name)236 Timer nest(TimingIdentifier name) { 237 return tm ? nest(name.getAsOpaquePointer(), [=]() { return name.str(); }) 238 : Timer(); 239 } 240 241 /// See above. nest(StringRef name)242 Timer nest(StringRef name) { 243 return tm ? nest(TimingIdentifier::get(name, *tm)) : Timer(); 244 } 245 246 /// Hide the timer in timing reports and directly show its children. hide()247 void hide() { 248 if (tm) 249 tm->hideTimer(handle); 250 } 251 252 protected: Timer(TimingManager & tm,void * handle)253 Timer(TimingManager &tm, void *handle) : tm(&tm), handle(handle) {} 254 255 // Allow the `TimingManager` access to the above constructor. 256 friend class TimingManager; 257 258 private: 259 /// The associated timing manager. 260 TimingManager *tm = nullptr; 261 /// An opaque handle that identifies the timer in the timing manager 262 /// implementation. 263 void *handle = nullptr; 264 }; 265 266 //===----------------------------------------------------------------------===// 267 // TimingScope 268 //===----------------------------------------------------------------------===// 269 270 /// An RAII-style wrapper around a timer that ensures the timer is properly 271 /// started and stopped. 272 class TimingScope { 273 public: TimingScope()274 TimingScope() : timer() {} TimingScope(const Timer & other)275 TimingScope(const Timer &other) : timer(other) { 276 if (timer) 277 timer.start(); 278 } TimingScope(Timer && other)279 TimingScope(Timer &&other) : timer(std::move(other)) { 280 if (timer) 281 timer.start(); 282 } TimingScope(TimingScope && other)283 TimingScope(TimingScope &&other) : timer(std::move(other.timer)) {} ~TimingScope()284 ~TimingScope() { stop(); } 285 286 TimingScope &operator=(TimingScope &&other) { 287 stop(); 288 timer = std::move(other.timer); 289 return *this; 290 } 291 292 /// Check if the timing scope actually contains a valid timer. 293 explicit operator bool() const { return bool(timer); } 294 295 // Disable copying of the `TimingScope`. 296 TimingScope(const TimingScope &) = delete; 297 TimingScope &operator=(const TimingScope &) = delete; 298 299 /// Manually stop the timer early. stop()300 void stop() { 301 timer.stop(); 302 timer = Timer(); 303 } 304 305 /// Create a nested timing scope. 306 /// 307 /// This returns a new `TimingScope` with a timer nested within the current 308 /// scope. In this fashion, the time in this scope may be further subdivided 309 /// in a more fine-grained fashion. 310 template <typename... Args> nest(Args...args)311 TimingScope nest(Args... args) { 312 return TimingScope(std::move(timer.nest(std::forward<Args>(args)...))); 313 } 314 315 /// Hide the timer in timing reports and directly show its children. hide()316 void hide() { timer.hide(); } 317 318 private: 319 /// The wrapped timer. 320 Timer timer; 321 }; 322 323 //===----------------------------------------------------------------------===// 324 // DefaultTimingManager 325 //===----------------------------------------------------------------------===// 326 327 /// Facilities for time measurement and report printing to an output stream. 328 /// 329 /// This is MLIR's default implementation of a `TimingManager`. Prints an 330 /// execution time report upon destruction, or manually through `print()`. By 331 /// default the results are printed in `DisplayMode::Tree` mode to stderr. 332 /// Use `setEnabled(true)` to enable collection of timing samples; it is 333 /// disabled by default. 334 /// 335 /// You should only instantiate a `DefaultTimingManager` if you are writing a 336 /// tool and want to pass a timing manager to the remaining infrastructure. If 337 /// you are writing library or infrastructure code, you should rather accept 338 /// the `TimingManager` base class to allow for users of your code to substitute 339 /// their own timing implementations. Also, if you only intend to collect time 340 /// samples, consider accepting a `Timer` or `TimingScope` instead. 341 class DefaultTimingManager : public TimingManager { 342 public: 343 /// The different display modes for printing the timers. 344 enum class DisplayMode { 345 /// In this mode the results are displayed in a list sorted by total time, 346 /// with timers aggregated into one unique result per timer name. 347 List, 348 349 /// In this mode the results are displayed in a tree view, with child timers 350 /// nested under their parents. 351 Tree, 352 }; 353 354 DefaultTimingManager(); 355 DefaultTimingManager(DefaultTimingManager &&rhs); 356 virtual ~DefaultTimingManager(); 357 358 // Disable copying of the `DefaultTimingManager`. 359 DefaultTimingManager(const DefaultTimingManager &rhs) = delete; 360 DefaultTimingManager &operator=(const DefaultTimingManager &rhs) = delete; 361 362 /// Enable or disable execution time sampling. 363 void setEnabled(bool enabled); 364 365 /// Return whether execution time sampling is enabled. 366 bool isEnabled() const; 367 368 /// Change the display mode. 369 void setDisplayMode(DisplayMode displayMode); 370 371 /// Return the current display mode; 372 DisplayMode getDisplayMode() const; 373 374 /// Change the stream where the output will be printed to. 375 void setOutput(raw_ostream &os); 376 377 /// Return the current output stream where the output will be printed to. 378 raw_ostream &getOutput() const; 379 380 /// Print and clear the timing results. Only call this when there are no more 381 /// references to nested timers around, as printing post-processes and clears 382 /// the timers. 383 void print(); 384 385 /// Clear the timing results. Only call this when there are no more references 386 /// to nested timers around, as clearing invalidates them. 387 void clear(); 388 389 /// Debug print the timer data structures to an output stream. 390 void dumpTimers(raw_ostream &os = llvm::errs()); 391 392 /// Debug print the timers as a list. Only call this when there are no more 393 /// references to nested timers around. 394 void dumpAsList(raw_ostream &os = llvm::errs()); 395 396 /// Debug print the timers as a tree. Only call this when there are no 397 /// more references to nested timers around. 398 void dumpAsTree(raw_ostream &os = llvm::errs()); 399 400 protected: 401 // `TimingManager` callbacks 402 Optional<void *> rootTimer() override; 403 void startTimer(void *handle) override; 404 void stopTimer(void *handle) override; 405 void *nestTimer(void *handle, const void *id, 406 function_ref<std::string()> nameBuilder) override; 407 void hideTimer(void *handle) override; 408 409 private: 410 const std::unique_ptr<detail::DefaultTimingManagerImpl> impl; 411 }; 412 413 /// Register a set of useful command-line options that can be used to configure 414 /// a `DefaultTimingManager`. The values of these options can be applied via the 415 /// `applyDefaultTimingManagerCLOptions` method. 416 void registerDefaultTimingManagerCLOptions(); 417 418 /// Apply any values that were registered with 419 /// 'registerDefaultTimingManagerOptions' to a `DefaultTimingManager`. 420 void applyDefaultTimingManagerCLOptions(DefaultTimingManager &tm); 421 422 } // namespace mlir 423 424 #endif // MLIR_SUPPORT_TIMING_H 425