//===- subzero/src/IceTimerTree.cpp - Pass timer defs ---------------------===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Defines the TimerTree class, which tracks flat and cumulative /// execution time collection of call chains. /// //===----------------------------------------------------------------------===// #include "IceTimerTree.h" #include "IceDefs.h" #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #endif // __clang__ #include "llvm/Support/Format.h" #include "llvm/Support/Timer.h" #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ namespace Ice { TimerStack::TimerStack(const std::string &Name) : Name(Name), FirstTimestamp(timestamp()), LastTimestamp(FirstTimestamp) { if (!BuildDefs::timers()) return; Nodes.resize(1); // Reserve Nodes[0] for the root node (sentinel). IDs.resize(TT__num); LeafTimes.resize(TT__num); LeafCounts.resize(TT__num); #define STR(s) #s #define X(tag) \ IDs[TT_##tag] = STR(tag); \ IDsIndex[STR(tag)] = TT_##tag; TIMERTREE_TABLE; #undef X #undef STR } // Returns the unique timer ID for the given Name, creating a new ID if needed. TimerIdT TimerStack::getTimerID(const std::string &Name) { if (!BuildDefs::timers()) return 0; if (IDsIndex.find(Name) == IDsIndex.end()) { IDsIndex[Name] = IDs.size(); IDs.push_back(Name); LeafTimes.push_back(decltype(LeafTimes)::value_type()); LeafCounts.push_back(decltype(LeafCounts)::value_type()); } return IDsIndex[Name]; } // Creates a mapping from TimerIdT (leaf) values in the Src timer stack into // TimerIdT values in this timer stack. Creates new entries in this timer stack // as needed. TimerStack::TranslationType TimerStack::translateIDsFrom(const TimerStack &Src) { size_t Size = Src.IDs.size(); TranslationType Mapping(Size); for (TimerIdT i = 0; i < Size; ++i) { Mapping[i] = getTimerID(Src.IDs[i]); } return Mapping; } // Merges two timer stacks, by combining and summing corresponding entries. // This timer stack is updated from Src. void TimerStack::mergeFrom(const TimerStack &Src) { if (!BuildDefs::timers()) return; TranslationType Mapping = translateIDsFrom(Src); TTindex SrcIndex = 0; for (const TimerTreeNode &SrcNode : Src.Nodes) { // The first node is reserved as a sentinel, so avoid it. if (SrcIndex > 0) { // Find the full path to the Src node, translated to path components // corresponding to this timer stack. PathType MyPath = Src.getPath(SrcIndex, Mapping); // Find a node in this timer stack corresponding to the given path, // creating new interior nodes as necessary. TTindex MyIndex = findPath(MyPath); Nodes[MyIndex].Time += SrcNode.Time; Nodes[MyIndex].UpdateCount += SrcNode.UpdateCount; } ++SrcIndex; } for (TimerIdT i = 0; i < Src.LeafTimes.size(); ++i) { LeafTimes[Mapping[i]] += Src.LeafTimes[i]; LeafCounts[Mapping[i]] += Src.LeafCounts[i]; } StateChangeCount += Src.StateChangeCount; } // Constructs a path consisting of the sequence of leaf values leading to a // given node, with the Mapping translation applied to the leaf values. The // path ends up being in "reverse" order, i.e. from leaf to root. TimerStack::PathType TimerStack::getPath(TTindex Index, const TranslationType &Mapping) const { PathType Path; while (Index) { Path.push_back(Mapping[Nodes[Index].Interior]); assert(Nodes[Index].Parent < Index); Index = Nodes[Index].Parent; } return Path; } // Given a parent node and a leaf ID, returns the index of the parent's child // ID, creating a new node for the child as necessary. TimerStack::TTindex TimerStack::getChildIndex(TimerStack::TTindex Parent, TimerIdT ID) { if (Nodes[Parent].Children.size() <= ID) Nodes[Parent].Children.resize(ID + 1); if (Nodes[Parent].Children[ID] == 0) { TTindex Size = Nodes.size(); Nodes[Parent].Children[ID] = Size; Nodes.resize(Size + 1); Nodes[Size].Parent = Parent; Nodes[Size].Interior = ID; } return Nodes[Parent].Children[ID]; } // Finds a node in the timer stack corresponding to the given path, creating // new interior nodes as necessary. TimerStack::TTindex TimerStack::findPath(const PathType &Path) { TTindex CurIndex = 0; // The path is in reverse order (leaf to root), so it needs to be followed in // reverse. for (TTindex Index : reverse_range(Path)) { CurIndex = getChildIndex(CurIndex, Index); } assert(CurIndex); // shouldn't be the sentinel node return CurIndex; } // Pushes a new marker onto the timer stack. void TimerStack::push(TimerIdT ID) { if (!BuildDefs::timers()) return; constexpr bool UpdateCounts = false; update(UpdateCounts); StackTop = getChildIndex(StackTop, ID); assert(StackTop); } // Pops the top marker from the timer stack. Validates via assert() that the // expected marker is popped. void TimerStack::pop(TimerIdT ID) { if (!BuildDefs::timers()) return; constexpr bool UpdateCounts = true; update(UpdateCounts); assert(StackTop); assert(Nodes[StackTop].Parent < StackTop); // Verify that the expected ID is being popped. assert(Nodes[StackTop].Interior == ID); (void)ID; // Verify that the parent's child points to the current stack top. assert(Nodes[Nodes[StackTop].Parent].Children[ID] == StackTop); StackTop = Nodes[StackTop].Parent; } // At a state change (e.g. push or pop), updates the flat and cumulative // timings for everything on the timer stack. void TimerStack::update(bool UpdateCounts) { if (!BuildDefs::timers()) return; ++StateChangeCount; // Whenever the stack is about to change, we grab the time delta since the // last change and add it to all active cumulative elements and to the flat // element for the top of the stack. double Current = timestamp(); double Delta = Current - LastTimestamp; if (StackTop) { TimerIdT Leaf = Nodes[StackTop].Interior; if (Leaf >= LeafTimes.size()) { LeafTimes.resize(Leaf + 1); LeafCounts.resize(Leaf + 1); } LeafTimes[Leaf] += Delta; if (UpdateCounts) ++LeafCounts[Leaf]; } TTindex Prefix = StackTop; while (Prefix) { Nodes[Prefix].Time += Delta; // Only update a leaf node count, not the internal node counts. if (UpdateCounts && Prefix == StackTop) ++Nodes[Prefix].UpdateCount; TTindex Next = Nodes[Prefix].Parent; assert(Next < Prefix); Prefix = Next; } // Capture the next timestamp *after* the updates are finished. This // minimizes how much the timer can perturb the reported timing. The numbers // may not sum to 100%, and the missing amount is indicative of the overhead // of timing. LastTimestamp = timestamp(); } void TimerStack::reset() { if (!BuildDefs::timers()) return; StateChangeCount = 0; FirstTimestamp = LastTimestamp = timestamp(); LeafTimes.assign(LeafTimes.size(), 0); LeafCounts.assign(LeafCounts.size(), 0); for (TimerTreeNode &Node : Nodes) { Node.Time = 0; Node.UpdateCount = 0; } } namespace { using DumpMapType = std::multimap<double, std::string>; // Dump the Map items in reverse order of their time contribution. If // AddPercents is true (i.e. for printing "flat times"), it also prints a // cumulative percentage column, and recalculates TotalTime as the sum of all // the individual times so that cumulative percentage adds up to 100%. void dumpHelper(Ostream &Str, const DumpMapType &Map, double TotalTime, bool AddPercents) { if (!BuildDefs::timers()) return; if (AddPercents) { // Recalculate TotalTime as the sum of the individual times. This is // because the individual times generally add up to less than 100% because // of timer overhead. TotalTime = 0; for (const auto &I : Map) { TotalTime += I.first; } } double Sum = 0; for (const auto &I : reverse_range(Map)) { Sum += I.first; if (AddPercents) { Str << llvm::format(" %10.6f %4.1f%% %5.1f%% ", I.first, I.first * 100 / TotalTime, Sum * 100 / TotalTime) << I.second << "\n"; } else { Str << llvm::format(" %10.6f %4.1f%% ", I.first, I.first * 100 / TotalTime) << I.second << "\n"; } } } } // end of anonymous namespace void TimerStack::dump(Ostream &Str, bool DumpCumulative) { if (!BuildDefs::timers()) return; constexpr bool UpdateCounts = true; update(UpdateCounts); double TotalTime = LastTimestamp - FirstTimestamp; assert(TotalTime); char PrefixStr[30]; if (DumpCumulative) { Str << Name << " - Cumulative times:\n" " Seconds Pct EventCnt TimerPath\n"; DumpMapType CumulativeMap; for (TTindex i = 1; i < Nodes.size(); ++i) { TTindex Prefix = i; std::string Suffix = ""; while (Prefix) { if (Suffix.empty()) Suffix = IDs[Nodes[Prefix].Interior]; else Suffix = IDs[Nodes[Prefix].Interior] + "." + Suffix; assert(Nodes[Prefix].Parent < Prefix); Prefix = Nodes[Prefix].Parent; } snprintf(PrefixStr, llvm::array_lengthof(PrefixStr), "%9zu ", Nodes[i].UpdateCount); CumulativeMap.insert(std::make_pair(Nodes[i].Time, PrefixStr + Suffix)); } constexpr bool NoAddPercents = false; dumpHelper(Str, CumulativeMap, TotalTime, NoAddPercents); } Str << Name << " - Flat times:\n" " Seconds Pct CumPct EventCnt TimerName\n"; DumpMapType FlatMap; for (TimerIdT i = 0; i < LeafTimes.size(); ++i) { if (LeafCounts[i]) { snprintf(PrefixStr, llvm::array_lengthof(PrefixStr), "%9zu ", LeafCounts[i]); FlatMap.insert(std::make_pair(LeafTimes[i], PrefixStr + IDs[i])); } } constexpr bool AddPercents = true; dumpHelper(Str, FlatMap, TotalTime, AddPercents); Str << "Number of timer updates: " << StateChangeCount << "\n"; } double TimerStack::timestamp() { // TODO: Implement in terms of std::chrono for C++11. return llvm::TimeRecord::getCurrentTime(false).getWallTime(); } } // end of namespace Ice