xref: /llvm-project/mlir/lib/Support/Timing.cpp (revision d5ec49ff3dc26cdbe350e9cafc6b8e331fff7911)
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>
306f241426SMehdi Amini #include <optional>
3133f908c4SFabian Schuiki 
3233f908c4SFabian Schuiki using namespace mlir;
3333f908c4SFabian Schuiki using namespace detail;
3433f908c4SFabian Schuiki using DisplayMode = DefaultTimingManager::DisplayMode;
35362aa434SHsiangkai Wang using OutputFormat = DefaultTimingManager::OutputFormat;
3633f908c4SFabian Schuiki 
3733f908c4SFabian Schuiki constexpr llvm::StringLiteral kTimingDescription =
3833f908c4SFabian Schuiki     "... Execution time report ...";
3933f908c4SFabian Schuiki 
4033f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
4133f908c4SFabian Schuiki // TimingManager
4233f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
4333f908c4SFabian Schuiki 
4433f908c4SFabian Schuiki namespace mlir {
4533f908c4SFabian Schuiki namespace detail {
4633f908c4SFabian Schuiki /// Private implementation details of the `TimingManager`.
4733f908c4SFabian Schuiki class TimingManagerImpl {
4833f908c4SFabian Schuiki public:
4933f908c4SFabian Schuiki   // Identifier allocator, map, and mutex for thread safety.
5033f908c4SFabian Schuiki   llvm::BumpPtrAllocator identifierAllocator;
5133f908c4SFabian Schuiki   llvm::StringSet<llvm::BumpPtrAllocator &> identifiers;
5233f908c4SFabian Schuiki   llvm::sys::SmartRWMutex<true> identifierMutex;
5333f908c4SFabian Schuiki 
5433f908c4SFabian Schuiki   /// A thread local cache of identifiers to reduce lock contention.
5534bcadc3SKazu Hirata   ThreadLocalCache<llvm::StringMap<llvm::StringMapEntry<std::nullopt_t> *>>
5633f908c4SFabian Schuiki       localIdentifierCache;
5733f908c4SFabian Schuiki 
TimingManagerImpl()5833f908c4SFabian Schuiki   TimingManagerImpl() : identifiers(identifierAllocator) {}
5933f908c4SFabian Schuiki };
6033f908c4SFabian Schuiki } // namespace detail
6133f908c4SFabian Schuiki } // namespace mlir
6233f908c4SFabian Schuiki 
TimingManager()6333f908c4SFabian Schuiki TimingManager::TimingManager() : impl(std::make_unique<TimingManagerImpl>()) {}
6433f908c4SFabian Schuiki 
65e5639b3fSMehdi Amini TimingManager::~TimingManager() = default;
6633f908c4SFabian Schuiki 
6733f908c4SFabian Schuiki /// Get the root timer of this timing manager.
getRootTimer()6833f908c4SFabian Schuiki Timer TimingManager::getRootTimer() {
6933f908c4SFabian Schuiki   auto rt = rootTimer();
70064a08cdSKazu Hirata   return rt ? Timer(*this, *rt) : Timer();
7133f908c4SFabian Schuiki }
7233f908c4SFabian Schuiki 
7333f908c4SFabian Schuiki /// Get the root timer of this timing manager wrapped in a `TimingScope`.
getRootScope()7433f908c4SFabian Schuiki TimingScope TimingManager::getRootScope() {
7533f908c4SFabian Schuiki   return TimingScope(getRootTimer());
7633f908c4SFabian Schuiki }
7733f908c4SFabian Schuiki 
7833f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
7933f908c4SFabian Schuiki // Identifier uniquing
8033f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
8133f908c4SFabian Schuiki 
8233f908c4SFabian Schuiki /// Return an identifier for the specified string.
get(StringRef str,TimingManager & tm)8333f908c4SFabian Schuiki TimingIdentifier TimingIdentifier::get(StringRef str, TimingManager &tm) {
8433f908c4SFabian Schuiki   // Check for an existing instance in the local cache.
8533f908c4SFabian Schuiki   auto &impl = *tm.impl;
8633f908c4SFabian Schuiki   auto *&localEntry = (*impl.localIdentifierCache)[str];
8733f908c4SFabian Schuiki   if (localEntry)
8833f908c4SFabian Schuiki     return TimingIdentifier(localEntry);
8933f908c4SFabian Schuiki 
9033f908c4SFabian Schuiki   // Check for an existing identifier in read-only mode.
9133f908c4SFabian Schuiki   {
9233f908c4SFabian Schuiki     llvm::sys::SmartScopedReader<true> contextLock(impl.identifierMutex);
9333f908c4SFabian Schuiki     auto it = impl.identifiers.find(str);
9433f908c4SFabian Schuiki     if (it != impl.identifiers.end()) {
9533f908c4SFabian Schuiki       localEntry = &*it;
9633f908c4SFabian Schuiki       return TimingIdentifier(localEntry);
9733f908c4SFabian Schuiki     }
9833f908c4SFabian Schuiki   }
9933f908c4SFabian Schuiki 
10033f908c4SFabian Schuiki   // Acquire a writer-lock so that we can safely create the new instance.
10133f908c4SFabian Schuiki   llvm::sys::SmartScopedWriter<true> contextLock(impl.identifierMutex);
10233f908c4SFabian Schuiki   auto it = impl.identifiers.insert(str).first;
10333f908c4SFabian Schuiki   localEntry = &*it;
10433f908c4SFabian Schuiki   return TimingIdentifier(localEntry);
10533f908c4SFabian Schuiki }
10633f908c4SFabian Schuiki 
10733f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
10833f908c4SFabian Schuiki // Helpers for time record printing
10933f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
11033f908c4SFabian Schuiki 
11133f908c4SFabian Schuiki namespace {
11233f908c4SFabian Schuiki 
113362aa434SHsiangkai Wang class OutputTextStrategy : public OutputStrategy {
114362aa434SHsiangkai Wang public:
OutputTextStrategy(raw_ostream & os)115362aa434SHsiangkai Wang   OutputTextStrategy(raw_ostream &os) : OutputStrategy(os) {}
11633f908c4SFabian Schuiki 
printHeader(const TimeRecord & total)117362aa434SHsiangkai Wang   void printHeader(const TimeRecord &total) override {
11833f908c4SFabian Schuiki     // Figure out how many spaces to description name.
11933f908c4SFabian Schuiki     unsigned padding = (80 - kTimingDescription.size()) / 2;
12033f908c4SFabian Schuiki     os << "===" << std::string(73, '-') << "===\n";
12133f908c4SFabian Schuiki     os.indent(padding) << kTimingDescription << '\n';
12233f908c4SFabian Schuiki     os << "===" << std::string(73, '-') << "===\n";
12333f908c4SFabian Schuiki 
12433f908c4SFabian Schuiki     // Print the total time followed by the section headers.
12533f908c4SFabian Schuiki     os << llvm::format("  Total Execution Time: %.4f seconds\n\n", total.wall);
12633f908c4SFabian Schuiki     if (total.user != total.wall)
12733f908c4SFabian Schuiki       os << "  ----User Time----";
12833f908c4SFabian Schuiki     os << "  ----Wall Time----  ----Name----\n";
12933f908c4SFabian Schuiki   }
13033f908c4SFabian Schuiki 
printFooter()131362aa434SHsiangkai Wang   void printFooter() override { os.flush(); }
132362aa434SHsiangkai Wang 
printTime(const TimeRecord & time,const TimeRecord & total)133362aa434SHsiangkai Wang   void printTime(const TimeRecord &time, const TimeRecord &total) override {
134362aa434SHsiangkai Wang     if (total.user != total.wall) {
135362aa434SHsiangkai Wang       os << llvm::format("  %8.4f (%5.1f%%)", time.user,
136362aa434SHsiangkai Wang                          100.0 * time.user / total.user);
137362aa434SHsiangkai Wang     }
138362aa434SHsiangkai Wang     os << llvm::format("  %8.4f (%5.1f%%)  ", time.wall,
139362aa434SHsiangkai Wang                        100.0 * time.wall / total.wall);
140362aa434SHsiangkai Wang   }
141362aa434SHsiangkai Wang 
printListEntry(StringRef name,const TimeRecord & time,const TimeRecord & total,bool lastEntry)142362aa434SHsiangkai Wang   void printListEntry(StringRef name, const TimeRecord &time,
143362aa434SHsiangkai Wang                       const TimeRecord &total, bool lastEntry) override {
144362aa434SHsiangkai Wang     printTime(time, total);
145362aa434SHsiangkai Wang     os << name << "\n";
146362aa434SHsiangkai Wang   }
147362aa434SHsiangkai Wang 
printTreeEntry(unsigned indent,StringRef name,const TimeRecord & time,const TimeRecord & total)148362aa434SHsiangkai Wang   void printTreeEntry(unsigned indent, StringRef name, const TimeRecord &time,
149362aa434SHsiangkai Wang                       const TimeRecord &total) override {
150362aa434SHsiangkai Wang     printTime(time, total);
151362aa434SHsiangkai Wang     os.indent(indent) << name << "\n";
152362aa434SHsiangkai Wang   }
153362aa434SHsiangkai Wang 
printTreeEntryEnd(unsigned indent,bool lastEntry)154362aa434SHsiangkai Wang   void printTreeEntryEnd(unsigned indent, bool lastEntry) override {}
155362aa434SHsiangkai Wang };
156362aa434SHsiangkai Wang 
157362aa434SHsiangkai Wang class OutputJsonStrategy : public OutputStrategy {
158362aa434SHsiangkai Wang public:
OutputJsonStrategy(raw_ostream & os)159362aa434SHsiangkai Wang   OutputJsonStrategy(raw_ostream &os) : OutputStrategy(os) {}
160362aa434SHsiangkai Wang 
printHeader(const TimeRecord & total)161362aa434SHsiangkai Wang   void printHeader(const TimeRecord &total) override { os << "[" << "\n"; }
162362aa434SHsiangkai Wang 
printFooter()163362aa434SHsiangkai Wang   void printFooter() override {
164362aa434SHsiangkai Wang     os << "]" << "\n";
165362aa434SHsiangkai Wang     os.flush();
166362aa434SHsiangkai Wang   }
167362aa434SHsiangkai Wang 
printTime(const TimeRecord & time,const TimeRecord & total)168362aa434SHsiangkai Wang   void printTime(const TimeRecord &time, const TimeRecord &total) override {
169362aa434SHsiangkai Wang     if (total.user != total.wall) {
170362aa434SHsiangkai Wang       os << "\"user\": {";
171362aa434SHsiangkai Wang       os << "\"duration\": " << llvm::format("%8.4f", time.user) << ", ";
172362aa434SHsiangkai Wang       os << "\"percentage\": "
173362aa434SHsiangkai Wang          << llvm::format("%5.1f", 100.0 * time.user / total.user);
174362aa434SHsiangkai Wang       os << "}, ";
175362aa434SHsiangkai Wang     }
176362aa434SHsiangkai Wang     os << "\"wall\": {";
177362aa434SHsiangkai Wang     os << "\"duration\": " << llvm::format("%8.4f", time.wall) << ", ";
178362aa434SHsiangkai Wang     os << "\"percentage\": "
179362aa434SHsiangkai Wang        << llvm::format("%5.1f", 100.0 * time.wall / total.wall);
180362aa434SHsiangkai Wang     os << "}";
181362aa434SHsiangkai Wang   }
182362aa434SHsiangkai Wang 
printListEntry(StringRef name,const TimeRecord & time,const TimeRecord & total,bool lastEntry)183362aa434SHsiangkai Wang   void printListEntry(StringRef name, const TimeRecord &time,
184362aa434SHsiangkai Wang                       const TimeRecord &total, bool lastEntry) override {
185362aa434SHsiangkai Wang     os << "{";
186362aa434SHsiangkai Wang     printTime(time, total);
187362aa434SHsiangkai Wang     os << ", \"name\": " << "\"" << name << "\"";
188362aa434SHsiangkai Wang     os << "}";
189362aa434SHsiangkai Wang     if (!lastEntry)
190362aa434SHsiangkai Wang       os << ",";
191362aa434SHsiangkai Wang     os << "\n";
192362aa434SHsiangkai Wang   }
193362aa434SHsiangkai Wang 
printTreeEntry(unsigned indent,StringRef name,const TimeRecord & time,const TimeRecord & total)194362aa434SHsiangkai Wang   void printTreeEntry(unsigned indent, StringRef name, const TimeRecord &time,
195362aa434SHsiangkai Wang                       const TimeRecord &total) override {
196362aa434SHsiangkai Wang     os.indent(indent) << "{";
197362aa434SHsiangkai Wang     printTime(time, total);
198362aa434SHsiangkai Wang     os << ", \"name\": " << "\"" << name << "\"";
199362aa434SHsiangkai Wang     os << ", \"passes\": [" << "\n";
200362aa434SHsiangkai Wang   }
201362aa434SHsiangkai Wang 
printTreeEntryEnd(unsigned indent,bool lastEntry)202362aa434SHsiangkai Wang   void printTreeEntryEnd(unsigned indent, bool lastEntry) override {
203362aa434SHsiangkai Wang     os.indent(indent) << "{}]";
204362aa434SHsiangkai Wang     os << "}";
205362aa434SHsiangkai Wang     if (!lastEntry)
206362aa434SHsiangkai Wang       os << ",";
207362aa434SHsiangkai Wang     os << "\n";
208362aa434SHsiangkai Wang   }
209362aa434SHsiangkai Wang };
210362aa434SHsiangkai Wang 
211362aa434SHsiangkai Wang } // namespace
212362aa434SHsiangkai Wang 
21333f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
21433f908c4SFabian Schuiki // Timer Implementation for DefaultTimingManager
21533f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
21633f908c4SFabian Schuiki 
21733f908c4SFabian Schuiki namespace {
21833f908c4SFabian Schuiki 
21933f908c4SFabian Schuiki /// A timer used to sample execution time.
22033f908c4SFabian Schuiki ///
22133f908c4SFabian Schuiki /// Separately tracks wall time and user time to account for parallel threads of
22233f908c4SFabian Schuiki /// execution. Timers are intended to be started and stopped multiple times.
22333f908c4SFabian Schuiki /// Each start and stop will add to the timer's wall and user time.
22433f908c4SFabian Schuiki class TimerImpl {
22533f908c4SFabian Schuiki public:
22633f908c4SFabian Schuiki   using ChildrenMap = llvm::MapVector<const void *, std::unique_ptr<TimerImpl>>;
22733f908c4SFabian Schuiki   using AsyncChildrenMap = llvm::DenseMap<uint64_t, ChildrenMap>;
22833f908c4SFabian Schuiki 
TimerImpl(std::string && name,std::unique_ptr<OutputStrategy> & output)229362aa434SHsiangkai Wang   TimerImpl(std::string &&name, std::unique_ptr<OutputStrategy> &output)
230362aa434SHsiangkai Wang       : threadId(llvm::get_threadid()), name(name), output(output) {}
23133f908c4SFabian Schuiki 
23233f908c4SFabian Schuiki   /// Start the timer.
start()2331dd37975Srdzhabarov   void start() { startTime = std::chrono::steady_clock::now(); }
23433f908c4SFabian Schuiki 
23533f908c4SFabian Schuiki   /// Stop the timer.
stop()23633f908c4SFabian Schuiki   void stop() {
2371dd37975Srdzhabarov     auto newTime = std::chrono::steady_clock::now() - startTime;
23833f908c4SFabian Schuiki     wallTime += newTime;
23933f908c4SFabian Schuiki     userTime += newTime;
24033f908c4SFabian Schuiki   }
24133f908c4SFabian Schuiki 
24233f908c4SFabian Schuiki   /// Create a child timer nested within this one. Multiple calls to this
24333f908c4SFabian Schuiki   /// function with the same unique identifier `id` will return the same child
24433f908c4SFabian Schuiki   /// timer.
24533f908c4SFabian Schuiki   ///
24633f908c4SFabian Schuiki   /// This function can be called from other threads, as long as this timer
24733f908c4SFabian Schuiki   /// outlives any uses of the child timer on the other thread.
nest(const void * id,function_ref<std::string ()> nameBuilder)24833f908c4SFabian Schuiki   TimerImpl *nest(const void *id, function_ref<std::string()> nameBuilder) {
24933f908c4SFabian Schuiki     auto tid = llvm::get_threadid();
25033f908c4SFabian Schuiki     if (tid == threadId)
251337c937dSMehdi Amini       return nestTail(children[id], nameBuilder);
25233f908c4SFabian Schuiki     std::unique_lock<std::mutex> lock(asyncMutex);
253337c937dSMehdi Amini     return nestTail(asyncChildren[tid][id], nameBuilder);
25433f908c4SFabian Schuiki   }
25533f908c4SFabian Schuiki 
25633f908c4SFabian Schuiki   /// Tail-called from `nest()`.
nestTail(std::unique_ptr<TimerImpl> & child,function_ref<std::string ()> nameBuilder)25733f908c4SFabian Schuiki   TimerImpl *nestTail(std::unique_ptr<TimerImpl> &child,
25833f908c4SFabian Schuiki                       function_ref<std::string()> nameBuilder) {
25933f908c4SFabian Schuiki     if (!child)
260362aa434SHsiangkai Wang       child = std::make_unique<TimerImpl>(nameBuilder(), output);
26133f908c4SFabian Schuiki     return child.get();
26233f908c4SFabian Schuiki   }
26333f908c4SFabian Schuiki 
26433f908c4SFabian Schuiki   /// Finalize this timer and all its children.
26533f908c4SFabian Schuiki   ///
26633f908c4SFabian Schuiki   /// If this timer has async children, which happens if `nest()` was called
26733f908c4SFabian Schuiki   /// from another thread, this function merges the async childr timers into the
26833f908c4SFabian Schuiki   /// main list of child timers.
26933f908c4SFabian Schuiki   ///
27033f908c4SFabian Schuiki   /// Caution: Call this function only after all nested timers running on other
27133f908c4SFabian Schuiki   /// threads no longer need their timers!
finalize()27233f908c4SFabian Schuiki   void finalize() {
27333f908c4SFabian Schuiki     addAsyncUserTime();
27433f908c4SFabian Schuiki     mergeAsyncChildren();
27533f908c4SFabian Schuiki   }
27633f908c4SFabian Schuiki 
27733f908c4SFabian Schuiki   /// Add the user time of all async children to this timer's user time. This is
27833f908c4SFabian Schuiki   /// necessary since the user time already contains all regular child timers,
27933f908c4SFabian Schuiki   /// but not the asynchronous ones (by the nesting nature of the timers).
addAsyncUserTime()28033f908c4SFabian Schuiki   std::chrono::nanoseconds addAsyncUserTime() {
28133f908c4SFabian Schuiki     auto added = std::chrono::nanoseconds(0);
28233f908c4SFabian Schuiki     for (auto &child : children)
28333f908c4SFabian Schuiki       added += child.second->addAsyncUserTime();
28433f908c4SFabian Schuiki     for (auto &thread : asyncChildren) {
28533f908c4SFabian Schuiki       for (auto &child : thread.second) {
28633f908c4SFabian Schuiki         child.second->addAsyncUserTime();
28733f908c4SFabian Schuiki         added += child.second->userTime;
28833f908c4SFabian Schuiki       }
28933f908c4SFabian Schuiki     }
29033f908c4SFabian Schuiki     userTime += added;
29133f908c4SFabian Schuiki     return added;
29233f908c4SFabian Schuiki   }
29333f908c4SFabian Schuiki 
29433f908c4SFabian Schuiki   /// Ensure that this timer and recursively all its children have their async
29533f908c4SFabian Schuiki   /// children folded into the main map of children.
mergeAsyncChildren()29633f908c4SFabian Schuiki   void mergeAsyncChildren() {
29733f908c4SFabian Schuiki     for (auto &child : children)
29833f908c4SFabian Schuiki       child.second->mergeAsyncChildren();
29933f908c4SFabian Schuiki     mergeChildren(std::move(asyncChildren));
30033f908c4SFabian Schuiki     assert(asyncChildren.empty());
30133f908c4SFabian Schuiki   }
30233f908c4SFabian Schuiki 
30333f908c4SFabian Schuiki   /// Merge multiple child timers into this timer.
30433f908c4SFabian Schuiki   ///
30533f908c4SFabian Schuiki   /// Children in `other` are added as children to this timer, or, if this timer
30633f908c4SFabian Schuiki   /// already contains a child with the corresponding unique identifier, are
30733f908c4SFabian Schuiki   /// merged into the existing child.
mergeChildren(ChildrenMap && other)30833f908c4SFabian Schuiki   void mergeChildren(ChildrenMap &&other) {
30933f908c4SFabian Schuiki     if (children.empty()) {
31033f908c4SFabian Schuiki       children = std::move(other);
3111dd37975Srdzhabarov       for (auto &child : children)
31233f908c4SFabian Schuiki         child.second->mergeAsyncChildren();
31333f908c4SFabian Schuiki     } else {
31433f908c4SFabian Schuiki       for (auto &child : other)
31533f908c4SFabian Schuiki         mergeChild(child.first, std::move(child.second));
31633f908c4SFabian Schuiki       other.clear();
31733f908c4SFabian Schuiki     }
31833f908c4SFabian Schuiki   }
31933f908c4SFabian Schuiki 
32033f908c4SFabian Schuiki   /// See above.
mergeChildren(AsyncChildrenMap && other)32133f908c4SFabian Schuiki   void mergeChildren(AsyncChildrenMap &&other) {
32233f908c4SFabian Schuiki     for (auto &thread : other) {
32333f908c4SFabian Schuiki       mergeChildren(std::move(thread.second));
32433f908c4SFabian Schuiki       assert(thread.second.empty());
32533f908c4SFabian Schuiki     }
32633f908c4SFabian Schuiki     other.clear();
32733f908c4SFabian Schuiki   }
32833f908c4SFabian Schuiki 
32933f908c4SFabian Schuiki   /// Merge a child timer into this timer for a given unique identifier.
33033f908c4SFabian Schuiki   ///
33133f908c4SFabian Schuiki   /// Moves all child and async child timers of `other` into this timer's child
33233f908c4SFabian Schuiki   /// for the given unique identifier.
mergeChild(const void * id,std::unique_ptr<TimerImpl> && other)33333f908c4SFabian Schuiki   void mergeChild(const void *id, std::unique_ptr<TimerImpl> &&other) {
33433f908c4SFabian Schuiki     auto &into = children[id];
33533f908c4SFabian Schuiki     if (!into) {
33633f908c4SFabian Schuiki       into = std::move(other);
33733f908c4SFabian Schuiki       into->mergeAsyncChildren();
33833f908c4SFabian Schuiki     } else {
33933f908c4SFabian Schuiki       into->wallTime = std::max(into->wallTime, other->wallTime);
34033f908c4SFabian Schuiki       into->userTime += other->userTime;
34133f908c4SFabian Schuiki       into->mergeChildren(std::move(other->children));
34233f908c4SFabian Schuiki       into->mergeChildren(std::move(other->asyncChildren));
34333f908c4SFabian Schuiki       other.reset();
34433f908c4SFabian Schuiki     }
34533f908c4SFabian Schuiki   }
34633f908c4SFabian Schuiki 
34733f908c4SFabian Schuiki   /// Dump a human-readable tree representation of the timer and its children.
34833f908c4SFabian Schuiki   /// This is useful for debugging the timing mechanisms and structure of the
34933f908c4SFabian Schuiki   /// timers.
dump(raw_ostream & os,unsigned indent=0,unsigned markThreadId=0)35033f908c4SFabian Schuiki   void dump(raw_ostream &os, unsigned indent = 0, unsigned markThreadId = 0) {
35133f908c4SFabian Schuiki     auto time = getTimeRecord();
35233f908c4SFabian Schuiki     os << std::string(indent * 2, ' ') << name << " [" << threadId << "]"
35333f908c4SFabian Schuiki        << llvm::format("  %7.4f / %7.4f", time.user, time.wall);
35433f908c4SFabian Schuiki     if (threadId != markThreadId && markThreadId != 0)
35533f908c4SFabian Schuiki       os << " (*)";
35633f908c4SFabian Schuiki     os << "\n";
35733f908c4SFabian Schuiki     for (auto &child : children)
35833f908c4SFabian Schuiki       child.second->dump(os, indent + 1, threadId);
35933f908c4SFabian Schuiki     for (auto &thread : asyncChildren)
36033f908c4SFabian Schuiki       for (auto &child : thread.second)
36133f908c4SFabian Schuiki         child.second->dump(os, indent + 1, threadId);
36233f908c4SFabian Schuiki   }
36333f908c4SFabian Schuiki 
36433f908c4SFabian Schuiki   /// Returns the time for this timer in seconds.
getTimeRecord()36533f908c4SFabian Schuiki   TimeRecord getTimeRecord() {
36633f908c4SFabian Schuiki     return TimeRecord(
36733f908c4SFabian Schuiki         std::chrono::duration_cast<std::chrono::duration<double>>(wallTime)
36833f908c4SFabian Schuiki             .count(),
36933f908c4SFabian Schuiki         std::chrono::duration_cast<std::chrono::duration<double>>(userTime)
37033f908c4SFabian Schuiki             .count());
37133f908c4SFabian Schuiki   }
37233f908c4SFabian Schuiki 
37333f908c4SFabian Schuiki   /// Print the timing result in list mode.
printAsList(TimeRecord total)374362aa434SHsiangkai Wang   void printAsList(TimeRecord total) {
37533f908c4SFabian Schuiki     // Flatten the leaf timers in the tree and merge them by name.
37633f908c4SFabian Schuiki     llvm::StringMap<TimeRecord> mergedTimers;
37733f908c4SFabian Schuiki     std::function<void(TimerImpl *)> addTimer = [&](TimerImpl *timer) {
37833f908c4SFabian Schuiki       mergedTimers[timer->name] += timer->getTimeRecord();
37933f908c4SFabian Schuiki       for (auto &children : timer->children)
38033f908c4SFabian Schuiki         addTimer(children.second.get());
38133f908c4SFabian Schuiki     };
38233f908c4SFabian Schuiki     addTimer(this);
38333f908c4SFabian Schuiki 
38433f908c4SFabian Schuiki     // Sort the timing information by wall time.
38533f908c4SFabian Schuiki     std::vector<std::pair<StringRef, TimeRecord>> timerNameAndTime;
38633f908c4SFabian Schuiki     for (auto &it : mergedTimers)
38733f908c4SFabian Schuiki       timerNameAndTime.emplace_back(it.first(), it.second);
38833f908c4SFabian Schuiki     llvm::array_pod_sort(timerNameAndTime.begin(), timerNameAndTime.end(),
38933f908c4SFabian Schuiki                          [](const std::pair<StringRef, TimeRecord> *lhs,
39033f908c4SFabian Schuiki                             const std::pair<StringRef, TimeRecord> *rhs) {
39133f908c4SFabian Schuiki                            return llvm::array_pod_sort_comparator<double>(
39233f908c4SFabian Schuiki                                &rhs->second.wall, &lhs->second.wall);
39333f908c4SFabian Schuiki                          });
39433f908c4SFabian Schuiki 
39533f908c4SFabian Schuiki     // Print the timing information sequentially.
39633f908c4SFabian Schuiki     for (auto &timeData : timerNameAndTime)
397362aa434SHsiangkai Wang       output->printListEntry(timeData.first, timeData.second, total);
39833f908c4SFabian Schuiki   }
39933f908c4SFabian Schuiki 
40033f908c4SFabian Schuiki   /// Print the timing result in tree mode.
printAsTree(TimeRecord total,unsigned indent=0)401362aa434SHsiangkai Wang   void printAsTree(TimeRecord total, unsigned indent = 0) {
40233f908c4SFabian Schuiki     unsigned childIndent = indent;
40333f908c4SFabian Schuiki     if (!hidden) {
404362aa434SHsiangkai Wang       output->printTreeEntry(indent, name, getTimeRecord(), total);
40533f908c4SFabian Schuiki       childIndent += 2;
40633f908c4SFabian Schuiki     }
40733f908c4SFabian Schuiki     for (auto &child : children) {
408362aa434SHsiangkai Wang       child.second->printAsTree(total, childIndent);
409362aa434SHsiangkai Wang     }
410362aa434SHsiangkai Wang     if (!hidden) {
411362aa434SHsiangkai Wang       output->printTreeEntryEnd(indent);
41233f908c4SFabian Schuiki     }
41333f908c4SFabian Schuiki   }
41433f908c4SFabian Schuiki 
41533f908c4SFabian Schuiki   /// Print the current timing information.
print(DisplayMode displayMode)416362aa434SHsiangkai Wang   void print(DisplayMode displayMode) {
41733f908c4SFabian Schuiki     // Print the banner.
41833f908c4SFabian Schuiki     auto total = getTimeRecord();
419362aa434SHsiangkai Wang     output->printHeader(total);
42033f908c4SFabian Schuiki 
42133f908c4SFabian Schuiki     // Defer to a specialized printer for each display mode.
42233f908c4SFabian Schuiki     switch (displayMode) {
42333f908c4SFabian Schuiki     case DisplayMode::List:
424362aa434SHsiangkai Wang       printAsList(total);
42533f908c4SFabian Schuiki       break;
42633f908c4SFabian Schuiki     case DisplayMode::Tree:
427362aa434SHsiangkai Wang       printAsTree(total);
42833f908c4SFabian Schuiki       break;
42933f908c4SFabian Schuiki     }
43033f908c4SFabian Schuiki 
43133f908c4SFabian Schuiki     // Print the top-level time not accounted for by child timers, and the
43233f908c4SFabian Schuiki     // total.
43333f908c4SFabian Schuiki     auto rest = total;
43433f908c4SFabian Schuiki     for (auto &child : children)
43533f908c4SFabian Schuiki       rest -= child.second->getTimeRecord();
436362aa434SHsiangkai Wang     output->printListEntry("Rest", rest, total);
437362aa434SHsiangkai Wang     output->printListEntry("Total", total, total, /*lastEntry=*/true);
438362aa434SHsiangkai Wang     output->printFooter();
43933f908c4SFabian Schuiki   }
44033f908c4SFabian Schuiki 
44133f908c4SFabian Schuiki   /// The last time instant at which the timer was started.
4421dd37975Srdzhabarov   std::chrono::time_point<std::chrono::steady_clock> startTime;
44333f908c4SFabian Schuiki 
44433f908c4SFabian Schuiki   /// Accumulated wall time. If multiple threads of execution are merged into
44533f908c4SFabian Schuiki   /// this timer, the wall time will hold the maximum wall time of each thread
44633f908c4SFabian Schuiki   /// of execution.
44733f908c4SFabian Schuiki   std::chrono::nanoseconds wallTime = std::chrono::nanoseconds(0);
44833f908c4SFabian Schuiki 
44933f908c4SFabian Schuiki   /// Accumulated user time. If multiple threads of execution are merged into
45033f908c4SFabian Schuiki   /// this timer, each thread's user time is added here.
45133f908c4SFabian Schuiki   std::chrono::nanoseconds userTime = std::chrono::nanoseconds(0);
45233f908c4SFabian Schuiki 
45333f908c4SFabian Schuiki   /// The thread on which this timer is running.
45433f908c4SFabian Schuiki   uint64_t threadId;
45533f908c4SFabian Schuiki 
45633f908c4SFabian Schuiki   /// A descriptive name for this timer.
45733f908c4SFabian Schuiki   std::string name;
45833f908c4SFabian Schuiki 
45933f908c4SFabian Schuiki   /// Whether to omit this timer from reports and directly show its children.
46033f908c4SFabian Schuiki   bool hidden = false;
46133f908c4SFabian Schuiki 
46233f908c4SFabian Schuiki   /// Child timers on the same thread the timer itself. We keep at most one
46333f908c4SFabian Schuiki   /// timer per unique identifier.
46433f908c4SFabian Schuiki   ChildrenMap children;
46533f908c4SFabian Schuiki 
46633f908c4SFabian Schuiki   /// Child timers on other threads. We keep at most one timer per unique
46733f908c4SFabian Schuiki   /// identifier.
46833f908c4SFabian Schuiki   AsyncChildrenMap asyncChildren;
46933f908c4SFabian Schuiki 
47033f908c4SFabian Schuiki   /// Mutex for the async children.
47133f908c4SFabian Schuiki   std::mutex asyncMutex;
472362aa434SHsiangkai Wang 
473362aa434SHsiangkai Wang   std::unique_ptr<OutputStrategy> &output;
47433f908c4SFabian Schuiki };
47533f908c4SFabian Schuiki 
47633f908c4SFabian Schuiki } // namespace
47733f908c4SFabian Schuiki 
47833f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
47933f908c4SFabian Schuiki // DefaultTimingManager
48033f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
48133f908c4SFabian Schuiki 
48233f908c4SFabian Schuiki namespace mlir {
48333f908c4SFabian Schuiki namespace detail {
48433f908c4SFabian Schuiki 
48533f908c4SFabian Schuiki /// Implementation details of the `DefaultTimingManager`.
48633f908c4SFabian Schuiki class DefaultTimingManagerImpl {
48733f908c4SFabian Schuiki public:
48833f908c4SFabian Schuiki   /// Whether we should do our work or not.
48933f908c4SFabian Schuiki   bool enabled = false;
49033f908c4SFabian Schuiki 
49133f908c4SFabian Schuiki   /// The configured display mode.
49233f908c4SFabian Schuiki   DisplayMode displayMode = DisplayMode::Tree;
49333f908c4SFabian Schuiki 
49433f908c4SFabian Schuiki   /// The root timer.
49533f908c4SFabian Schuiki   std::unique_ptr<TimerImpl> rootTimer;
49633f908c4SFabian Schuiki };
49733f908c4SFabian Schuiki 
49833f908c4SFabian Schuiki } // namespace detail
49933f908c4SFabian Schuiki } // namespace mlir
50033f908c4SFabian Schuiki 
DefaultTimingManager()50133f908c4SFabian Schuiki DefaultTimingManager::DefaultTimingManager()
502*d5ec49ffSChenguang Wang     : impl(std::make_unique<DefaultTimingManagerImpl>()),
503*d5ec49ffSChenguang Wang       out(std::make_unique<OutputTextStrategy>(llvm::errs())) {
50433f908c4SFabian Schuiki   clear(); // initializes the root timer
50533f908c4SFabian Schuiki }
50633f908c4SFabian Schuiki 
~DefaultTimingManager()50733f908c4SFabian Schuiki DefaultTimingManager::~DefaultTimingManager() { print(); }
50833f908c4SFabian Schuiki 
50933f908c4SFabian Schuiki /// Enable or disable execution time sampling.
setEnabled(bool enabled)51033f908c4SFabian Schuiki void DefaultTimingManager::setEnabled(bool enabled) { impl->enabled = enabled; }
51133f908c4SFabian Schuiki 
51233f908c4SFabian Schuiki /// Return whether execution time sampling is enabled.
isEnabled() const51333f908c4SFabian Schuiki bool DefaultTimingManager::isEnabled() const { return impl->enabled; }
51433f908c4SFabian Schuiki 
51533f908c4SFabian Schuiki /// Change the display mode.
setDisplayMode(DisplayMode displayMode)51633f908c4SFabian Schuiki void DefaultTimingManager::setDisplayMode(DisplayMode displayMode) {
51733f908c4SFabian Schuiki   impl->displayMode = displayMode;
51833f908c4SFabian Schuiki }
51933f908c4SFabian Schuiki 
52033f908c4SFabian Schuiki /// Return the current display mode;
getDisplayMode() const52133f908c4SFabian Schuiki DefaultTimingManager::DisplayMode DefaultTimingManager::getDisplayMode() const {
52233f908c4SFabian Schuiki   return impl->displayMode;
52333f908c4SFabian Schuiki }
52433f908c4SFabian Schuiki 
52533f908c4SFabian Schuiki /// Change the stream where the output will be printed to.
setOutput(std::unique_ptr<OutputStrategy> output)526362aa434SHsiangkai Wang void DefaultTimingManager::setOutput(std::unique_ptr<OutputStrategy> output) {
527362aa434SHsiangkai Wang   out = std::move(output);
52833f908c4SFabian Schuiki }
52933f908c4SFabian Schuiki 
53033f908c4SFabian Schuiki /// Print and clear the timing results.
print()53133f908c4SFabian Schuiki void DefaultTimingManager::print() {
53233f908c4SFabian Schuiki   if (impl->enabled) {
53333f908c4SFabian Schuiki     impl->rootTimer->finalize();
534362aa434SHsiangkai Wang     impl->rootTimer->print(impl->displayMode);
53533f908c4SFabian Schuiki   }
53633f908c4SFabian Schuiki   clear();
53733f908c4SFabian Schuiki }
53833f908c4SFabian Schuiki 
53933f908c4SFabian Schuiki /// Clear the timing results.
clear()54033f908c4SFabian Schuiki void DefaultTimingManager::clear() {
541362aa434SHsiangkai Wang   impl->rootTimer = std::make_unique<TimerImpl>("root", out);
54233f908c4SFabian Schuiki   impl->rootTimer->hidden = true;
54333f908c4SFabian Schuiki }
54433f908c4SFabian Schuiki 
54533f908c4SFabian Schuiki /// Debug print the timer data structures to an output stream.
dumpTimers(raw_ostream & os)54633f908c4SFabian Schuiki void DefaultTimingManager::dumpTimers(raw_ostream &os) {
54733f908c4SFabian Schuiki   impl->rootTimer->dump(os);
54833f908c4SFabian Schuiki }
54933f908c4SFabian Schuiki 
55033f908c4SFabian Schuiki /// Debug print the timers as a list.
dumpAsList(raw_ostream & os)55133f908c4SFabian Schuiki void DefaultTimingManager::dumpAsList(raw_ostream &os) {
55233f908c4SFabian Schuiki   impl->rootTimer->finalize();
553362aa434SHsiangkai Wang   impl->rootTimer->print(DisplayMode::List);
55433f908c4SFabian Schuiki }
55533f908c4SFabian Schuiki 
55633f908c4SFabian Schuiki /// Debug print the timers as a tree.
dumpAsTree(raw_ostream & os)55733f908c4SFabian Schuiki void DefaultTimingManager::dumpAsTree(raw_ostream &os) {
55833f908c4SFabian Schuiki   impl->rootTimer->finalize();
559362aa434SHsiangkai Wang   impl->rootTimer->print(DisplayMode::Tree);
56033f908c4SFabian Schuiki }
56133f908c4SFabian Schuiki 
rootTimer()5620a81ace0SKazu Hirata std::optional<void *> DefaultTimingManager::rootTimer() {
56333f908c4SFabian Schuiki   if (impl->enabled)
56433f908c4SFabian Schuiki     return impl->rootTimer.get();
5651a36588eSKazu Hirata   return std::nullopt;
56633f908c4SFabian Schuiki }
56733f908c4SFabian Schuiki 
startTimer(void * handle)56833f908c4SFabian Schuiki void DefaultTimingManager::startTimer(void *handle) {
56933f908c4SFabian Schuiki   static_cast<TimerImpl *>(handle)->start();
57033f908c4SFabian Schuiki }
57133f908c4SFabian Schuiki 
stopTimer(void * handle)57233f908c4SFabian Schuiki void DefaultTimingManager::stopTimer(void *handle) {
57333f908c4SFabian Schuiki   static_cast<TimerImpl *>(handle)->stop();
57433f908c4SFabian Schuiki }
57533f908c4SFabian Schuiki 
nestTimer(void * handle,const void * id,function_ref<std::string ()> nameBuilder)57633f908c4SFabian Schuiki void *DefaultTimingManager::nestTimer(void *handle, const void *id,
57733f908c4SFabian Schuiki                                       function_ref<std::string()> nameBuilder) {
578337c937dSMehdi Amini   return static_cast<TimerImpl *>(handle)->nest(id, nameBuilder);
57933f908c4SFabian Schuiki }
58033f908c4SFabian Schuiki 
hideTimer(void * handle)58133f908c4SFabian Schuiki void DefaultTimingManager::hideTimer(void *handle) {
58233f908c4SFabian Schuiki   static_cast<TimerImpl *>(handle)->hidden = true;
58333f908c4SFabian Schuiki }
58433f908c4SFabian Schuiki 
58533f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
58633f908c4SFabian Schuiki // DefaultTimingManager Command Line Options
58733f908c4SFabian Schuiki //===----------------------------------------------------------------------===//
58833f908c4SFabian Schuiki 
58933f908c4SFabian Schuiki namespace {
59033f908c4SFabian Schuiki struct DefaultTimingManagerOptions {
59133f908c4SFabian Schuiki   llvm::cl::opt<bool> timing{"mlir-timing",
59233f908c4SFabian Schuiki                              llvm::cl::desc("Display execution times"),
59333f908c4SFabian Schuiki                              llvm::cl::init(false)};
59433f908c4SFabian Schuiki   llvm::cl::opt<DisplayMode> displayMode{
59533f908c4SFabian Schuiki       "mlir-timing-display", llvm::cl::desc("Display method for timing data"),
59633f908c4SFabian Schuiki       llvm::cl::init(DisplayMode::Tree),
59733f908c4SFabian Schuiki       llvm::cl::values(
59833f908c4SFabian Schuiki           clEnumValN(DisplayMode::List, "list",
59933f908c4SFabian Schuiki                      "display the results in a list sorted by total time"),
60033f908c4SFabian Schuiki           clEnumValN(DisplayMode::Tree, "tree",
60133f908c4SFabian Schuiki                      "display the results ina with a nested tree view"))};
602362aa434SHsiangkai Wang   llvm::cl::opt<OutputFormat> outputFormat{
603362aa434SHsiangkai Wang       "mlir-output-format", llvm::cl::desc("Output format for timing data"),
604362aa434SHsiangkai Wang       llvm::cl::init(OutputFormat::Text),
605362aa434SHsiangkai Wang       llvm::cl::values(clEnumValN(OutputFormat::Text, "text",
606362aa434SHsiangkai Wang                                   "display the results in text format"),
607362aa434SHsiangkai Wang                        clEnumValN(OutputFormat::Json, "json",
608362aa434SHsiangkai Wang                                   "display the results in JSON format"))};
60933f908c4SFabian Schuiki };
610be0a7e9fSMehdi Amini } // namespace
61133f908c4SFabian Schuiki 
61233f908c4SFabian Schuiki static llvm::ManagedStatic<DefaultTimingManagerOptions> options;
61333f908c4SFabian Schuiki 
registerDefaultTimingManagerCLOptions()61433f908c4SFabian Schuiki void mlir::registerDefaultTimingManagerCLOptions() {
61533f908c4SFabian Schuiki   // Make sure that the options struct has been constructed.
61633f908c4SFabian Schuiki   *options;
61733f908c4SFabian Schuiki }
61833f908c4SFabian Schuiki 
applyDefaultTimingManagerCLOptions(DefaultTimingManager & tm)61933f908c4SFabian Schuiki void mlir::applyDefaultTimingManagerCLOptions(DefaultTimingManager &tm) {
62033f908c4SFabian Schuiki   if (!options.isConstructed())
62133f908c4SFabian Schuiki     return;
62233f908c4SFabian Schuiki   tm.setEnabled(options->timing);
62333f908c4SFabian Schuiki   tm.setDisplayMode(options->displayMode);
624362aa434SHsiangkai Wang 
625362aa434SHsiangkai Wang   std::unique_ptr<OutputStrategy> printer;
626362aa434SHsiangkai Wang   if (options->outputFormat == OutputFormat::Text)
627362aa434SHsiangkai Wang     printer = std::make_unique<OutputTextStrategy>(llvm::errs());
628362aa434SHsiangkai Wang   else if (options->outputFormat == OutputFormat::Json)
629362aa434SHsiangkai Wang     printer = std::make_unique<OutputJsonStrategy>(llvm::errs());
630362aa434SHsiangkai Wang   tm.setOutput(std::move(printer));
63133f908c4SFabian Schuiki }
632