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