forked from OSchip/llvm-project
241 lines
7.8 KiB
C++
241 lines
7.8 KiB
C++
//===--------------------- TimelineView.cpp ---------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/// \brief
|
|
///
|
|
/// This file implements the TimelineView interface.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TimelineView.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace mca {
|
|
|
|
void TimelineView::initialize(unsigned MaxIterations) {
|
|
unsigned NumInstructions =
|
|
AsmSequence.getNumIterations() * AsmSequence.size();
|
|
if (!MaxIterations)
|
|
MaxIterations = DEFAULT_ITERATIONS;
|
|
unsigned NumEntries =
|
|
std::min(NumInstructions, MaxIterations * AsmSequence.size());
|
|
Timeline.resize(NumEntries);
|
|
TimelineViewEntry NullTVEntry = {0, 0, 0, 0, 0};
|
|
std::fill(Timeline.begin(), Timeline.end(), NullTVEntry);
|
|
|
|
WaitTime.resize(AsmSequence.size());
|
|
WaitTimeEntry NullWTEntry = {0, 0, 0, 0};
|
|
std::fill(WaitTime.begin(), WaitTime.end(), NullWTEntry);
|
|
}
|
|
|
|
void TimelineView::onInstructionEvent(const HWInstructionEvent &Event) {
|
|
const unsigned Index = Event.IR.getSourceIndex();
|
|
if (CurrentCycle >= MaxCycle || Index >= Timeline.size())
|
|
return;
|
|
switch (Event.Type) {
|
|
case HWInstructionEvent::Retired: {
|
|
TimelineViewEntry &TVEntry = Timeline[Index];
|
|
TVEntry.CycleRetired = CurrentCycle;
|
|
|
|
// Update the WaitTime entry which corresponds to this Index.
|
|
WaitTimeEntry &WTEntry = WaitTime[Index % AsmSequence.size()];
|
|
WTEntry.Executions++;
|
|
WTEntry.CyclesSpentInSchedulerQueue +=
|
|
TVEntry.CycleIssued - TVEntry.CycleDispatched;
|
|
assert(TVEntry.CycleDispatched <= TVEntry.CycleReady);
|
|
WTEntry.CyclesSpentInSQWhileReady +=
|
|
TVEntry.CycleIssued - TVEntry.CycleReady;
|
|
WTEntry.CyclesSpentAfterWBAndBeforeRetire +=
|
|
(TVEntry.CycleRetired - 1) - TVEntry.CycleExecuted;
|
|
break;
|
|
}
|
|
case HWInstructionEvent::Ready:
|
|
Timeline[Index].CycleReady = CurrentCycle;
|
|
break;
|
|
case HWInstructionEvent::Issued:
|
|
Timeline[Index].CycleIssued = CurrentCycle;
|
|
break;
|
|
case HWInstructionEvent::Executed:
|
|
Timeline[Index].CycleExecuted = CurrentCycle;
|
|
break;
|
|
case HWInstructionEvent::Dispatched:
|
|
Timeline[Index].CycleDispatched = CurrentCycle;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
LastCycle = std::max(LastCycle, CurrentCycle);
|
|
}
|
|
|
|
void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS,
|
|
const WaitTimeEntry &Entry,
|
|
unsigned SourceIndex) const {
|
|
OS << SourceIndex << '.';
|
|
OS.PadToColumn(7);
|
|
|
|
if (Entry.Executions == 0) {
|
|
OS << "- - - - ";
|
|
} else {
|
|
double AverageTime1, AverageTime2, AverageTime3;
|
|
unsigned Executions = Entry.Executions;
|
|
AverageTime1 = (double)Entry.CyclesSpentInSchedulerQueue / Executions;
|
|
AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / Executions;
|
|
AverageTime3 = (double)Entry.CyclesSpentAfterWBAndBeforeRetire / Executions;
|
|
|
|
OS << Executions;
|
|
OS.PadToColumn(13);
|
|
|
|
OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10);
|
|
OS.PadToColumn(20);
|
|
OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10);
|
|
OS.PadToColumn(27);
|
|
OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10);
|
|
OS.PadToColumn(34);
|
|
}
|
|
}
|
|
|
|
void TimelineView::printAverageWaitTimes(raw_ostream &OS) const {
|
|
if (WaitTime.empty())
|
|
return;
|
|
|
|
std::string Buffer;
|
|
raw_string_ostream TempStream(Buffer);
|
|
formatted_raw_ostream FOS(TempStream);
|
|
|
|
FOS << "\n\nAverage Wait times (based on the timeline view):\n"
|
|
<< "[0]: Executions\n"
|
|
<< "[1]: Average time spent waiting in a scheduler's queue\n"
|
|
<< "[2]: Average time spent waiting in a scheduler's queue while ready\n"
|
|
<< "[3]: Average time elapsed from WB until retire stage\n\n";
|
|
FOS << " [0] [1] [2] [3]\n";
|
|
|
|
// Use a different string stream for the instruction.
|
|
std::string Instruction;
|
|
raw_string_ostream InstrStream(Instruction);
|
|
|
|
for (unsigned I = 0, E = WaitTime.size(); I < E; ++I) {
|
|
printWaitTimeEntry(FOS, WaitTime[I], I);
|
|
// Append the instruction info at the end of the line.
|
|
const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
|
|
|
|
MCIP.printInst(&Inst, InstrStream, "", STI);
|
|
InstrStream.flush();
|
|
|
|
// Consume any tabs or spaces at the beginning of the string.
|
|
StringRef Str(Instruction);
|
|
Str = Str.ltrim();
|
|
FOS << " " << Str << '\n';
|
|
FOS.flush();
|
|
Instruction = "";
|
|
|
|
OS << Buffer;
|
|
Buffer = "";
|
|
}
|
|
}
|
|
|
|
void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS,
|
|
const TimelineViewEntry &Entry,
|
|
unsigned Iteration,
|
|
unsigned SourceIndex) const {
|
|
if (Iteration == 0 && SourceIndex == 0)
|
|
OS << '\n';
|
|
OS << '[' << Iteration << ',' << SourceIndex << ']';
|
|
OS.PadToColumn(10);
|
|
for (unsigned I = 0, E = Entry.CycleDispatched; I < E; ++I)
|
|
OS << ((I % 5 == 0) ? '.' : ' ');
|
|
OS << TimelineView::DisplayChar::Dispatched;
|
|
if (Entry.CycleDispatched != Entry.CycleExecuted) {
|
|
// Zero latency instructions have the same value for CycleDispatched,
|
|
// CycleIssued and CycleExecuted.
|
|
for (unsigned I = Entry.CycleDispatched + 1, E = Entry.CycleIssued; I < E;
|
|
++I)
|
|
OS << TimelineView::DisplayChar::Waiting;
|
|
if (Entry.CycleIssued == Entry.CycleExecuted)
|
|
OS << TimelineView::DisplayChar::DisplayChar::Executed;
|
|
else {
|
|
if (Entry.CycleDispatched != Entry.CycleIssued)
|
|
OS << TimelineView::DisplayChar::Executing;
|
|
for (unsigned I = Entry.CycleIssued + 1, E = Entry.CycleExecuted; I < E;
|
|
++I)
|
|
OS << TimelineView::DisplayChar::Executing;
|
|
OS << TimelineView::DisplayChar::Executed;
|
|
}
|
|
}
|
|
|
|
for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I)
|
|
OS << TimelineView::DisplayChar::RetireLag;
|
|
OS << TimelineView::DisplayChar::Retired;
|
|
|
|
// Skip other columns.
|
|
for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I)
|
|
OS << ((I % 5 == 0 || I == LastCycle) ? '.' : ' ');
|
|
}
|
|
|
|
static void printTimelineHeader(formatted_raw_ostream &OS, unsigned Cycles) {
|
|
OS << "\n\nTimeline view:\n";
|
|
if (Cycles >= 10) {
|
|
OS.PadToColumn(10);
|
|
for (unsigned I = 0; I <= Cycles; ++I) {
|
|
if (((I / 10) & 1) == 0)
|
|
OS << ' ';
|
|
else
|
|
OS << I % 10;
|
|
}
|
|
OS << '\n';
|
|
}
|
|
|
|
OS << "Index";
|
|
OS.PadToColumn(10);
|
|
for (unsigned I = 0; I <= Cycles; ++I) {
|
|
if (((I / 10) & 1) == 0)
|
|
OS << I % 10;
|
|
else
|
|
OS << ' ';
|
|
}
|
|
OS << '\n';
|
|
}
|
|
|
|
void TimelineView::printTimeline(raw_ostream &OS) const {
|
|
std::string Buffer;
|
|
raw_string_ostream StringStream(Buffer);
|
|
formatted_raw_ostream FOS(StringStream);
|
|
|
|
printTimelineHeader(FOS, LastCycle);
|
|
FOS.flush();
|
|
OS << Buffer;
|
|
|
|
// Use a different string stream for the instruction.
|
|
std::string Instruction;
|
|
raw_string_ostream InstrStream(Instruction);
|
|
|
|
for (unsigned I = 0, E = Timeline.size(); I < E; ++I) {
|
|
Buffer = "";
|
|
const TimelineViewEntry &Entry = Timeline[I];
|
|
if (Entry.CycleRetired == 0)
|
|
return;
|
|
|
|
unsigned Iteration = I / AsmSequence.size();
|
|
unsigned SourceIndex = I % AsmSequence.size();
|
|
printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex);
|
|
// Append the instruction info at the end of the line.
|
|
const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
|
|
MCIP.printInst(&Inst, InstrStream, "", STI);
|
|
InstrStream.flush();
|
|
|
|
// Consume any tabs or spaces at the beginning of the string.
|
|
StringRef Str(Instruction);
|
|
Str = Str.ltrim();
|
|
FOS << " " << Str << '\n';
|
|
FOS.flush();
|
|
Instruction = "";
|
|
OS << Buffer;
|
|
}
|
|
}
|
|
} // namespace mca
|