MLIR  19.0.0git
Timing.h
Go to the documentation of this file.
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 #include <optional>
21 
22 namespace mlir {
23 
24 class Timer;
25 class TimingManager;
26 class TimingScope;
27 class DefaultTimingManager;
28 namespace detail {
29 class TimingManagerImpl;
30 class DefaultTimingManagerImpl;
31 } // namespace detail
32 
33 //===----------------------------------------------------------------------===//
34 // TimingIdentifier
35 //===----------------------------------------------------------------------===//
36 
37 /// This class represesents a uniqued string owned by a `TimingManager`. Most
38 /// importantly, instances of this class provide a stable opaque pointer that
39 /// is guaranteed to be reproduced by later interning of the same string. The
40 /// `TimingManager` uses this mechanism to provide timers with an opaque id
41 /// even when the user of the API merely provided a string as identification
42 /// (instead of a pass for example).
43 ///
44 /// This is a POD type with pointer size, so it should be passed around by
45 /// value. The underlying data is owned by the `TimingManager`.
47  using EntryType = llvm::StringMapEntry<std::nullopt_t>;
48 
49 public:
50  TimingIdentifier(const TimingIdentifier &) = default;
51  TimingIdentifier &operator=(const TimingIdentifier &other) = default;
52 
53  /// Return an identifier for the specified string.
54  static TimingIdentifier get(StringRef str, TimingManager &tm);
55 
56  /// Return a `StringRef` for the string.
57  StringRef strref() const { return entry->first(); }
58 
59  /// Return an `std::string`.
60  std::string str() const { return strref().str(); }
61 
62  /// Return the opaque pointer that corresponds to this identifier.
63  const void *getAsOpaquePointer() const {
64  return static_cast<const void *>(entry);
65  }
66 
67 private:
68  const EntryType *entry;
69  explicit TimingIdentifier(const EntryType *entry) : entry(entry) {}
70 };
71 
72 //===----------------------------------------------------------------------===//
73 // TimingManager
74 //===----------------------------------------------------------------------===//
75 
76 /// This class represents facilities to measure execution time.
77 ///
78 /// Libraries and infrastructure code operate on opque `Timer` handles returned
79 /// by various functions of this manager. Timers are started and stopped to
80 /// demarcate regions in the code where execution time is of interest, and they
81 /// can be nested to provide more detailed timing resolution. Calls to the timer
82 /// start, stop, and nesting functions must be balanced. To facilitate this,
83 /// users are encouraged to leverage the `TimingScope` RAII-style wrapper around
84 /// `Timer`s.
85 ///
86 /// Users can provide their own implementation of `TimingManager`, or use the
87 /// default `DefaultTimingManager` implementation in MLIR. Implementations
88 /// override the various protected virtual functions to create, nest, start, and
89 /// stop timers. A common pattern is for subclasses to provide a custom timer
90 /// class and simply pass pointers to instances of this class around as the
91 /// opaque timer handle. The manager itself can then forward callbacks to the
92 /// this class. Alternatively, external timing libraries may return their own
93 /// opaque handles for timing scopes.
94 ///
95 /// For example:
96 /// ```
97 /// void doWork(TimingManager &tm) {
98 /// auto root = tm.getRootScope();
99 ///
100 /// {
101 /// auto scope = root.nest("First");
102 /// doSomeWork();
103 /// // <-- "First" timer stops here
104 /// }
105 ///
106 /// auto scope = root.nest("Second");
107 /// doEvenMoreWork();
108 /// scope.stop(); // <-- "Second" timer stops here
109 ///
110 /// // <-- Root timer stops here
111 /// }
112 /// ```
114 public:
115  explicit TimingManager();
116  virtual ~TimingManager();
117 
118  /// Get the root timer of this timing manager. The returned timer must be
119  /// started and stopped manually. Execution time can be measured by nesting
120  /// timers within this root timer and starting/stopping them as appropriate.
121  /// Use this function only if you need access to the timer itself. Otherwise
122  /// consider the more convenient `getRootScope()` which offers an RAII-style
123  /// wrapper around the timer.
125 
126  /// Get the root timer of this timing manager wrapped in a `TimingScope` for
127  /// convenience. Automatically starts the timer and stops it as soon as the
128  /// `TimingScope` is destroyed, e.g. when it goes out of scope.
130 
131 protected:
132  // Allow `Timer` access to the protected callbacks.
133  friend class Timer;
134 
135  //===--------------------------------------------------------------------===//
136  // Callbacks
137  //
138  // See the corresponding functions in `Timer` for additional details.
139 
140  /// Return the root timer. Implementations should return `std::nullopt` if the
141  /// collection of timing samples is disabled. This will cause the timers
142  /// constructed from the manager to be tombstones which can be skipped
143  /// quickly.
144  virtual std::optional<void *> rootTimer() = 0;
145 
146  /// Start the timer with the given handle.
147  virtual void startTimer(void *handle) = 0;
148 
149  /// Stop the timer with the given handle.
150  virtual void stopTimer(void *handle) = 0;
151 
152  /// Create a child timer nested within the one with the given handle. The `id`
153  /// parameter is used to uniquely identify the timer within its parent.
154  /// Multiple calls to this function with the same `handle` and `id` should
155  /// return the same timer, or at least cause the samples of the returned
156  /// timers to be combined for the final timing results.
157  virtual void *nestTimer(void *handle, const void *id,
158  function_ref<std::string()> nameBuilder) = 0;
159 
160  /// Hide the timer in timing reports and directly show its children. This is
161  /// merely a hint that implementations are free to ignore.
162  virtual void hideTimer(void *handle) {}
163 
164 protected:
165  const std::unique_ptr<detail::TimingManagerImpl> impl;
166 
167  // Allow `TimingIdentifier::get` access to the private impl details.
168  friend class TimingIdentifier;
169 
170 private:
171  // Disallow copying the manager.
172  TimingManager(const TimingManager &) = delete;
173  void operator=(const TimingManager &) = delete;
174 };
175 
176 //===----------------------------------------------------------------------===//
177 // Timer
178 //===----------------------------------------------------------------------===//
179 
180 /// A handle for a timer in a `TimingManager`.
181 ///
182 /// This class encapsulates a pointer to a `TimingManager` and an opaque handle
183 /// to a timer running within that manager. Libraries and infrastructure code
184 /// operate on `Timer` rather than any concrete classes handed out by custom
185 /// manager implementations.
186 class Timer {
187 public:
188  Timer() = default;
189  Timer(const Timer &other) = default;
190  Timer(Timer &&other) : Timer(other) {
191  other.tm = nullptr;
192  other.handle = nullptr;
193  }
194 
195  Timer &operator=(Timer &&other) {
196  tm = other.tm;
197  handle = other.handle;
198  other.tm = nullptr;
199  other.handle = nullptr;
200  return *this;
201  }
202 
203  /// Returns whether this is a valid timer handle. Invalid timer handles are
204  /// used when timing is disabled in the `TimingManager` to keep the impact on
205  /// performance low.
206  explicit operator bool() const { return tm != nullptr; }
207 
208  /// Start the timer. This must be accompanied by a corresponding call to
209  /// `stop()` at a later point.
210  void start() {
211  if (tm)
212  tm->startTimer(handle);
213  }
214 
215  /// Stop the timer. This must have been preceded by a corresponding call to
216  /// `start()` at an earlier point.
217  void stop() {
218  if (tm)
219  tm->stopTimer(handle);
220  }
221 
222  /// Create a child timer nested within this one. Multiple calls to this
223  /// function with the same unique identifier `id` will return the same child
224  /// timer. The timer must have been started when calling this function.
225  ///
226  /// This function can be called from other threads, as long as this timer
227  /// is not stopped before any uses of the child timer on the other thread are
228  /// stopped.
229  ///
230  /// The `nameBuilder` function is not guaranteed to be called.
231  Timer nest(const void *id, function_ref<std::string()> nameBuilder) {
232  return tm ? Timer(*tm, tm->nestTimer(handle, id, nameBuilder)) : Timer();
233  }
234 
235  /// See above.
237  return tm ? nest(name.getAsOpaquePointer(), [=]() { return name.str(); })
238  : Timer();
239  }
240 
241  /// See above.
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.
247  void hide() {
248  if (tm)
249  tm->hideTimer(handle);
250  }
251 
252 protected:
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:
275  TimingScope(const Timer &other) : timer(other) {
276  if (timer)
277  timer.start();
278  }
279  TimingScope(Timer &&other) : timer(std::move(other)) {
280  if (timer)
281  timer.start();
282  }
283  TimingScope(TimingScope &&other) : timer(std::move(other.timer)) {}
285 
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.
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>
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.
316  void hide() { timer.hide(); }
317 
318 private:
319  /// The wrapped timer.
320  Timer timer;
321 };
322 
323 //===----------------------------------------------------------------------===//
324 // OutputStrategy
325 //===----------------------------------------------------------------------===//
326 
327 /// Simple record class to record timing information.
328 struct TimeRecord {
329  TimeRecord(double wall = 0.0, double user = 0.0) : wall(wall), user(user) {}
330 
332  wall += other.wall;
333  user += other.user;
334  return *this;
335  }
336 
338  wall -= other.wall;
339  user -= other.user;
340  return *this;
341  }
342 
343  double wall, user;
344 };
345 
346 /// Facilities for printing timing reports to various output formats.
347 ///
348 /// This is an abstract class that serves as the foundation for printing.
349 /// Users can implement additional output formats by extending this abstract
350 /// class.
352 public:
353  OutputStrategy(raw_ostream &os) : os(os) {}
354  virtual ~OutputStrategy() = default;
355 
356  virtual void printHeader(const TimeRecord &total) = 0;
357  virtual void printFooter() = 0;
358  virtual void printTime(const TimeRecord &time, const TimeRecord &total) = 0;
359  virtual void printListEntry(StringRef name, const TimeRecord &time,
360  const TimeRecord &total,
361  bool lastEntry = false) = 0;
362  virtual void printTreeEntry(unsigned indent, StringRef name,
363  const TimeRecord &time,
364  const TimeRecord &total) = 0;
365  virtual void printTreeEntryEnd(unsigned indent, bool lastEntry = false) = 0;
366 
367  raw_ostream &os;
368 };
369 
370 //===----------------------------------------------------------------------===//
371 // DefaultTimingManager
372 //===----------------------------------------------------------------------===//
373 
374 /// Facilities for time measurement and report printing to an output stream.
375 ///
376 /// This is MLIR's default implementation of a `TimingManager`. Prints an
377 /// execution time report upon destruction, or manually through `print()`. By
378 /// default the results are printed in `DisplayMode::Tree` mode to stderr.
379 /// Use `setEnabled(true)` to enable collection of timing samples; it is
380 /// disabled by default.
381 ///
382 /// You should only instantiate a `DefaultTimingManager` if you are writing a
383 /// tool and want to pass a timing manager to the remaining infrastructure. If
384 /// you are writing library or infrastructure code, you should rather accept
385 /// the `TimingManager` base class to allow for users of your code to substitute
386 /// their own timing implementations. Also, if you only intend to collect time
387 /// samples, consider accepting a `Timer` or `TimingScope` instead.
389 public:
390  /// The different display modes for printing the timers.
391  enum class DisplayMode {
392  /// In this mode the results are displayed in a list sorted by total time,
393  /// with timers aggregated into one unique result per timer name.
394  List,
395 
396  /// In this mode the results are displayed in a tree view, with child timers
397  /// nested under their parents.
398  Tree,
399  };
400 
401  /// The different output formats for printing the timers.
402  enum class OutputFormat {
403  /// In this format the results are displayed in text format.
404  Text,
405 
406  /// In this format the results are displayed in JSON format.
407  Json,
408  };
409 
412  ~DefaultTimingManager() override;
413 
414  // Disable copying of the `DefaultTimingManager`.
417 
418  /// Enable or disable execution time sampling.
419  void setEnabled(bool enabled);
420 
421  /// Return whether execution time sampling is enabled.
422  bool isEnabled() const;
423 
424  /// Change the display mode.
425  void setDisplayMode(DisplayMode displayMode);
426 
427  /// Return the current display mode;
428  DisplayMode getDisplayMode() const;
429 
430  /// Change the stream where the output will be printed to.
431  void setOutput(std::unique_ptr<OutputStrategy> output);
432 
433  /// Print and clear the timing results. Only call this when there are no more
434  /// references to nested timers around, as printing post-processes and clears
435  /// the timers.
436  void print();
437 
438  /// Clear the timing results. Only call this when there are no more references
439  /// to nested timers around, as clearing invalidates them.
440  void clear();
441 
442  /// Debug print the timer data structures to an output stream.
443  void dumpTimers(raw_ostream &os = llvm::errs());
444 
445  /// Debug print the timers as a list. Only call this when there are no more
446  /// references to nested timers around.
447  void dumpAsList(raw_ostream &os = llvm::errs());
448 
449  /// Debug print the timers as a tree. Only call this when there are no
450  /// more references to nested timers around.
451  void dumpAsTree(raw_ostream &os = llvm::errs());
452 
453 protected:
454  // `TimingManager` callbacks
455  std::optional<void *> rootTimer() override;
456  void startTimer(void *handle) override;
457  void stopTimer(void *handle) override;
458  void *nestTimer(void *handle, const void *id,
459  function_ref<std::string()> nameBuilder) override;
460  void hideTimer(void *handle) override;
461 
462 private:
463  const std::unique_ptr<detail::DefaultTimingManagerImpl> impl;
464  std::unique_ptr<OutputStrategy> out;
465 };
466 
467 /// Register a set of useful command-line options that can be used to configure
468 /// a `DefaultTimingManager`. The values of these options can be applied via the
469 /// `applyDefaultTimingManagerCLOptions` method.
471 
472 /// Apply any values that were registered with
473 /// 'registerDefaultTimingManagerOptions' to a `DefaultTimingManager`.
475 
476 } // namespace mlir
477 
478 #endif // MLIR_SUPPORT_TIMING_H
Facilities for time measurement and report printing to an output stream.
Definition: Timing.h:388
void setDisplayMode(DisplayMode displayMode)
Change the display mode.
Definition: Timing.cpp:516
void stopTimer(void *handle) override
Stop the timer with the given handle.
Definition: Timing.cpp:572
std::optional< void * > rootTimer() override
Return the root timer.
Definition: Timing.cpp:562
DisplayMode
The different display modes for printing the timers.
Definition: Timing.h:391
@ Tree
In this mode the results are displayed in a tree view, with child timers nested under their parents.
@ List
In this mode the results are displayed in a list sorted by total time, with timers aggregated into on...
void setOutput(std::unique_ptr< OutputStrategy > output)
Change the stream where the output will be printed to.
Definition: Timing.cpp:526
void dumpTimers(raw_ostream &os=llvm::errs())
Debug print the timer data structures to an output stream.
Definition: Timing.cpp:546
void * nestTimer(void *handle, const void *id, function_ref< std::string()> nameBuilder) override
Create a child timer nested within the one with the given handle.
Definition: Timing.cpp:576
void startTimer(void *handle) override
Start the timer with the given handle.
Definition: Timing.cpp:568
DefaultTimingManager & operator=(const DefaultTimingManager &rhs)=delete
DisplayMode getDisplayMode() const
Return the current display mode;.
Definition: Timing.cpp:521
void dumpAsList(raw_ostream &os=llvm::errs())
Debug print the timers as a list.
Definition: Timing.cpp:551
void dumpAsTree(raw_ostream &os=llvm::errs())
Debug print the timers as a tree.
Definition: Timing.cpp:557
DefaultTimingManager(DefaultTimingManager &&rhs)
void clear()
Clear the timing results.
Definition: Timing.cpp:540
~DefaultTimingManager() override
Definition: Timing.cpp:507
void print()
Print and clear the timing results.
Definition: Timing.cpp:531
void setEnabled(bool enabled)
Enable or disable execution time sampling.
Definition: Timing.cpp:510
OutputFormat
The different output formats for printing the timers.
Definition: Timing.h:402
@ Text
In this format the results are displayed in text format.
@ Json
In this format the results are displayed in JSON format.
DefaultTimingManager(const DefaultTimingManager &rhs)=delete
bool isEnabled() const
Return whether execution time sampling is enabled.
Definition: Timing.cpp:513
void hideTimer(void *handle) override
Hide the timer in timing reports and directly show its children.
Definition: Timing.cpp:581
Facilities for printing timing reports to various output formats.
Definition: Timing.h:351
virtual void printListEntry(StringRef name, const TimeRecord &time, const TimeRecord &total, bool lastEntry=false)=0
raw_ostream & os
Definition: Timing.h:367
virtual void printFooter()=0
OutputStrategy(raw_ostream &os)
Definition: Timing.h:353
virtual ~OutputStrategy()=default
virtual void printTime(const TimeRecord &time, const TimeRecord &total)=0
virtual void printTreeEntryEnd(unsigned indent, bool lastEntry=false)=0
virtual void printHeader(const TimeRecord &total)=0
virtual void printTreeEntry(unsigned indent, StringRef name, const TimeRecord &time, const TimeRecord &total)=0
A handle for a timer in a TimingManager.
Definition: Timing.h:186
Timer(TimingManager &tm, void *handle)
Definition: Timing.h:253
Timer nest(TimingIdentifier name)
See above.
Definition: Timing.h:236
Timer(Timer &&other)
Definition: Timing.h:190
void hide()
Hide the timer in timing reports and directly show its children.
Definition: Timing.h:247
Timer & operator=(Timer &&other)
Definition: Timing.h:195
Timer()=default
void stop()
Stop the timer.
Definition: Timing.h:217
Timer nest(StringRef name)
See above.
Definition: Timing.h:242
void start()
Start the timer.
Definition: Timing.h:210
Timer nest(const void *id, function_ref< std::string()> nameBuilder)
Create a child timer nested within this one.
Definition: Timing.h:231
Timer(const Timer &other)=default
This class represesents a uniqued string owned by a TimingManager.
Definition: Timing.h:46
const void * getAsOpaquePointer() const
Return the opaque pointer that corresponds to this identifier.
Definition: Timing.h:63
StringRef strref() const
Return a StringRef for the string.
Definition: Timing.h:57
std::string str() const
Return an std::string.
Definition: Timing.h:60
TimingIdentifier(const TimingIdentifier &)=default
TimingIdentifier & operator=(const TimingIdentifier &other)=default
static TimingIdentifier get(StringRef str, TimingManager &tm)
Return an identifier for the specified string.
Definition: Timing.cpp:83
This class represents facilities to measure execution time.
Definition: Timing.h:113
virtual void hideTimer(void *handle)
Hide the timer in timing reports and directly show its children.
Definition: Timing.h:162
TimingScope getRootScope()
Get the root timer of this timing manager wrapped in a TimingScope for convenience.
Definition: Timing.cpp:74
virtual void startTimer(void *handle)=0
Start the timer with the given handle.
virtual ~TimingManager()
const std::unique_ptr< detail::TimingManagerImpl > impl
Definition: Timing.h:165
virtual std::optional< void * > rootTimer()=0
Return the root timer.
Timer getRootTimer()
Get the root timer of this timing manager.
Definition: Timing.cpp:68
virtual void * nestTimer(void *handle, const void *id, function_ref< std::string()> nameBuilder)=0
Create a child timer nested within the one with the given handle.
virtual void stopTimer(void *handle)=0
Stop the timer with the given handle.
An RAII-style wrapper around a timer that ensures the timer is properly started and stopped.
Definition: Timing.h:272
TimingScope & operator=(TimingScope &&other)
Definition: Timing.h:286
TimingScope & operator=(const TimingScope &)=delete
TimingScope nest(Args... args)
Create a nested timing scope.
Definition: Timing.h:311
TimingScope(const TimingScope &)=delete
void hide()
Hide the timer in timing reports and directly show its children.
Definition: Timing.h:316
void stop()
Manually stop the timer early.
Definition: Timing.h:300
TimingScope(TimingScope &&other)
Definition: Timing.h:283
TimingScope(Timer &&other)
Definition: Timing.h:279
TimingScope(const Timer &other)
Definition: Timing.h:275
Include the generated interface declarations.
void registerDefaultTimingManagerCLOptions()
Register a set of useful command-line options that can be used to configure a DefaultTimingManager.
Definition: Timing.cpp:614
void applyDefaultTimingManagerCLOptions(DefaultTimingManager &tm)
Apply any values that were registered with 'registerDefaultTimingManagerOptions' to a DefaultTimingMa...
Definition: Timing.cpp:619
Simple record class to record timing information.
Definition: Timing.h:328
TimeRecord & operator-=(const TimeRecord &other)
Definition: Timing.h:337
TimeRecord & operator+=(const TimeRecord &other)
Definition: Timing.h:331
double user
Definition: Timing.h:343
double wall
Definition: Timing.h:343
TimeRecord(double wall=0.0, double user=0.0)
Definition: Timing.h:329