llvm-project/llvm/tools/llvm-mca/TimelineView.cpp
Matt Davis 21a8d32307 [llvm-mca] Avoid exposing index values in the MCA interfaces.
Summary:
This patch eliminates many places where we originally needed to  pass index
values to represent an instruction.  The index is still used as a key, in various parts of 
MCA.  I'm  not comfortable eliminating the index just yet.    By burying the index in
the instruction, we can avoid exposing that value in many places.

Eventually, we should consider removing the Instructions list in the Backend 
all together,   it's only used to hold and reclaim the memory for the allocated 
Instruction instances.  Instead we could pass around a smart pointer.  But that's
a separate discussion/patch.

Reviewers: andreadb, courbet, RKSimon

Reviewed By: andreadb

Subscribers: javed.absar, tschuett, gbedwell, llvm-commits

Differential Revision: https://reviews.llvm.org/D46367

llvm-svn: 331660
2018-05-07 18:29:15 +00:00

231 lines
7.4 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);
}
static void printAverageTime(raw_string_ostream &OS, double AverageTime) {
// Round to the nearest tenth.
OS << format("%.1f", floor((AverageTime * 10) + 0.5)/10);
if (AverageTime < 10.0)
OS << " ";
else if (AverageTime < 100.0)
OS << " ";
else
OS << " ";
}
void TimelineView::printWaitTimeEntry(raw_string_ostream &OS,
const WaitTimeEntry &Entry,
unsigned SourceIndex) const {
OS << SourceIndex << '.';
if (SourceIndex < 10)
OS << " ";
else if (SourceIndex < 100)
OS << " ";
else if (SourceIndex < 1000)
OS << " ";
else
OS << ' ';
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;
if (Executions < 10)
OS << ' ' << Executions << " ";
else if (Executions < 100)
OS << ' ' << Executions << " ";
else
OS << Executions << " ";
printAverageTime(OS, AverageTime1);
printAverageTime(OS, AverageTime2);
printAverageTime(OS, AverageTime3);
}
}
void TimelineView::printAverageWaitTimes(raw_ostream &OS) const {
if (WaitTime.empty())
return;
std::string Buffer;
raw_string_ostream TempStream(Buffer);
TempStream
<< "\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";
TempStream << " [0] [1] [2] [3]\n";
for (unsigned I = 0, E = WaitTime.size(); I < E; ++I) {
printWaitTimeEntry(TempStream, WaitTime[I], I);
// Append the instruction info at the end of the line.
const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
MCIP.printInst(&Inst, TempStream, "", STI);
TempStream << '\n';
TempStream.flush();
OS << Buffer;
Buffer = "";
}
}
void TimelineView::printTimelineViewEntry(raw_string_ostream &OS,
const TimelineViewEntry &Entry,
unsigned Iteration,
unsigned SourceIndex) const {
if (Iteration == 0 && SourceIndex == 0)
OS << '\n';
OS << '[' << Iteration << ',' << SourceIndex << "]\t";
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(raw_string_ostream &OS, unsigned Cycles) {
OS << "\n\nTimeline view:\n";
OS << " \t";
for (unsigned I = 0; I <= Cycles; ++I) {
if (((I / 10) & 1) == 0)
OS << ' ';
else
OS << I % 10;
}
OS << "\nIndex\t";
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 TempStream(Buffer);
printTimelineHeader(TempStream, LastCycle);
TempStream.flush();
OS << Buffer;
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(TempStream, Entry, Iteration, SourceIndex);
// Append the instruction info at the end of the line.
const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
MCIP.printInst(&Inst, TempStream, "", STI);
TempStream << '\n';
TempStream.flush();
OS << Buffer;
}
}
} // namespace mca