317 lines
10 KiB
C++
317 lines
10 KiB
C++
//===- 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
|