| //===--------------------- 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::onEvent(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 |