TimelineView.cpp 10 KB

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