MLIR 22.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
22namespace mlir {
23
24class Timer;
25class TimingManager;
26class TimingScope;
28namespace detail {
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<llvm::EmptyStringSetTag>;
48
49public:
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
67private:
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/// ```
114public:
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
131protected:
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
164protected:
165 const std::unique_ptr<detail::TimingManagerImpl> impl;
166
167 // Allow `TimingIdentifier::get` access to the private impl details.
168 friend class TimingIdentifier;
169
170private:
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.
186class Timer {
187public:
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
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
252protected:
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
258private:
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.
273public:
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;
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
318private:
319 /// The wrapped timer.
320 Timer timer;
321};
322
323//===----------------------------------------------------------------------===//
324// OutputStrategy
325//===----------------------------------------------------------------------===//
326
327/// Simple record class to record timing information.
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.
352public:
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
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.
389public:
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.
395
396 /// In this mode the results are displayed in a tree view, with child timers
397 /// nested under their parents.
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.
405
406 /// In this format the results are displayed in JSON format.
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;
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
453protected:
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
462private:
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/// Create an output strategy for the specified format, to be passed to
477/// DefaultTimingManager::setOutput().
478std::unique_ptr<OutputStrategy>
480
481} // namespace mlir
482
483#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:514
void stopTimer(void *handle) override
Stop the timer with the given handle.
Definition Timing.cpp:570
std::optional< void * > rootTimer() override
Return the root timer.
Definition Timing.cpp:560
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.
Definition Timing.h:398
@ List
In this mode the results are displayed in a list sorted by total time, with timers aggregated into on...
Definition Timing.h:394
void setOutput(std::unique_ptr< OutputStrategy > output)
Change the stream where the output will be printed to.
Definition Timing.cpp:524
void dumpTimers(raw_ostream &os=llvm::errs())
Debug print the timer data structures to an output stream.
Definition Timing.cpp:544
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:574
void startTimer(void *handle) override
Start the timer with the given handle.
Definition Timing.cpp:566
DisplayMode getDisplayMode() const
Return the current display mode;.
Definition Timing.cpp:519
void dumpAsList(raw_ostream &os=llvm::errs())
Debug print the timers as a list.
Definition Timing.cpp:549
void dumpAsTree(raw_ostream &os=llvm::errs())
Debug print the timers as a tree.
Definition Timing.cpp:555
DefaultTimingManager(DefaultTimingManager &&rhs)
void clear()
Clear the timing results.
Definition Timing.cpp:538
~DefaultTimingManager() override
Definition Timing.cpp:505
void print()
Print and clear the timing results.
Definition Timing.cpp:529
void setEnabled(bool enabled)
Enable or disable execution time sampling.
Definition Timing.cpp:508
OutputFormat
The different output formats for printing the timers.
Definition Timing.h:402
@ Text
In this format the results are displayed in text format.
Definition Timing.h:404
@ Json
In this format the results are displayed in JSON format.
Definition Timing.h:407
DefaultTimingManager(const DefaultTimingManager &rhs)=delete
bool isEnabled() const
Return whether execution time sampling is enabled.
Definition Timing.cpp:511
DefaultTimingManager & operator=(const DefaultTimingManager &rhs)=delete
void hideTimer(void *handle) override
Hide the timer in timing reports and directly show its children.
Definition Timing.cpp:579
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()=default
void stop()
Stop the timer.
Definition Timing.h:217
friend class TimingManager
Definition Timing.h:256
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 & operator=(Timer &&other)
Definition Timing.h:195
Timer(const Timer &other)=default
This class represesents a uniqued string owned by a TimingManager.
Definition Timing.h:46
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
const void * getAsOpaquePointer() const
Return the opaque pointer that corresponds to this identifier.
Definition Timing.h:63
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:82
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
virtual std::optional< void * > rootTimer()=0
Return the root timer.
TimingScope getRootScope()
Get the root timer of this timing manager wrapped in a TimingScope for convenience.
Definition Timing.cpp:73
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.
friend class TimingIdentifier
Definition Timing.h:168
friend class Timer
Definition Timing.h:133
virtual void startTimer(void *handle)=0
Start the timer with the given handle.
virtual ~TimingManager()
Timer getRootTimer()
Get the root timer of this timing manager.
Definition Timing.cpp:67
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=(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 & operator=(TimingScope &&other)
Definition Timing.h:286
TimingScope(const Timer &other)
Definition Timing.h:275
Implementation details of the DefaultTimingManager.
Definition Timing.cpp:484
Private implementation details of the TimingManager.
Definition Timing.cpp:45
AttrTypeReplacer.
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:612
void applyDefaultTimingManagerCLOptions(DefaultTimingManager &tm)
Apply any values that were registered with 'registerDefaultTimingManagerOptions' to a DefaultTimingMa...
Definition Timing.cpp:617
std::unique_ptr< OutputStrategy > createOutputStrategy(DefaultTimingManager::OutputFormat fmt, raw_ostream &os)
Create an output strategy for the specified format, to be passed to DefaultTimingManager::setOutput()...
Definition Timing.cpp:626
llvm::function_ref< Fn > function_ref
Definition LLVM.h:152
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
TimeRecord(double wall=0.0, double user=0.0)
Definition Timing.h:329