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