SchedulerStatistics.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. //===--------------------- SchedulerStatistics.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. /// \file
  9. ///
  10. /// This file implements the SchedulerStatistics interface.
  11. ///
  12. //===----------------------------------------------------------------------===//
  13. #include "Views/SchedulerStatistics.h"
  14. #include "llvm/Support/Format.h"
  15. #include "llvm/Support/FormattedStream.h"
  16. namespace llvm {
  17. namespace mca {
  18. SchedulerStatistics::SchedulerStatistics(const llvm::MCSubtargetInfo &STI)
  19. : SM(STI.getSchedModel()), LQResourceID(0), SQResourceID(0), NumIssued(0),
  20. NumCycles(0), MostRecentLoadDispatched(~0U),
  21. MostRecentStoreDispatched(~0U),
  22. Usage(STI.getSchedModel().NumProcResourceKinds, {0, 0, 0}) {
  23. if (SM.hasExtraProcessorInfo()) {
  24. const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo();
  25. LQResourceID = EPI.LoadQueueID;
  26. SQResourceID = EPI.StoreQueueID;
  27. }
  28. }
  29. // FIXME: This implementation works under the assumption that load/store queue
  30. // entries are reserved at 'instruction dispatched' stage, and released at
  31. // 'instruction executed' stage. This currently matches the behavior of LSUnit.
  32. //
  33. // The current design minimizes the number of events generated by the
  34. // Dispatch/Execute stages, at the cost of doing extra bookkeeping in method
  35. // `onEvent`. However, it introduces a subtle dependency between this view and
  36. // how the LSUnit works.
  37. //
  38. // In future we should add a new "memory queue" event type, so that we stop
  39. // making assumptions on how LSUnit internally works (See PR39828).
  40. void SchedulerStatistics::onEvent(const HWInstructionEvent &Event) {
  41. if (Event.Type == HWInstructionEvent::Issued) {
  42. const Instruction &Inst = *Event.IR.getInstruction();
  43. NumIssued += Inst.getDesc().NumMicroOps;
  44. } else if (Event.Type == HWInstructionEvent::Dispatched) {
  45. const Instruction &Inst = *Event.IR.getInstruction();
  46. const unsigned Index = Event.IR.getSourceIndex();
  47. if (LQResourceID && Inst.getMayLoad() &&
  48. MostRecentLoadDispatched != Index) {
  49. Usage[LQResourceID].SlotsInUse++;
  50. MostRecentLoadDispatched = Index;
  51. }
  52. if (SQResourceID && Inst.getMayStore() &&
  53. MostRecentStoreDispatched != Index) {
  54. Usage[SQResourceID].SlotsInUse++;
  55. MostRecentStoreDispatched = Index;
  56. }
  57. } else if (Event.Type == HWInstructionEvent::Executed) {
  58. const Instruction &Inst = *Event.IR.getInstruction();
  59. if (LQResourceID && Inst.getMayLoad()) {
  60. assert(Usage[LQResourceID].SlotsInUse);
  61. Usage[LQResourceID].SlotsInUse--;
  62. }
  63. if (SQResourceID && Inst.getMayStore()) {
  64. assert(Usage[SQResourceID].SlotsInUse);
  65. Usage[SQResourceID].SlotsInUse--;
  66. }
  67. }
  68. }
  69. void SchedulerStatistics::onReservedBuffers(const InstRef & /* unused */,
  70. ArrayRef<unsigned> Buffers) {
  71. for (const unsigned Buffer : Buffers) {
  72. if (Buffer == LQResourceID || Buffer == SQResourceID)
  73. continue;
  74. Usage[Buffer].SlotsInUse++;
  75. }
  76. }
  77. void SchedulerStatistics::onReleasedBuffers(const InstRef & /* unused */,
  78. ArrayRef<unsigned> Buffers) {
  79. for (const unsigned Buffer : Buffers) {
  80. if (Buffer == LQResourceID || Buffer == SQResourceID)
  81. continue;
  82. Usage[Buffer].SlotsInUse--;
  83. }
  84. }
  85. void SchedulerStatistics::updateHistograms() {
  86. for (BufferUsage &BU : Usage) {
  87. BU.CumulativeNumUsedSlots += BU.SlotsInUse;
  88. BU.MaxUsedSlots = std::max(BU.MaxUsedSlots, BU.SlotsInUse);
  89. }
  90. IssueWidthPerCycle[NumIssued]++;
  91. NumIssued = 0;
  92. }
  93. void SchedulerStatistics::printSchedulerStats(raw_ostream &OS) const {
  94. OS << "\n\nSchedulers - "
  95. << "number of cycles where we saw N micro opcodes issued:\n";
  96. OS << "[# issued], [# cycles]\n";
  97. bool HasColors = OS.has_colors();
  98. const auto It =
  99. std::max_element(IssueWidthPerCycle.begin(), IssueWidthPerCycle.end());
  100. for (const std::pair<const unsigned, unsigned> &Entry : IssueWidthPerCycle) {
  101. unsigned NumIssued = Entry.first;
  102. if (NumIssued == It->first && HasColors)
  103. OS.changeColor(raw_ostream::SAVEDCOLOR, true, false);
  104. unsigned IPC = Entry.second;
  105. OS << " " << NumIssued << ", " << IPC << " ("
  106. << format("%.1f", ((double)IPC / NumCycles) * 100) << "%)\n";
  107. if (HasColors)
  108. OS.resetColor();
  109. }
  110. }
  111. void SchedulerStatistics::printSchedulerUsage(raw_ostream &OS) const {
  112. assert(NumCycles && "Unexpected number of cycles!");
  113. OS << "\nScheduler's queue usage:\n";
  114. if (all_of(Usage, [](const BufferUsage &BU) { return !BU.MaxUsedSlots; })) {
  115. OS << "No scheduler resources used.\n";
  116. return;
  117. }
  118. OS << "[1] Resource name.\n"
  119. << "[2] Average number of used buffer entries.\n"
  120. << "[3] Maximum number of used buffer entries.\n"
  121. << "[4] Total number of buffer entries.\n\n"
  122. << " [1] [2] [3] [4]\n";
  123. formatted_raw_ostream FOS(OS);
  124. bool HasColors = FOS.has_colors();
  125. for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
  126. const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
  127. if (ProcResource.BufferSize <= 0)
  128. continue;
  129. const BufferUsage &BU = Usage[I];
  130. double AvgUsage = (double)BU.CumulativeNumUsedSlots / NumCycles;
  131. double AlmostFullThreshold = (double)(ProcResource.BufferSize * 4) / 5;
  132. unsigned NormalizedAvg = floor((AvgUsage * 10) + 0.5) / 10;
  133. unsigned NormalizedThreshold = floor((AlmostFullThreshold * 10) + 0.5) / 10;
  134. FOS << ProcResource.Name;
  135. FOS.PadToColumn(17);
  136. if (HasColors && NormalizedAvg >= NormalizedThreshold)
  137. FOS.changeColor(raw_ostream::YELLOW, true, false);
  138. FOS << NormalizedAvg;
  139. if (HasColors)
  140. FOS.resetColor();
  141. FOS.PadToColumn(28);
  142. if (HasColors &&
  143. BU.MaxUsedSlots == static_cast<unsigned>(ProcResource.BufferSize))
  144. FOS.changeColor(raw_ostream::RED, true, false);
  145. FOS << BU.MaxUsedSlots;
  146. if (HasColors)
  147. FOS.resetColor();
  148. FOS.PadToColumn(39);
  149. FOS << ProcResource.BufferSize << '\n';
  150. }
  151. FOS.flush();
  152. }
  153. void SchedulerStatistics::printView(raw_ostream &OS) const {
  154. printSchedulerStats(OS);
  155. printSchedulerUsage(OS);
  156. }
  157. } // namespace mca
  158. } // namespace llvm