MLIR 24.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 /// Whether timer operations may run concurrently on different threads. Each
119 /// timer must still be started and stopped on one thread, and parent timers
120 /// must outlive their children. Configuration and reporting are not covered
121 /// by this guarantee.
122 virtual bool isThreadSafe() const { return false; }
123
124 /// Get the root timer of this timing manager. The returned timer must be
125 /// started and stopped manually. Execution time can be measured by nesting
126 /// timers within this root timer and starting/stopping them as appropriate.
127 /// Use this function only if you need access to the timer itself. Otherwise
128 /// consider the more convenient `getRootScope()` which offers an RAII-style
129 /// wrapper around the timer.
131
132 /// Get the root timer of this timing manager wrapped in a `TimingScope` for
133 /// convenience. Automatically starts the timer and stops it as soon as the
134 /// `TimingScope` is destroyed, e.g. when it goes out of scope.
136
137protected:
138 // Allow `Timer` access to the protected callbacks.
139 friend class Timer;
140
141 //===--------------------------------------------------------------------===//
142 // Callbacks
143 //
144 // See the corresponding functions in `Timer` for additional details.
145
146 /// Return the root timer. Implementations should return `std::nullopt` if the
147 /// collection of timing samples is disabled. This will cause the timers
148 /// constructed from the manager to be tombstones which can be skipped
149 /// quickly.
150 virtual std::optional<void *> rootTimer() = 0;
151
152 /// Start the timer with the given handle.
153 virtual void startTimer(void *handle) = 0;
154
155 /// Stop the timer with the given handle.
156 virtual void stopTimer(void *handle) = 0;
157
158 /// Create a child timer nested within the one with the given handle. The `id`
159 /// parameter is used to uniquely identify the timer within its parent.
160 /// Multiple calls to this function with the same `handle` and `id` should
161 /// return the same timer, or at least cause the samples of the returned
162 /// timers to be combined for the final timing results.
163 virtual void *nestTimer(void *handle, const void *id,
164 function_ref<std::string()> nameBuilder) = 0;
165
166 /// Hide the timer in timing reports and directly show its children. This is
167 /// merely a hint that implementations are free to ignore.
168 virtual void hideTimer(void *handle) {}
169
170protected:
171 const std::unique_ptr<detail::TimingManagerImpl> impl;
172
173 // Allow `TimingIdentifier::get` access to the private impl details.
174 friend class TimingIdentifier;
175
176private:
177 // Disallow copying the manager.
178 TimingManager(const TimingManager &) = delete;
179 void operator=(const TimingManager &) = delete;
180};
181
182//===----------------------------------------------------------------------===//
183// Timer
184//===----------------------------------------------------------------------===//
185
186/// A handle for a timer in a `TimingManager`.
187///
188/// This class encapsulates a pointer to a `TimingManager` and an opaque handle
189/// to a timer running within that manager. Libraries and infrastructure code
190/// operate on `Timer` rather than any concrete classes handed out by custom
191/// manager implementations.
192class Timer {
193public:
194 Timer() = default;
195 Timer(const Timer &other) = default;
196 Timer(Timer &&other) : Timer(other) {
197 other.tm = nullptr;
198 other.handle = nullptr;
199 }
200
202 tm = other.tm;
203 handle = other.handle;
204 other.tm = nullptr;
205 other.handle = nullptr;
206 return *this;
207 }
208
209 /// Returns whether this is a valid timer handle. Invalid timer handles are
210 /// used when timing is disabled in the `TimingManager` to keep the impact on
211 /// performance low.
212 explicit operator bool() const { return tm != nullptr; }
213
214 /// Whether the timing manager supports concurrent timer operations.
215 bool isThreadSafe() const { return !tm || tm->isThreadSafe(); }
216
217 /// Start the timer. This must be accompanied by a corresponding call to
218 /// `stop()` at a later point.
219 void start() {
220 if (tm)
221 tm->startTimer(handle);
222 }
223
224 /// Stop the timer. This must have been preceded by a corresponding call to
225 /// `start()` at an earlier point.
226 void stop() {
227 if (tm)
228 tm->stopTimer(handle);
229 }
230
231 /// Create a child timer nested within this one. Multiple calls to this
232 /// function with the same unique identifier `id` will return the same child
233 /// timer. The timer must have been started when calling this function.
234 ///
235 /// This function can be called from other threads, as long as this timer
236 /// is not stopped before any uses of the child timer on the other thread are
237 /// stopped.
238 ///
239 /// The `nameBuilder` function is not guaranteed to be called.
240 Timer nest(const void *id, function_ref<std::string()> nameBuilder) {
241 return tm ? Timer(*tm, tm->nestTimer(handle, id, nameBuilder)) : Timer();
242 }
243
244 /// See above.
246 return tm ? nest(name.getAsOpaquePointer(), [=]() { return name.str(); })
247 : Timer();
248 }
249
250 /// See above.
251 Timer nest(StringRef name) {
252 return tm ? nest(TimingIdentifier::get(name, *tm)) : Timer();
253 }
254
255 /// Hide the timer in timing reports and directly show its children.
256 void hide() {
257 if (tm)
258 tm->hideTimer(handle);
259 }
260
261protected:
262 Timer(TimingManager &tm, void *handle) : tm(&tm), handle(handle) {}
263
264 // Allow the `TimingManager` access to the above constructor.
265 friend class TimingManager;
266
267private:
268 /// The associated timing manager.
269 TimingManager *tm = nullptr;
270 /// An opaque handle that identifies the timer in the timing manager
271 /// implementation.
272 void *handle = nullptr;
273};
274
275//===----------------------------------------------------------------------===//
276// TimingScope
277//===----------------------------------------------------------------------===//
278
279/// An RAII-style wrapper around a timer that ensures the timer is properly
280/// started and stopped.
282public:
284 TimingScope(const Timer &other) : timer(other) {
285 if (timer)
286 timer.start();
287 }
288 TimingScope(Timer &&other) : timer(std::move(other)) {
289 if (timer)
290 timer.start();
291 }
292 TimingScope(TimingScope &&other) : timer(std::move(other.timer)) {}
294
296 stop();
297 timer = std::move(other.timer);
298 return *this;
299 }
300
301 /// Check if the timing scope actually contains a valid timer.
302 explicit operator bool() const { return bool(timer); }
303
304 /// Whether the timing manager supports concurrent timer operations.
305 bool isThreadSafe() const { return timer.isThreadSafe(); }
306
307 // Disable copying of the `TimingScope`.
308 TimingScope(const TimingScope &) = delete;
310
311 /// Manually stop the timer early.
312 void stop() {
313 timer.stop();
314 timer = Timer();
315 }
316
317 /// Create a nested timing scope.
318 ///
319 /// This returns a new `TimingScope` with a timer nested within the current
320 /// scope. In this fashion, the time in this scope may be further subdivided
321 /// in a more fine-grained fashion.
322 template <typename... Args>
323 TimingScope nest(Args... args) {
324 return TimingScope(std::move(timer.nest(std::forward<Args>(args)...)));
325 }
326
327 /// Hide the timer in timing reports and directly show its children.
328 void hide() { timer.hide(); }
329
330private:
331 /// The wrapped timer.
332 Timer timer;
333};
334
335//===----------------------------------------------------------------------===//
336// OutputStrategy
337//===----------------------------------------------------------------------===//
338
339/// Simple record class to record timing information.
341 TimeRecord(double wall = 0.0, double user = 0.0) : wall(wall), user(user) {}
342
344 wall += other.wall;
345 user += other.user;
346 return *this;
347 }
348
350 wall -= other.wall;
351 user -= other.user;
352 return *this;
353 }
354
355 double wall, user;
356};
357
358/// Facilities for printing timing reports to various output formats.
359///
360/// This is an abstract class that serves as the foundation for printing.
361/// Users can implement additional output formats by extending this abstract
362/// class.
364public:
366 virtual ~OutputStrategy() = default;
367
368 virtual void printHeader(const TimeRecord &total) = 0;
369 virtual void printFooter() = 0;
370 virtual void printTime(const TimeRecord &time, const TimeRecord &total) = 0;
371 virtual void printListEntry(StringRef name, const TimeRecord &time,
372 const TimeRecord &total,
373 bool lastEntry = false) = 0;
374 virtual void printTreeEntry(unsigned indent, StringRef name,
375 const TimeRecord &time,
376 const TimeRecord &total) = 0;
377 virtual void printTreeEntryEnd(unsigned indent, bool lastEntry = false) = 0;
378
380};
381
382//===----------------------------------------------------------------------===//
383// DefaultTimingManager
384//===----------------------------------------------------------------------===//
385
386/// Facilities for time measurement and report printing to an output stream.
387///
388/// This is MLIR's default implementation of a `TimingManager`. Prints an
389/// execution time report upon destruction, or manually through `print()`. By
390/// default the results are printed in `DisplayMode::Tree` mode to stderr.
391/// Use `setEnabled(true)` to enable collection of timing samples; it is
392/// disabled by default.
393///
394/// You should only instantiate a `DefaultTimingManager` if you are writing a
395/// tool and want to pass a timing manager to the remaining infrastructure. If
396/// you are writing library or infrastructure code, you should rather accept
397/// the `TimingManager` base class to allow for users of your code to substitute
398/// their own timing implementations. Also, if you only intend to collect time
399/// samples, consider accepting a `Timer` or `TimingScope` instead.
401public:
402 /// The different display modes for printing the timers.
403 enum class DisplayMode {
404 /// In this mode the results are displayed in a list sorted by total time,
405 /// with timers aggregated into one unique result per timer name.
407
408 /// In this mode the results are displayed in a tree view, with child timers
409 /// nested under their parents.
411 };
412
413 /// The different output formats for printing the timers.
414 enum class OutputFormat {
415 /// In this format the results are displayed in text format.
417
418 /// In this format the results are displayed in JSON format.
420 };
421
424 ~DefaultTimingManager() override;
425
426 bool isThreadSafe() const override { return true; }
427
428 // Disable copying of the `DefaultTimingManager`.
431
432 /// Enable or disable execution time sampling.
433 void setEnabled(bool enabled);
434
435 /// Return whether execution time sampling is enabled.
436 bool isEnabled() const;
437
438 /// Change the display mode.
439 void setDisplayMode(DisplayMode displayMode);
440
441 /// Return the current display mode;
443
444 /// Change the stream where the output will be printed to.
445 void setOutput(std::unique_ptr<OutputStrategy> output);
446
447 /// Print and clear the timing results. Only call this when there are no more
448 /// references to nested timers around, as printing post-processes and clears
449 /// the timers.
450 void print();
451
452 /// Clear the timing results. Only call this when there are no more references
453 /// to nested timers around, as clearing invalidates them.
454 void clear();
455
456 /// Debug print the timer data structures to an output stream.
457 void dumpTimers(raw_ostream &os = llvm::errs());
458
459 /// Debug print the timers as a list. Only call this when there are no more
460 /// references to nested timers around.
461 void dumpAsList(raw_ostream &os = llvm::errs());
462
463 /// Debug print the timers as a tree. Only call this when there are no
464 /// more references to nested timers around.
465 void dumpAsTree(raw_ostream &os = llvm::errs());
466
467protected:
468 // `TimingManager` callbacks
469 std::optional<void *> rootTimer() override;
470 void startTimer(void *handle) override;
471 void stopTimer(void *handle) override;
472 void *nestTimer(void *handle, const void *id,
473 function_ref<std::string()> nameBuilder) override;
474 void hideTimer(void *handle) override;
475
476private:
477 const std::unique_ptr<detail::DefaultTimingManagerImpl> impl;
478 std::unique_ptr<OutputStrategy> out;
479};
480
481/// Register a set of useful command-line options that can be used to configure
482/// a `DefaultTimingManager`. The values of these options can be applied via the
483/// `applyDefaultTimingManagerCLOptions` method.
485
486/// Apply any values that were registered with
487/// 'registerDefaultTimingManagerOptions' to a `DefaultTimingManager`.
489
490/// Create an output strategy for the specified format, to be passed to
491/// DefaultTimingManager::setOutput().
492std::unique_ptr<OutputStrategy>
494
495} // namespace mlir
496
497#endif // MLIR_SUPPORT_TIMING_H
Facilities for time measurement and report printing to an output stream.
Definition Timing.h:400
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:403
@ Tree
In this mode the results are displayed in a tree view, with child timers nested under their parents.
Definition Timing.h:410
@ List
In this mode the results are displayed in a list sorted by total time, with timers aggregated into on...
Definition Timing.h:406
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
bool isThreadSafe() const override
Whether timer operations may run concurrently on different threads.
Definition Timing.h:426
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:414
@ Text
In this format the results are displayed in text format.
Definition Timing.h:416
@ Json
In this format the results are displayed in JSON format.
Definition Timing.h:419
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:379
virtual void printFooter()=0
OutputStrategy(raw_ostream &os)
Definition Timing.h:365
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:192
Timer(TimingManager &tm, void *handle)
Definition Timing.h:262
Timer nest(TimingIdentifier name)
See above.
Definition Timing.h:245
Timer(Timer &&other)
Definition Timing.h:196
void hide()
Hide the timer in timing reports and directly show its children.
Definition Timing.h:256
Timer()=default
void stop()
Stop the timer.
Definition Timing.h:226
friend class TimingManager
Definition Timing.h:265
Timer nest(StringRef name)
See above.
Definition Timing.h:251
void start()
Start the timer.
Definition Timing.h:219
bool isThreadSafe() const
Whether the timing manager supports concurrent timer operations.
Definition Timing.h:215
Timer nest(const void *id, function_ref< std::string()> nameBuilder)
Create a child timer nested within this one.
Definition Timing.h:240
Timer & operator=(Timer &&other)
Definition Timing.h:201
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:168
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 bool isThreadSafe() const
Whether timer operations may run concurrently on different threads.
Definition Timing.h:122
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:174
friend class Timer
Definition Timing.h:139
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:281
TimingScope & operator=(const TimingScope &)=delete
TimingScope nest(Args... args)
Create a nested timing scope.
Definition Timing.h:323
bool isThreadSafe() const
Whether the timing manager supports concurrent timer operations.
Definition Timing.h:305
TimingScope(const TimingScope &)=delete
void hide()
Hide the timer in timing reports and directly show its children.
Definition Timing.h:328
void stop()
Manually stop the timer early.
Definition Timing.h:312
TimingScope(TimingScope &&other)
Definition Timing.h:292
TimingScope(Timer &&other)
Definition Timing.h:288
TimingScope & operator=(TimingScope &&other)
Definition Timing.h:295
TimingScope(const Timer &other)
Definition Timing.h:284
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:155
Simple record class to record timing information.
Definition Timing.h:340
TimeRecord & operator-=(const TimeRecord &other)
Definition Timing.h:349
TimeRecord & operator+=(const TimeRecord &other)
Definition Timing.h:343
TimeRecord(double wall=0.0, double user=0.0)
Definition Timing.h:341