133f908c4SFabian Schuiki //===- Timing.cpp - Execution time measurement facilities -----------------===//
233f908c4SFabian Schuiki //
333f908c4SFabian Schuiki // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
433f908c4SFabian Schuiki // See https://llvm.org/LICENSE.txt for license information.
533f908c4SFabian Schuiki // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
633f908c4SFabian Schuiki //
733f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
833f908c4SFabian Schuiki //
933f908c4SFabian Schuiki // Facilities to measure and provide statistics on execution time.
1033f908c4SFabian Schuiki //
1133f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
1233f908c4SFabian Schuiki
1333f908c4SFabian Schuiki #include "mlir/Support/Timing.h"
1433f908c4SFabian Schuiki #include "mlir/Support/ThreadLocalCache.h"
1533f908c4SFabian Schuiki #include "llvm/ADT/MapVector.h"
1633f908c4SFabian Schuiki #include "llvm/ADT/Statistic.h"
1733f908c4SFabian Schuiki #include "llvm/ADT/StringMap.h"
1833f908c4SFabian Schuiki #include "llvm/ADT/StringSet.h"
1933f908c4SFabian Schuiki #include "llvm/Support/Allocator.h"
2033f908c4SFabian Schuiki #include "llvm/Support/CommandLine.h"
2133f908c4SFabian Schuiki #include "llvm/Support/Format.h"
2233f908c4SFabian Schuiki #include "llvm/Support/FormatVariadic.h"
2333f908c4SFabian Schuiki #include "llvm/Support/ManagedStatic.h"
2433f908c4SFabian Schuiki #include "llvm/Support/RWMutex.h"
2533f908c4SFabian Schuiki #include "llvm/Support/Threading.h"
2633f908c4SFabian Schuiki #include "llvm/Support/raw_ostream.h"
2733f908c4SFabian Schuiki
2833f908c4SFabian Schuiki #include <atomic>
2933f908c4SFabian Schuiki #include <chrono>
3033f908c4SFabian Schuiki
3133f908c4SFabian Schuiki using namespace mlir;
3233f908c4SFabian Schuiki using namespace detail;
3333f908c4SFabian Schuiki using DisplayMode = DefaultTimingManager::DisplayMode;
3433f908c4SFabian Schuiki
3533f908c4SFabian Schuiki constexpr llvm::StringLiteral kTimingDescription =
3633f908c4SFabian Schuiki "... Execution time report ...";
3733f908c4SFabian Schuiki
3833f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
3933f908c4SFabian Schuiki // TimingManager
4033f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
4133f908c4SFabian Schuiki
4233f908c4SFabian Schuiki namespace mlir {
4333f908c4SFabian Schuiki namespace detail {
4433f908c4SFabian Schuiki /// Private implementation details of the `TimingManager`.
4533f908c4SFabian Schuiki class TimingManagerImpl {
4633f908c4SFabian Schuiki public:
4733f908c4SFabian Schuiki // Identifier allocator, map, and mutex for thread safety.
4833f908c4SFabian Schuiki llvm::BumpPtrAllocator identifierAllocator;
4933f908c4SFabian Schuiki llvm::StringSet<llvm::BumpPtrAllocator &> identifiers;
5033f908c4SFabian Schuiki llvm::sys::SmartRWMutex<true> identifierMutex;
5133f908c4SFabian Schuiki
5233f908c4SFabian Schuiki /// A thread local cache of identifiers to reduce lock contention.
5333f908c4SFabian Schuiki ThreadLocalCache<llvm::StringMap<llvm::StringMapEntry<llvm::NoneType> *>>
5433f908c4SFabian Schuiki localIdentifierCache;
5533f908c4SFabian Schuiki
TimingManagerImpl()5633f908c4SFabian Schuiki TimingManagerImpl() : identifiers(identifierAllocator) {}
5733f908c4SFabian Schuiki };
5833f908c4SFabian Schuiki } // namespace detail
5933f908c4SFabian Schuiki } // namespace mlir
6033f908c4SFabian Schuiki
TimingManager()6133f908c4SFabian Schuiki TimingManager::TimingManager() : impl(std::make_unique<TimingManagerImpl>()) {}
6233f908c4SFabian Schuiki
63e5639b3fSMehdi Amini TimingManager::~TimingManager() = default;
6433f908c4SFabian Schuiki
6533f908c4SFabian Schuiki /// Get the root timer of this timing manager.
getRootTimer()6633f908c4SFabian Schuiki Timer TimingManager::getRootTimer() {
6733f908c4SFabian Schuiki auto rt = rootTimer();
68*064a08cdSKazu Hirata return rt ? Timer(*this, *rt) : Timer();
6933f908c4SFabian Schuiki }
7033f908c4SFabian Schuiki
7133f908c4SFabian Schuiki /// Get the root timer of this timing manager wrapped in a `TimingScope`.
getRootScope()7233f908c4SFabian Schuiki TimingScope TimingManager::getRootScope() {
7333f908c4SFabian Schuiki return TimingScope(getRootTimer());
7433f908c4SFabian Schuiki }
7533f908c4SFabian Schuiki
7633f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
7733f908c4SFabian Schuiki // Identifier uniquing
7833f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
7933f908c4SFabian Schuiki
8033f908c4SFabian Schuiki /// Return an identifier for the specified string.
get(StringRef str,TimingManager & tm)8133f908c4SFabian Schuiki TimingIdentifier TimingIdentifier::get(StringRef str, TimingManager &tm) {
8233f908c4SFabian Schuiki // Check for an existing instance in the local cache.
8333f908c4SFabian Schuiki auto &impl = *tm.impl;
8433f908c4SFabian Schuiki auto *&localEntry = (*impl.localIdentifierCache)[str];
8533f908c4SFabian Schuiki if (localEntry)
8633f908c4SFabian Schuiki return TimingIdentifier(localEntry);
8733f908c4SFabian Schuiki
8833f908c4SFabian Schuiki // Check for an existing identifier in read-only mode.
8933f908c4SFabian Schuiki {
9033f908c4SFabian Schuiki llvm::sys::SmartScopedReader<true> contextLock(impl.identifierMutex);
9133f908c4SFabian Schuiki auto it = impl.identifiers.find(str);
9233f908c4SFabian Schuiki if (it != impl.identifiers.end()) {
9333f908c4SFabian Schuiki localEntry = &*it;
9433f908c4SFabian Schuiki return TimingIdentifier(localEntry);
9533f908c4SFabian Schuiki }
9633f908c4SFabian Schuiki }
9733f908c4SFabian Schuiki
9833f908c4SFabian Schuiki // Acquire a writer-lock so that we can safely create the new instance.
9933f908c4SFabian Schuiki llvm::sys::SmartScopedWriter<true> contextLock(impl.identifierMutex);
10033f908c4SFabian Schuiki auto it = impl.identifiers.insert(str).first;
10133f908c4SFabian Schuiki localEntry = &*it;
10233f908c4SFabian Schuiki return TimingIdentifier(localEntry);
10333f908c4SFabian Schuiki }
10433f908c4SFabian Schuiki
10533f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
10633f908c4SFabian Schuiki // Helpers for time record printing
10733f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
10833f908c4SFabian Schuiki
10933f908c4SFabian Schuiki namespace {
11033f908c4SFabian Schuiki
11133f908c4SFabian Schuiki /// Simple record class to record timing information.
11233f908c4SFabian Schuiki struct TimeRecord {
TimeRecord__anon672fa56d0111::TimeRecord11333f908c4SFabian Schuiki TimeRecord(double wall = 0.0, double user = 0.0) : wall(wall), user(user) {}
11433f908c4SFabian Schuiki
operator +=__anon672fa56d0111::TimeRecord11533f908c4SFabian Schuiki TimeRecord &operator+=(const TimeRecord &other) {
11633f908c4SFabian Schuiki wall += other.wall;
11733f908c4SFabian Schuiki user += other.user;
11833f908c4SFabian Schuiki return *this;
11933f908c4SFabian Schuiki }
12033f908c4SFabian Schuiki
operator -=__anon672fa56d0111::TimeRecord12133f908c4SFabian Schuiki TimeRecord &operator-=(const TimeRecord &other) {
12233f908c4SFabian Schuiki wall -= other.wall;
12333f908c4SFabian Schuiki user -= other.user;
12433f908c4SFabian Schuiki return *this;
12533f908c4SFabian Schuiki }
12633f908c4SFabian Schuiki
12733f908c4SFabian Schuiki /// Print the current time record to 'os', with a breakdown showing
12833f908c4SFabian Schuiki /// contributions to the give 'total' time record.
print__anon672fa56d0111::TimeRecord12933f908c4SFabian Schuiki void print(raw_ostream &os, const TimeRecord &total) {
13033f908c4SFabian Schuiki if (total.user != total.wall)
13133f908c4SFabian Schuiki os << llvm::format(" %8.4f (%5.1f%%)", user, 100.0 * user / total.user);
13233f908c4SFabian Schuiki os << llvm::format(" %8.4f (%5.1f%%) ", wall, 100.0 * wall / total.wall);
13333f908c4SFabian Schuiki }
13433f908c4SFabian Schuiki
13533f908c4SFabian Schuiki double wall, user;
13633f908c4SFabian Schuiki };
13733f908c4SFabian Schuiki
13833f908c4SFabian Schuiki } // namespace
13933f908c4SFabian Schuiki
14033f908c4SFabian Schuiki /// Utility to print a single line entry in the timer output.
printTimeEntry(raw_ostream & os,unsigned indent,StringRef name,TimeRecord time,TimeRecord total)14133f908c4SFabian Schuiki static void printTimeEntry(raw_ostream &os, unsigned indent, StringRef name,
14233f908c4SFabian Schuiki TimeRecord time, TimeRecord total) {
14333f908c4SFabian Schuiki time.print(os, total);
14433f908c4SFabian Schuiki os.indent(indent) << name << "\n";
14533f908c4SFabian Schuiki }
14633f908c4SFabian Schuiki
14733f908c4SFabian Schuiki /// Utility to print the timer heading information.
printTimeHeader(raw_ostream & os,TimeRecord total)14833f908c4SFabian Schuiki static void printTimeHeader(raw_ostream &os, TimeRecord total) {
14933f908c4SFabian Schuiki // Figure out how many spaces to description name.
15033f908c4SFabian Schuiki unsigned padding = (80 - kTimingDescription.size()) / 2;
15133f908c4SFabian Schuiki os << "===" << std::string(73, '-') << "===\n";
15233f908c4SFabian Schuiki os.indent(padding) << kTimingDescription << '\n';
15333f908c4SFabian Schuiki os << "===" << std::string(73, '-') << "===\n";
15433f908c4SFabian Schuiki
15533f908c4SFabian Schuiki // Print the total time followed by the section headers.
15633f908c4SFabian Schuiki os << llvm::format(" Total Execution Time: %.4f seconds\n\n", total.wall);
15733f908c4SFabian Schuiki if (total.user != total.wall)
15833f908c4SFabian Schuiki os << " ----User Time----";
15933f908c4SFabian Schuiki os << " ----Wall Time---- ----Name----\n";
16033f908c4SFabian Schuiki }
16133f908c4SFabian Schuiki
16233f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
16333f908c4SFabian Schuiki // Timer Implementation for DefaultTimingManager
16433f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
16533f908c4SFabian Schuiki
16633f908c4SFabian Schuiki namespace {
16733f908c4SFabian Schuiki
16833f908c4SFabian Schuiki /// A timer used to sample execution time.
16933f908c4SFabian Schuiki ///
17033f908c4SFabian Schuiki /// Separately tracks wall time and user time to account for parallel threads of
17133f908c4SFabian Schuiki /// execution. Timers are intended to be started and stopped multiple times.
17233f908c4SFabian Schuiki /// Each start and stop will add to the timer's wall and user time.
17333f908c4SFabian Schuiki class TimerImpl {
17433f908c4SFabian Schuiki public:
17533f908c4SFabian Schuiki using ChildrenMap = llvm::MapVector<const void *, std::unique_ptr<TimerImpl>>;
17633f908c4SFabian Schuiki using AsyncChildrenMap = llvm::DenseMap<uint64_t, ChildrenMap>;
17733f908c4SFabian Schuiki
TimerImpl(std::string && name)17833f908c4SFabian Schuiki TimerImpl(std::string &&name) : threadId(llvm::get_threadid()), name(name) {}
17933f908c4SFabian Schuiki
18033f908c4SFabian Schuiki /// Start the timer.
start()1811dd37975Srdzhabarov void start() { startTime = std::chrono::steady_clock::now(); }
18233f908c4SFabian Schuiki
18333f908c4SFabian Schuiki /// Stop the timer.
stop()18433f908c4SFabian Schuiki void stop() {
1851dd37975Srdzhabarov auto newTime = std::chrono::steady_clock::now() - startTime;
18633f908c4SFabian Schuiki wallTime += newTime;
18733f908c4SFabian Schuiki userTime += newTime;
18833f908c4SFabian Schuiki }
18933f908c4SFabian Schuiki
19033f908c4SFabian Schuiki /// Create a child timer nested within this one. Multiple calls to this
19133f908c4SFabian Schuiki /// function with the same unique identifier `id` will return the same child
19233f908c4SFabian Schuiki /// timer.
19333f908c4SFabian Schuiki ///
19433f908c4SFabian Schuiki /// This function can be called from other threads, as long as this timer
19533f908c4SFabian Schuiki /// outlives any uses of the child timer on the other thread.
nest(const void * id,function_ref<std::string ()> nameBuilder)19633f908c4SFabian Schuiki TimerImpl *nest(const void *id, function_ref<std::string()> nameBuilder) {
19733f908c4SFabian Schuiki auto tid = llvm::get_threadid();
19833f908c4SFabian Schuiki if (tid == threadId)
199337c937dSMehdi Amini return nestTail(children[id], nameBuilder);
20033f908c4SFabian Schuiki std::unique_lock<std::mutex> lock(asyncMutex);
201337c937dSMehdi Amini return nestTail(asyncChildren[tid][id], nameBuilder);
20233f908c4SFabian Schuiki }
20333f908c4SFabian Schuiki
20433f908c4SFabian Schuiki /// Tail-called from `nest()`.
nestTail(std::unique_ptr<TimerImpl> & child,function_ref<std::string ()> nameBuilder)20533f908c4SFabian Schuiki TimerImpl *nestTail(std::unique_ptr<TimerImpl> &child,
20633f908c4SFabian Schuiki function_ref<std::string()> nameBuilder) {
20733f908c4SFabian Schuiki if (!child)
20833f908c4SFabian Schuiki child = std::make_unique<TimerImpl>(nameBuilder());
20933f908c4SFabian Schuiki return child.get();
21033f908c4SFabian Schuiki }
21133f908c4SFabian Schuiki
21233f908c4SFabian Schuiki /// Finalize this timer and all its children.
21333f908c4SFabian Schuiki ///
21433f908c4SFabian Schuiki /// If this timer has async children, which happens if `nest()` was called
21533f908c4SFabian Schuiki /// from another thread, this function merges the async childr timers into the
21633f908c4SFabian Schuiki /// main list of child timers.
21733f908c4SFabian Schuiki ///
21833f908c4SFabian Schuiki /// Caution: Call this function only after all nested timers running on other
21933f908c4SFabian Schuiki /// threads no longer need their timers!
finalize()22033f908c4SFabian Schuiki void finalize() {
22133f908c4SFabian Schuiki addAsyncUserTime();
22233f908c4SFabian Schuiki mergeAsyncChildren();
22333f908c4SFabian Schuiki }
22433f908c4SFabian Schuiki
22533f908c4SFabian Schuiki /// Add the user time of all async children to this timer's user time. This is
22633f908c4SFabian Schuiki /// necessary since the user time already contains all regular child timers,
22733f908c4SFabian Schuiki /// but not the asynchronous ones (by the nesting nature of the timers).
addAsyncUserTime()22833f908c4SFabian Schuiki std::chrono::nanoseconds addAsyncUserTime() {
22933f908c4SFabian Schuiki auto added = std::chrono::nanoseconds(0);
23033f908c4SFabian Schuiki for (auto &child : children)
23133f908c4SFabian Schuiki added += child.second->addAsyncUserTime();
23233f908c4SFabian Schuiki for (auto &thread : asyncChildren) {
23333f908c4SFabian Schuiki for (auto &child : thread.second) {
23433f908c4SFabian Schuiki child.second->addAsyncUserTime();
23533f908c4SFabian Schuiki added += child.second->userTime;
23633f908c4SFabian Schuiki }
23733f908c4SFabian Schuiki }
23833f908c4SFabian Schuiki userTime += added;
23933f908c4SFabian Schuiki return added;
24033f908c4SFabian Schuiki }
24133f908c4SFabian Schuiki
24233f908c4SFabian Schuiki /// Ensure that this timer and recursively all its children have their async
24333f908c4SFabian Schuiki /// children folded into the main map of children.
mergeAsyncChildren()24433f908c4SFabian Schuiki void mergeAsyncChildren() {
24533f908c4SFabian Schuiki for (auto &child : children)
24633f908c4SFabian Schuiki child.second->mergeAsyncChildren();
24733f908c4SFabian Schuiki mergeChildren(std::move(asyncChildren));
24833f908c4SFabian Schuiki assert(asyncChildren.empty());
24933f908c4SFabian Schuiki }
25033f908c4SFabian Schuiki
25133f908c4SFabian Schuiki /// Merge multiple child timers into this timer.
25233f908c4SFabian Schuiki ///
25333f908c4SFabian Schuiki /// Children in `other` are added as children to this timer, or, if this timer
25433f908c4SFabian Schuiki /// already contains a child with the corresponding unique identifier, are
25533f908c4SFabian Schuiki /// merged into the existing child.
mergeChildren(ChildrenMap && other)25633f908c4SFabian Schuiki void mergeChildren(ChildrenMap &&other) {
25733f908c4SFabian Schuiki if (children.empty()) {
25833f908c4SFabian Schuiki children = std::move(other);
2591dd37975Srdzhabarov for (auto &child : children)
26033f908c4SFabian Schuiki child.second->mergeAsyncChildren();
26133f908c4SFabian Schuiki } else {
26233f908c4SFabian Schuiki for (auto &child : other)
26333f908c4SFabian Schuiki mergeChild(child.first, std::move(child.second));
26433f908c4SFabian Schuiki other.clear();
26533f908c4SFabian Schuiki }
26633f908c4SFabian Schuiki }
26733f908c4SFabian Schuiki
26833f908c4SFabian Schuiki /// See above.
mergeChildren(AsyncChildrenMap && other)26933f908c4SFabian Schuiki void mergeChildren(AsyncChildrenMap &&other) {
27033f908c4SFabian Schuiki for (auto &thread : other) {
27133f908c4SFabian Schuiki mergeChildren(std::move(thread.second));
27233f908c4SFabian Schuiki assert(thread.second.empty());
27333f908c4SFabian Schuiki }
27433f908c4SFabian Schuiki other.clear();
27533f908c4SFabian Schuiki }
27633f908c4SFabian Schuiki
27733f908c4SFabian Schuiki /// Merge a child timer into this timer for a given unique identifier.
27833f908c4SFabian Schuiki ///
27933f908c4SFabian Schuiki /// Moves all child and async child timers of `other` into this timer's child
28033f908c4SFabian Schuiki /// for the given unique identifier.
mergeChild(const void * id,std::unique_ptr<TimerImpl> && other)28133f908c4SFabian Schuiki void mergeChild(const void *id, std::unique_ptr<TimerImpl> &&other) {
28233f908c4SFabian Schuiki auto &into = children[id];
28333f908c4SFabian Schuiki if (!into) {
28433f908c4SFabian Schuiki into = std::move(other);
28533f908c4SFabian Schuiki into->mergeAsyncChildren();
28633f908c4SFabian Schuiki } else {
28733f908c4SFabian Schuiki into->wallTime = std::max(into->wallTime, other->wallTime);
28833f908c4SFabian Schuiki into->userTime += other->userTime;
28933f908c4SFabian Schuiki into->mergeChildren(std::move(other->children));
29033f908c4SFabian Schuiki into->mergeChildren(std::move(other->asyncChildren));
29133f908c4SFabian Schuiki other.reset();
29233f908c4SFabian Schuiki }
29333f908c4SFabian Schuiki }
29433f908c4SFabian Schuiki
29533f908c4SFabian Schuiki /// Dump a human-readable tree representation of the timer and its children.
29633f908c4SFabian Schuiki /// This is useful for debugging the timing mechanisms and structure of the
29733f908c4SFabian Schuiki /// timers.
dump(raw_ostream & os,unsigned indent=0,unsigned markThreadId=0)29833f908c4SFabian Schuiki void dump(raw_ostream &os, unsigned indent = 0, unsigned markThreadId = 0) {
29933f908c4SFabian Schuiki auto time = getTimeRecord();
30033f908c4SFabian Schuiki os << std::string(indent * 2, ' ') << name << " [" << threadId << "]"
30133f908c4SFabian Schuiki << llvm::format(" %7.4f / %7.4f", time.user, time.wall);
30233f908c4SFabian Schuiki if (threadId != markThreadId && markThreadId != 0)
30333f908c4SFabian Schuiki os << " (*)";
30433f908c4SFabian Schuiki os << "\n";
30533f908c4SFabian Schuiki for (auto &child : children)
30633f908c4SFabian Schuiki child.second->dump(os, indent + 1, threadId);
30733f908c4SFabian Schuiki for (auto &thread : asyncChildren)
30833f908c4SFabian Schuiki for (auto &child : thread.second)
30933f908c4SFabian Schuiki child.second->dump(os, indent + 1, threadId);
31033f908c4SFabian Schuiki }
31133f908c4SFabian Schuiki
31233f908c4SFabian Schuiki /// Returns the time for this timer in seconds.
getTimeRecord()31333f908c4SFabian Schuiki TimeRecord getTimeRecord() {
31433f908c4SFabian Schuiki return TimeRecord(
31533f908c4SFabian Schuiki std::chrono::duration_cast<std::chrono::duration<double>>(wallTime)
31633f908c4SFabian Schuiki .count(),
31733f908c4SFabian Schuiki std::chrono::duration_cast<std::chrono::duration<double>>(userTime)
31833f908c4SFabian Schuiki .count());
31933f908c4SFabian Schuiki }
32033f908c4SFabian Schuiki
32133f908c4SFabian Schuiki /// Print the timing result in list mode.
printAsList(raw_ostream & os,TimeRecord total)32233f908c4SFabian Schuiki void printAsList(raw_ostream &os, TimeRecord total) {
32333f908c4SFabian Schuiki // Flatten the leaf timers in the tree and merge them by name.
32433f908c4SFabian Schuiki llvm::StringMap<TimeRecord> mergedTimers;
32533f908c4SFabian Schuiki std::function<void(TimerImpl *)> addTimer = [&](TimerImpl *timer) {
32633f908c4SFabian Schuiki mergedTimers[timer->name] += timer->getTimeRecord();
32733f908c4SFabian Schuiki for (auto &children : timer->children)
32833f908c4SFabian Schuiki addTimer(children.second.get());
32933f908c4SFabian Schuiki };
33033f908c4SFabian Schuiki addTimer(this);
33133f908c4SFabian Schuiki
33233f908c4SFabian Schuiki // Sort the timing information by wall time.
33333f908c4SFabian Schuiki std::vector<std::pair<StringRef, TimeRecord>> timerNameAndTime;
33433f908c4SFabian Schuiki for (auto &it : mergedTimers)
33533f908c4SFabian Schuiki timerNameAndTime.emplace_back(it.first(), it.second);
33633f908c4SFabian Schuiki llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(),
33733f908c4SFabian Schuiki [](const std::pair<StringRef, TimeRecord> *lhs,
33833f908c4SFabian Schuiki const std::pair<StringRef, TimeRecord> *rhs) {
33933f908c4SFabian Schuiki return llvm::array_pod_sort_comparator<double>(
34033f908c4SFabian Schuiki &rhs->second.wall, &lhs->second.wall);
34133f908c4SFabian Schuiki });
34233f908c4SFabian Schuiki
34333f908c4SFabian Schuiki // Print the timing information sequentially.
34433f908c4SFabian Schuiki for (auto &timeData : timerNameAndTime)
34533f908c4SFabian Schuiki printTimeEntry(os, 0, timeData.first, timeData.second, total);
34633f908c4SFabian Schuiki }
34733f908c4SFabian Schuiki
34833f908c4SFabian Schuiki /// Print the timing result in tree mode.
printAsTree(raw_ostream & os,TimeRecord total,unsigned indent=0)34933f908c4SFabian Schuiki void printAsTree(raw_ostream &os, TimeRecord total, unsigned indent = 0) {
35033f908c4SFabian Schuiki unsigned childIndent = indent;
35133f908c4SFabian Schuiki if (!hidden) {
35233f908c4SFabian Schuiki printTimeEntry(os, indent, name, getTimeRecord(), total);
35333f908c4SFabian Schuiki childIndent += 2;
35433f908c4SFabian Schuiki }
35533f908c4SFabian Schuiki for (auto &child : children) {
35633f908c4SFabian Schuiki child.second->printAsTree(os, total, childIndent);
35733f908c4SFabian Schuiki }
35833f908c4SFabian Schuiki }
35933f908c4SFabian Schuiki
36033f908c4SFabian Schuiki /// Print the current timing information.
print(raw_ostream & os,DisplayMode displayMode)36133f908c4SFabian Schuiki void print(raw_ostream &os, DisplayMode displayMode) {
36233f908c4SFabian Schuiki // Print the banner.
36333f908c4SFabian Schuiki auto total = getTimeRecord();
36433f908c4SFabian Schuiki printTimeHeader(os, total);
36533f908c4SFabian Schuiki
36633f908c4SFabian Schuiki // Defer to a specialized printer for each display mode.
36733f908c4SFabian Schuiki switch (displayMode) {
36833f908c4SFabian Schuiki case DisplayMode::List:
36933f908c4SFabian Schuiki printAsList(os, total);
37033f908c4SFabian Schuiki break;
37133f908c4SFabian Schuiki case DisplayMode::Tree:
37233f908c4SFabian Schuiki printAsTree(os, total);
37333f908c4SFabian Schuiki break;
37433f908c4SFabian Schuiki }
37533f908c4SFabian Schuiki
37633f908c4SFabian Schuiki // Print the top-level time not accounted for by child timers, and the
37733f908c4SFabian Schuiki // total.
37833f908c4SFabian Schuiki auto rest = total;
37933f908c4SFabian Schuiki for (auto &child : children)
38033f908c4SFabian Schuiki rest -= child.second->getTimeRecord();
38133f908c4SFabian Schuiki printTimeEntry(os, 0, "Rest", rest, total);
38233f908c4SFabian Schuiki printTimeEntry(os, 0, "Total", total, total);
38333f908c4SFabian Schuiki os.flush();
38433f908c4SFabian Schuiki }
38533f908c4SFabian Schuiki
38633f908c4SFabian Schuiki /// The last time instant at which the timer was started.
3871dd37975Srdzhabarov std::chrono::time_point<std::chrono::steady_clock> startTime;
38833f908c4SFabian Schuiki
38933f908c4SFabian Schuiki /// Accumulated wall time. If multiple threads of execution are merged into
39033f908c4SFabian Schuiki /// this timer, the wall time will hold the maximum wall time of each thread
39133f908c4SFabian Schuiki /// of execution.
39233f908c4SFabian Schuiki std::chrono::nanoseconds wallTime = std::chrono::nanoseconds(0);
39333f908c4SFabian Schuiki
39433f908c4SFabian Schuiki /// Accumulated user time. If multiple threads of execution are merged into
39533f908c4SFabian Schuiki /// this timer, each thread's user time is added here.
39633f908c4SFabian Schuiki std::chrono::nanoseconds userTime = std::chrono::nanoseconds(0);
39733f908c4SFabian Schuiki
39833f908c4SFabian Schuiki /// The thread on which this timer is running.
39933f908c4SFabian Schuiki uint64_t threadId;
40033f908c4SFabian Schuiki
40133f908c4SFabian Schuiki /// A descriptive name for this timer.
40233f908c4SFabian Schuiki std::string name;
40333f908c4SFabian Schuiki
40433f908c4SFabian Schuiki /// Whether to omit this timer from reports and directly show its children.
40533f908c4SFabian Schuiki bool hidden = false;
40633f908c4SFabian Schuiki
40733f908c4SFabian Schuiki /// Child timers on the same thread the timer itself. We keep at most one
40833f908c4SFabian Schuiki /// timer per unique identifier.
40933f908c4SFabian Schuiki ChildrenMap children;
41033f908c4SFabian Schuiki
41133f908c4SFabian Schuiki /// Child timers on other threads. We keep at most one timer per unique
41233f908c4SFabian Schuiki /// identifier.
41333f908c4SFabian Schuiki AsyncChildrenMap asyncChildren;
41433f908c4SFabian Schuiki
41533f908c4SFabian Schuiki /// Mutex for the async children.
41633f908c4SFabian Schuiki std::mutex asyncMutex;
41733f908c4SFabian Schuiki };
41833f908c4SFabian Schuiki
41933f908c4SFabian Schuiki } // namespace
42033f908c4SFabian Schuiki
42133f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
42233f908c4SFabian Schuiki // DefaultTimingManager
42333f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
42433f908c4SFabian Schuiki
42533f908c4SFabian Schuiki namespace mlir {
42633f908c4SFabian Schuiki namespace detail {
42733f908c4SFabian Schuiki
42833f908c4SFabian Schuiki /// Implementation details of the `DefaultTimingManager`.
42933f908c4SFabian Schuiki class DefaultTimingManagerImpl {
43033f908c4SFabian Schuiki public:
43133f908c4SFabian Schuiki /// Whether we should do our work or not.
43233f908c4SFabian Schuiki bool enabled = false;
43333f908c4SFabian Schuiki
43433f908c4SFabian Schuiki /// The configured display mode.
43533f908c4SFabian Schuiki DisplayMode displayMode = DisplayMode::Tree;
43633f908c4SFabian Schuiki
43733f908c4SFabian Schuiki /// The stream where we should print our output. This will always be non-null.
43833f908c4SFabian Schuiki raw_ostream *output = &llvm::errs();
43933f908c4SFabian Schuiki
44033f908c4SFabian Schuiki /// The root timer.
44133f908c4SFabian Schuiki std::unique_ptr<TimerImpl> rootTimer;
44233f908c4SFabian Schuiki };
44333f908c4SFabian Schuiki
44433f908c4SFabian Schuiki } // namespace detail
44533f908c4SFabian Schuiki } // namespace mlir
44633f908c4SFabian Schuiki
DefaultTimingManager()44733f908c4SFabian Schuiki DefaultTimingManager::DefaultTimingManager()
44833f908c4SFabian Schuiki : impl(std::make_unique<DefaultTimingManagerImpl>()) {
44933f908c4SFabian Schuiki clear(); // initializes the root timer
45033f908c4SFabian Schuiki }
45133f908c4SFabian Schuiki
~DefaultTimingManager()45233f908c4SFabian Schuiki DefaultTimingManager::~DefaultTimingManager() { print(); }
45333f908c4SFabian Schuiki
45433f908c4SFabian Schuiki /// Enable or disable execution time sampling.
setEnabled(bool enabled)45533f908c4SFabian Schuiki void DefaultTimingManager::setEnabled(bool enabled) { impl->enabled = enabled; }
45633f908c4SFabian Schuiki
45733f908c4SFabian Schuiki /// Return whether execution time sampling is enabled.
isEnabled() const45833f908c4SFabian Schuiki bool DefaultTimingManager::isEnabled() const { return impl->enabled; }
45933f908c4SFabian Schuiki
46033f908c4SFabian Schuiki /// Change the display mode.
setDisplayMode(DisplayMode displayMode)46133f908c4SFabian Schuiki void DefaultTimingManager::setDisplayMode(DisplayMode displayMode) {
46233f908c4SFabian Schuiki impl->displayMode = displayMode;
46333f908c4SFabian Schuiki }
46433f908c4SFabian Schuiki
46533f908c4SFabian Schuiki /// Return the current display mode;
getDisplayMode() const46633f908c4SFabian Schuiki DefaultTimingManager::DisplayMode DefaultTimingManager::getDisplayMode() const {
46733f908c4SFabian Schuiki return impl->displayMode;
46833f908c4SFabian Schuiki }
46933f908c4SFabian Schuiki
47033f908c4SFabian Schuiki /// Change the stream where the output will be printed to.
setOutput(raw_ostream & os)47133f908c4SFabian Schuiki void DefaultTimingManager::setOutput(raw_ostream &os) { impl->output = &os; }
47233f908c4SFabian Schuiki
47333f908c4SFabian Schuiki /// Return the current output stream where the output will be printed to.
getOutput() const47433f908c4SFabian Schuiki raw_ostream &DefaultTimingManager::getOutput() const {
47533f908c4SFabian Schuiki assert(impl->output);
47633f908c4SFabian Schuiki return *impl->output;
47733f908c4SFabian Schuiki }
47833f908c4SFabian Schuiki
47933f908c4SFabian Schuiki /// Print and clear the timing results.
print()48033f908c4SFabian Schuiki void DefaultTimingManager::print() {
48133f908c4SFabian Schuiki if (impl->enabled) {
48233f908c4SFabian Schuiki impl->rootTimer->finalize();
48333f908c4SFabian Schuiki impl->rootTimer->print(*impl->output, impl->displayMode);
48433f908c4SFabian Schuiki }
48533f908c4SFabian Schuiki clear();
48633f908c4SFabian Schuiki }
48733f908c4SFabian Schuiki
48833f908c4SFabian Schuiki /// Clear the timing results.
clear()48933f908c4SFabian Schuiki void DefaultTimingManager::clear() {
49033f908c4SFabian Schuiki impl->rootTimer = std::make_unique<TimerImpl>("root");
49133f908c4SFabian Schuiki impl->rootTimer->hidden = true;
49233f908c4SFabian Schuiki }
49333f908c4SFabian Schuiki
49433f908c4SFabian Schuiki /// Debug print the timer data structures to an output stream.
dumpTimers(raw_ostream & os)49533f908c4SFabian Schuiki void DefaultTimingManager::dumpTimers(raw_ostream &os) {
49633f908c4SFabian Schuiki impl->rootTimer->dump(os);
49733f908c4SFabian Schuiki }
49833f908c4SFabian Schuiki
49933f908c4SFabian Schuiki /// Debug print the timers as a list.
dumpAsList(raw_ostream & os)50033f908c4SFabian Schuiki void DefaultTimingManager::dumpAsList(raw_ostream &os) {
50133f908c4SFabian Schuiki impl->rootTimer->finalize();
50233f908c4SFabian Schuiki impl->rootTimer->print(os, DisplayMode::List);
50333f908c4SFabian Schuiki }
50433f908c4SFabian Schuiki
50533f908c4SFabian Schuiki /// Debug print the timers as a tree.
dumpAsTree(raw_ostream & os)50633f908c4SFabian Schuiki void DefaultTimingManager::dumpAsTree(raw_ostream &os) {
50733f908c4SFabian Schuiki impl->rootTimer->finalize();
50833f908c4SFabian Schuiki impl->rootTimer->print(os, DisplayMode::Tree);
50933f908c4SFabian Schuiki }
51033f908c4SFabian Schuiki
rootTimer()51133f908c4SFabian Schuiki Optional<void *> DefaultTimingManager::rootTimer() {
51233f908c4SFabian Schuiki if (impl->enabled)
51333f908c4SFabian Schuiki return impl->rootTimer.get();
51433f908c4SFabian Schuiki return llvm::None;
51533f908c4SFabian Schuiki }
51633f908c4SFabian Schuiki
startTimer(void * handle)51733f908c4SFabian Schuiki void DefaultTimingManager::startTimer(void *handle) {
51833f908c4SFabian Schuiki static_cast<TimerImpl *>(handle)->start();
51933f908c4SFabian Schuiki }
52033f908c4SFabian Schuiki
stopTimer(void * handle)52133f908c4SFabian Schuiki void DefaultTimingManager::stopTimer(void *handle) {
52233f908c4SFabian Schuiki static_cast<TimerImpl *>(handle)->stop();
52333f908c4SFabian Schuiki }
52433f908c4SFabian Schuiki
nestTimer(void * handle,const void * id,function_ref<std::string ()> nameBuilder)52533f908c4SFabian Schuiki void *DefaultTimingManager::nestTimer(void *handle, const void *id,
52633f908c4SFabian Schuiki function_ref<std::string()> nameBuilder) {
527337c937dSMehdi Amini return static_cast<TimerImpl *>(handle)->nest(id, nameBuilder);
52833f908c4SFabian Schuiki }
52933f908c4SFabian Schuiki
hideTimer(void * handle)53033f908c4SFabian Schuiki void DefaultTimingManager::hideTimer(void *handle) {
53133f908c4SFabian Schuiki static_cast<TimerImpl *>(handle)->hidden = true;
53233f908c4SFabian Schuiki }
53333f908c4SFabian Schuiki
53433f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
53533f908c4SFabian Schuiki // DefaultTimingManager Command Line Options
53633f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
53733f908c4SFabian Schuiki
53833f908c4SFabian Schuiki namespace {
53933f908c4SFabian Schuiki struct DefaultTimingManagerOptions {
54033f908c4SFabian Schuiki llvm::cl::opt<bool> timing{"mlir-timing",
54133f908c4SFabian Schuiki llvm::cl::desc("Display execution times"),
54233f908c4SFabian Schuiki llvm::cl::init(false)};
54333f908c4SFabian Schuiki llvm::cl::opt<DisplayMode> displayMode{
54433f908c4SFabian Schuiki "mlir-timing-display", llvm::cl::desc("Display method for timing data"),
54533f908c4SFabian Schuiki llvm::cl::init(DisplayMode::Tree),
54633f908c4SFabian Schuiki llvm::cl::values(
54733f908c4SFabian Schuiki clEnumValN(DisplayMode::List, "list",
54833f908c4SFabian Schuiki "display the results in a list sorted by total time"),
54933f908c4SFabian Schuiki clEnumValN(DisplayMode::Tree, "tree",
55033f908c4SFabian Schuiki "display the results ina with a nested tree view"))};
55133f908c4SFabian Schuiki };
552be0a7e9fSMehdi Amini } // namespace
55333f908c4SFabian Schuiki
55433f908c4SFabian Schuiki static llvm::ManagedStatic<DefaultTimingManagerOptions> options;
55533f908c4SFabian Schuiki
registerDefaultTimingManagerCLOptions()55633f908c4SFabian Schuiki void mlir::registerDefaultTimingManagerCLOptions() {
55733f908c4SFabian Schuiki // Make sure that the options struct has been constructed.
55833f908c4SFabian Schuiki *options;
55933f908c4SFabian Schuiki }
56033f908c4SFabian Schuiki
applyDefaultTimingManagerCLOptions(DefaultTimingManager & tm)56133f908c4SFabian Schuiki void mlir::applyDefaultTimingManagerCLOptions(DefaultTimingManager &tm) {
56233f908c4SFabian Schuiki if (!options.isConstructed())
56333f908c4SFabian Schuiki return;
56433f908c4SFabian Schuiki tm.setEnabled(options->timing);
56533f908c4SFabian Schuiki tm.setDisplayMode(options->displayMode);
56633f908c4SFabian Schuiki }
567