TimelineView.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. //===--------------------- TimelineView.cpp ---------------------*- C++ -*-===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. /// \brief
  10. ///
  11. /// This file implements the TimelineView interface.
  12. ///
  13. //===----------------------------------------------------------------------===//
  14. #include "Views/TimelineView.h"
  15. using namespace llvm;
  16. namespace mca {
  17. TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer,
  18. const SourceMgr &S, unsigned MaxIterations,
  19. unsigned Cycles)
  20. : STI(sti), MCIP(Printer), AsmSequence(S), CurrentCycle(0),
  21. MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0), WaitTime(S.size()),
  22. UsedBuffer(S.size()) {
  23. unsigned NumInstructions = AsmSequence.size();
  24. if (!MaxIterations)
  25. MaxIterations = DEFAULT_ITERATIONS;
  26. NumInstructions *= std::min(MaxIterations, AsmSequence.getNumIterations());
  27. Timeline.resize(NumInstructions);
  28. TimelineViewEntry InvalidTVEntry = {-1, 0, 0, 0};
  29. std::fill(Timeline.begin(), Timeline.end(), InvalidTVEntry);
  30. WaitTimeEntry NullWTEntry = {0, 0, 0};
  31. std::fill(WaitTime.begin(), WaitTime.end(), NullWTEntry);
  32. std::pair<unsigned, int> NullUsedBufferEntry = {/* Invalid resource ID*/ 0,
  33. /* unknown buffer size */ -1};
  34. std::fill(UsedBuffer.begin(), UsedBuffer.end(), NullUsedBufferEntry);
  35. }
  36. void TimelineView::onReservedBuffers(const InstRef &IR,
  37. ArrayRef<unsigned> Buffers) {
  38. if (IR.getSourceIndex() >= AsmSequence.size())
  39. return;
  40. const MCSchedModel &SM = STI.getSchedModel();
  41. std::pair<unsigned, int> BufferInfo = {0, -1};
  42. for (const unsigned Buffer : Buffers) {
  43. const MCProcResourceDesc &MCDesc = *SM.getProcResource(Buffer);
  44. if (!BufferInfo.first || BufferInfo.second > MCDesc.BufferSize) {
  45. BufferInfo.first = Buffer;
  46. BufferInfo.second = MCDesc.BufferSize;
  47. }
  48. }
  49. UsedBuffer[IR.getSourceIndex()] = BufferInfo;
  50. }
  51. void TimelineView::onEvent(const HWInstructionEvent &Event) {
  52. const unsigned Index = Event.IR.getSourceIndex();
  53. if (Index >= Timeline.size())
  54. return;
  55. switch (Event.Type) {
  56. case HWInstructionEvent::Retired: {
  57. TimelineViewEntry &TVEntry = Timeline[Index];
  58. if (CurrentCycle < MaxCycle)
  59. TVEntry.CycleRetired = CurrentCycle;
  60. // Update the WaitTime entry which corresponds to this Index.
  61. assert(TVEntry.CycleDispatched >= 0 && "Invalid TVEntry found!");
  62. unsigned CycleDispatched = static_cast<unsigned>(TVEntry.CycleDispatched);
  63. WaitTimeEntry &WTEntry = WaitTime[Index % AsmSequence.size()];
  64. WTEntry.CyclesSpentInSchedulerQueue +=
  65. TVEntry.CycleIssued - CycleDispatched;
  66. assert(CycleDispatched <= TVEntry.CycleReady &&
  67. "Instruction cannot be ready if it hasn't been dispatched yet!");
  68. WTEntry.CyclesSpentInSQWhileReady +=
  69. TVEntry.CycleIssued - TVEntry.CycleReady;
  70. WTEntry.CyclesSpentAfterWBAndBeforeRetire +=
  71. (CurrentCycle - 1) - TVEntry.CycleExecuted;
  72. break;
  73. }
  74. case HWInstructionEvent::Ready:
  75. Timeline[Index].CycleReady = CurrentCycle;
  76. break;
  77. case HWInstructionEvent::Issued:
  78. Timeline[Index].CycleIssued = CurrentCycle;
  79. break;
  80. case HWInstructionEvent::Executed:
  81. Timeline[Index].CycleExecuted = CurrentCycle;
  82. break;
  83. case HWInstructionEvent::Dispatched:
  84. // There may be multiple dispatch events. Microcoded instructions that are
  85. // expanded into multiple uOps may require multiple dispatch cycles. Here,
  86. // we want to capture the first dispatch cycle.
  87. if (Timeline[Index].CycleDispatched == -1)
  88. Timeline[Index].CycleDispatched = static_cast<int>(CurrentCycle);
  89. break;
  90. default:
  91. return;
  92. }
  93. if (CurrentCycle < MaxCycle)
  94. LastCycle = std::max(LastCycle, CurrentCycle);
  95. }
  96. static raw_ostream::Colors chooseColor(unsigned CumulativeCycles,
  97. unsigned Executions, int BufferSize) {
  98. if (CumulativeCycles && BufferSize < 0)
  99. return raw_ostream::MAGENTA;
  100. unsigned Size = static_cast<unsigned>(BufferSize);
  101. if (CumulativeCycles >= Size * Executions)
  102. return raw_ostream::RED;
  103. if ((CumulativeCycles * 2) >= Size * Executions)
  104. return raw_ostream::YELLOW;
  105. return raw_ostream::SAVEDCOLOR;
  106. }
  107. static void tryChangeColor(raw_ostream &OS, unsigned Cycles,
  108. unsigned Executions, int BufferSize) {
  109. if (!OS.has_colors())
  110. return;
  111. raw_ostream::Colors Color = chooseColor(Cycles, Executions, BufferSize);
  112. if (Color == raw_ostream::SAVEDCOLOR) {
  113. OS.resetColor();
  114. return;
  115. }
  116. OS.changeColor(Color, /* bold */ true, /* BG */ false);
  117. }
  118. void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS,
  119. const WaitTimeEntry &Entry,
  120. unsigned SourceIndex,
  121. unsigned Executions) const {
  122. OS << SourceIndex << '.';
  123. OS.PadToColumn(7);
  124. double AverageTime1, AverageTime2, AverageTime3;
  125. AverageTime1 = (double)Entry.CyclesSpentInSchedulerQueue / Executions;
  126. AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / Executions;
  127. AverageTime3 = (double)Entry.CyclesSpentAfterWBAndBeforeRetire / Executions;
  128. OS << Executions;
  129. OS.PadToColumn(13);
  130. int BufferSize = UsedBuffer[SourceIndex].second;
  131. tryChangeColor(OS, Entry.CyclesSpentInSchedulerQueue, Executions, BufferSize);
  132. OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10);
  133. OS.PadToColumn(20);
  134. tryChangeColor(OS, Entry.CyclesSpentInSQWhileReady, Executions, BufferSize);
  135. OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10);
  136. OS.PadToColumn(27);
  137. tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire, Executions,
  138. STI.getSchedModel().MicroOpBufferSize);
  139. OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10);
  140. if (OS.has_colors())
  141. OS.resetColor();
  142. OS.PadToColumn(34);
  143. }
  144. void TimelineView::printAverageWaitTimes(raw_ostream &OS) const {
  145. std::string Header =
  146. "\n\nAverage Wait times (based on the timeline view):\n"
  147. "[0]: Executions\n"
  148. "[1]: Average time spent waiting in a scheduler's queue\n"
  149. "[2]: Average time spent waiting in a scheduler's queue while ready\n"
  150. "[3]: Average time elapsed from WB until retire stage\n\n"
  151. " [0] [1] [2] [3]\n";
  152. OS << Header;
  153. // Use a different string stream for printing instructions.
  154. std::string Instruction;
  155. raw_string_ostream InstrStream(Instruction);
  156. formatted_raw_ostream FOS(OS);
  157. unsigned Executions = Timeline.size() / AsmSequence.size();
  158. for (unsigned I = 0, E = WaitTime.size(); I < E; ++I) {
  159. printWaitTimeEntry(FOS, WaitTime[I], I, Executions);
  160. // Append the instruction info at the end of the line.
  161. const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
  162. MCIP.printInst(&Inst, InstrStream, "", STI);
  163. InstrStream.flush();
  164. // Consume any tabs or spaces at the beginning of the string.
  165. StringRef Str(Instruction);
  166. Str = Str.ltrim();
  167. FOS << " " << Str << '\n';
  168. FOS.flush();
  169. Instruction = "";
  170. }
  171. }
  172. void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS,
  173. const TimelineViewEntry &Entry,
  174. unsigned Iteration,
  175. unsigned SourceIndex) const {
  176. if (Iteration == 0 && SourceIndex == 0)
  177. OS << '\n';
  178. OS << '[' << Iteration << ',' << SourceIndex << ']';
  179. OS.PadToColumn(10);
  180. assert(Entry.CycleDispatched >= 0 && "Invalid TimelineViewEntry!");
  181. unsigned CycleDispatched = static_cast<unsigned>(Entry.CycleDispatched);
  182. for (unsigned I = 0, E = CycleDispatched; I < E; ++I)
  183. OS << ((I % 5 == 0) ? '.' : ' ');
  184. OS << TimelineView::DisplayChar::Dispatched;
  185. if (CycleDispatched != Entry.CycleExecuted) {
  186. // Zero latency instructions have the same value for CycleDispatched,
  187. // CycleIssued and CycleExecuted.
  188. for (unsigned I = CycleDispatched + 1, E = Entry.CycleIssued; I < E; ++I)
  189. OS << TimelineView::DisplayChar::Waiting;
  190. if (Entry.CycleIssued == Entry.CycleExecuted)
  191. OS << TimelineView::DisplayChar::DisplayChar::Executed;
  192. else {
  193. if (CycleDispatched != Entry.CycleIssued)
  194. OS << TimelineView::DisplayChar::Executing;
  195. for (unsigned I = Entry.CycleIssued + 1, E = Entry.CycleExecuted; I < E;
  196. ++I)
  197. OS << TimelineView::DisplayChar::Executing;
  198. OS << TimelineView::DisplayChar::Executed;
  199. }
  200. }
  201. for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I)
  202. OS << TimelineView::DisplayChar::RetireLag;
  203. OS << TimelineView::DisplayChar::Retired;
  204. // Skip other columns.
  205. for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I)
  206. OS << ((I % 5 == 0 || I == LastCycle) ? '.' : ' ');
  207. }
  208. static void printTimelineHeader(formatted_raw_ostream &OS, unsigned Cycles) {
  209. OS << "\n\nTimeline view:\n";
  210. if (Cycles >= 10) {
  211. OS.PadToColumn(10);
  212. for (unsigned I = 0; I <= Cycles; ++I) {
  213. if (((I / 10) & 1) == 0)
  214. OS << ' ';
  215. else
  216. OS << I % 10;
  217. }
  218. OS << '\n';
  219. }
  220. OS << "Index";
  221. OS.PadToColumn(10);
  222. for (unsigned I = 0; I <= Cycles; ++I) {
  223. if (((I / 10) & 1) == 0)
  224. OS << I % 10;
  225. else
  226. OS << ' ';
  227. }
  228. OS << '\n';
  229. }
  230. void TimelineView::printTimeline(raw_ostream &OS) const {
  231. formatted_raw_ostream FOS(OS);
  232. printTimelineHeader(FOS, LastCycle);
  233. FOS.flush();
  234. // Use a different string stream for the instruction.
  235. std::string Instruction;
  236. raw_string_ostream InstrStream(Instruction);
  237. for (unsigned I = 0, E = Timeline.size(); I < E; ++I) {
  238. const TimelineViewEntry &Entry = Timeline[I];
  239. if (Entry.CycleRetired == 0)
  240. return;
  241. unsigned Iteration = I / AsmSequence.size();
  242. unsigned SourceIndex = I % AsmSequence.size();
  243. printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex);
  244. // Append the instruction info at the end of the line.
  245. const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
  246. MCIP.printInst(&Inst, InstrStream, "", STI);
  247. InstrStream.flush();
  248. // Consume any tabs or spaces at the beginning of the string.
  249. StringRef Str(Instruction);
  250. Str = Str.ltrim();
  251. FOS << " " << Str << '\n';
  252. FOS.flush();
  253. Instruction = "";
  254. }
  255. }
  256. } // namespace mca