1 //===--------------------- BottleneckAnalysis.cpp ---------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file implements the functionalities used by the BottleneckAnalysis
11 /// to report bottleneck info.
13 //===----------------------------------------------------------------------===//
15 #include "Views/BottleneckAnalysis.h"
16 #include "llvm/MC/MCInst.h"
17 #include "llvm/MCA/Support.h"
18 #include "llvm/Support/Format.h"
19 #include "llvm/Support/FormattedStream.h"
24 #define DEBUG_TYPE "llvm-mca"
26 PressureTracker::PressureTracker(const MCSchedModel
&Model
)
28 ResourcePressureDistribution(Model
.getNumProcResourceKinds(), 0),
29 ProcResID2Mask(Model
.getNumProcResourceKinds(), 0),
30 ResIdx2ProcResID(Model
.getNumProcResourceKinds(), 0),
31 ProcResID2ResourceUsersIndex(Model
.getNumProcResourceKinds(), 0) {
32 computeProcResourceMasks(SM
, ProcResID2Mask
);
34 // Ignore the invalid resource at index zero.
35 unsigned NextResourceUsersIdx
= 0;
36 for (unsigned I
= 1, E
= Model
.getNumProcResourceKinds(); I
< E
; ++I
) {
37 const MCProcResourceDesc
&ProcResource
= *SM
.getProcResource(I
);
38 ProcResID2ResourceUsersIndex
[I
] = NextResourceUsersIdx
;
39 NextResourceUsersIdx
+= ProcResource
.NumUnits
;
40 uint64_t ResourceMask
= ProcResID2Mask
[I
];
41 ResIdx2ProcResID
[getResourceStateIndex(ResourceMask
)] = I
;
44 ResourceUsers
.resize(NextResourceUsersIdx
);
45 std::fill(ResourceUsers
.begin(), ResourceUsers
.end(),
46 std::make_pair
<unsigned, unsigned>(~0U, 0U));
49 void PressureTracker::getResourceUsers(uint64_t ResourceMask
,
50 SmallVectorImpl
<User
> &Users
) const {
51 unsigned Index
= getResourceStateIndex(ResourceMask
);
52 unsigned ProcResID
= ResIdx2ProcResID
[Index
];
53 const MCProcResourceDesc
&PRDesc
= *SM
.getProcResource(ProcResID
);
54 for (unsigned I
= 0, E
= PRDesc
.NumUnits
; I
< E
; ++I
) {
55 const User U
= getResourceUser(ProcResID
, I
);
56 if (U
.second
&& IPI
.find(U
.first
) != IPI
.end())
57 Users
.emplace_back(U
);
61 void PressureTracker::onInstructionDispatched(unsigned IID
) {
62 IPI
.insert(std::make_pair(IID
, InstructionPressureInfo()));
65 void PressureTracker::onInstructionExecuted(unsigned IID
) { IPI
.erase(IID
); }
67 void PressureTracker::handleInstructionIssuedEvent(
68 const HWInstructionIssuedEvent
&Event
) {
69 unsigned IID
= Event
.IR
.getSourceIndex();
70 using ResourceRef
= HWInstructionIssuedEvent::ResourceRef
;
71 using ResourceUse
= std::pair
<ResourceRef
, ResourceCycles
>;
72 for (const ResourceUse
&Use
: Event
.UsedResources
) {
73 const ResourceRef
&RR
= Use
.first
;
74 unsigned Index
= ProcResID2ResourceUsersIndex
[RR
.first
];
75 Index
+= countTrailingZeros(RR
.second
);
76 ResourceUsers
[Index
] = std::make_pair(IID
, Use
.second
.getNumerator());
80 void PressureTracker::updateResourcePressureDistribution(
81 uint64_t CumulativeMask
) {
82 while (CumulativeMask
) {
83 uint64_t Current
= CumulativeMask
& (-CumulativeMask
);
84 unsigned ResIdx
= getResourceStateIndex(Current
);
85 unsigned ProcResID
= ResIdx2ProcResID
[ResIdx
];
86 uint64_t Mask
= ProcResID2Mask
[ProcResID
];
88 if (Mask
== Current
) {
89 ResourcePressureDistribution
[ProcResID
]++;
90 CumulativeMask
^= Current
;
96 uint64_t SubUnit
= Mask
& (-Mask
);
97 ResIdx
= getResourceStateIndex(SubUnit
);
98 ProcResID
= ResIdx2ProcResID
[ResIdx
];
99 ResourcePressureDistribution
[ProcResID
]++;
103 CumulativeMask
^= Current
;
107 void PressureTracker::handlePressureEvent(const HWPressureEvent
&Event
) {
108 assert(Event
.Reason
!= HWPressureEvent::INVALID
&&
109 "Unexpected invalid event!");
111 switch (Event
.Reason
) {
115 case HWPressureEvent::RESOURCES
: {
116 const uint64_t ResourceMask
= Event
.ResourceMask
;
117 updateResourcePressureDistribution(Event
.ResourceMask
);
119 for (const InstRef
&IR
: Event
.AffectedInstructions
) {
120 const Instruction
&IS
= *IR
.getInstruction();
121 unsigned BusyResources
= IS
.getCriticalResourceMask() & ResourceMask
;
125 unsigned IID
= IR
.getSourceIndex();
126 IPI
[IID
].ResourcePressureCycles
++;
131 case HWPressureEvent::REGISTER_DEPS
:
132 for (const InstRef
&IR
: Event
.AffectedInstructions
) {
133 unsigned IID
= IR
.getSourceIndex();
134 IPI
[IID
].RegisterPressureCycles
++;
138 case HWPressureEvent::MEMORY_DEPS
:
139 for (const InstRef
&IR
: Event
.AffectedInstructions
) {
140 unsigned IID
= IR
.getSourceIndex();
141 IPI
[IID
].MemoryPressureCycles
++;
147 void DependencyGraph::dumpDependencyEdge(raw_ostream
&OS
,
148 const DependencyEdge
&DepEdge
,
149 MCInstPrinter
&MCIP
) const {
150 unsigned FromIID
= DepEdge
.FromIID
;
151 unsigned ToIID
= DepEdge
.ToIID
;
152 assert(FromIID
< ToIID
&& "Graph should be acyclic!");
154 const DependencyEdge::Dependency
&DE
= DepEdge
.Dep
;
155 assert(DE
.Type
!= DependencyEdge::DT_INVALID
&& "Unexpected invalid edge!");
157 OS
<< " FROM: " << FromIID
<< " TO: " << ToIID
<< " ";
158 if (DE
.Type
== DependencyEdge::DT_REGISTER
) {
159 OS
<< " - REGISTER: ";
160 MCIP
.printRegName(OS
, DE
.ResourceOrRegID
);
161 } else if (DE
.Type
== DependencyEdge::DT_MEMORY
) {
164 assert(DE
.Type
== DependencyEdge::DT_RESOURCE
&&
165 "Unsupported dependency type!");
166 OS
<< " - RESOURCE MASK: " << DE
.ResourceOrRegID
;
168 OS
<< " - CYCLES: " << DE
.Cost
<< '\n';
172 void DependencyGraph::initializeRootSet(
173 SmallVectorImpl
<unsigned> &RootSet
) const {
174 for (unsigned I
= 0, E
= Nodes
.size(); I
< E
; ++I
) {
175 const DGNode
&N
= Nodes
[I
];
176 if (N
.NumPredecessors
== 0 && !N
.OutgoingEdges
.empty())
177 RootSet
.emplace_back(I
);
181 void DependencyGraph::propagateThroughEdges(
182 SmallVectorImpl
<unsigned> &RootSet
) {
183 SmallVector
<unsigned, 8> ToVisit
;
185 // A critical sequence is computed as the longest path from a node of the
186 // RootSet to a leaf node (i.e. a node with no successors). The RootSet is
187 // composed of nodes with at least one successor, and no predecessors.
189 // Each node of the graph starts with an initial default cost of zero. The
190 // cost of a node is a measure of criticality: the higher the cost, the bigger
191 // is the performance impact.
193 // This algorithm is very similar to a (reverse) Dijkstra. Every iteration of
194 // the inner loop selects (i.e. visits) a node N from a set of `unvisited
195 // nodes`, and then propagates the cost of N to all its neighbors.
197 // The `unvisited nodes` set initially contains all the nodes from the
198 // RootSet. A node N is added to the `unvisited nodes` if all its
199 // predecessors have been visited already.
201 // For simplicity, every node tracks the number of unvisited incoming edges in
202 // field `NumVisitedPredecessors`. When the value of that field drops to
203 // zero, then the corresponding node is added to a `ToVisit` set.
205 // At the end of every iteration of the outer loop, set `ToVisit` becomes our
206 // new `unvisited nodes` set.
208 // The algorithm terminates when the set of unvisited nodes (i.e. our RootSet)
209 // is empty. This algorithm works under the assumption that the graph is
212 for (unsigned IID
: RootSet
) {
213 const DGNode
&N
= Nodes
[IID
];
214 for (const DependencyEdge
&DepEdge
: N
.OutgoingEdges
) {
215 unsigned ToIID
= DepEdge
.ToIID
;
216 DGNode
&To
= Nodes
[ToIID
];
217 uint64_t Cost
= N
.Cost
+ DepEdge
.Dep
.Cost
;
218 // Check if this is the most expensive incoming edge seen so far. In
219 // case, update the total cost of the destination node (ToIID), as well
220 // its field `CriticalPredecessor`.
221 if (Cost
> To
.Cost
) {
222 To
.CriticalPredecessor
= DepEdge
;
224 To
.Depth
= N
.Depth
+ 1;
226 To
.NumVisitedPredecessors
++;
227 if (To
.NumVisitedPredecessors
== To
.NumPredecessors
)
228 ToVisit
.emplace_back(ToIID
);
232 std::swap(RootSet
, ToVisit
);
234 } while (!RootSet
.empty());
237 void DependencyGraph::getCriticalSequence(
238 SmallVectorImpl
<const DependencyEdge
*> &Seq
) const {
239 // At this stage, nodes of the graph have been already visited, and costs have
240 // been propagated through the edges (see method `propagateThroughEdges()`).
242 // Identify the node N with the highest cost in the graph. By construction,
243 // that node is the last instruction of our critical sequence.
244 // Field N.Depth would tell us the total length of the sequence.
246 // To obtain the sequence of critical edges, we simply follow the chain of critical
247 // predecessors starting from node N (field DGNode::CriticalPredecessor).
248 const auto It
= std::max_element(
249 Nodes
.begin(), Nodes
.end(),
250 [](const DGNode
&Lhs
, const DGNode
&Rhs
) { return Lhs
.Cost
< Rhs
.Cost
; });
251 unsigned IID
= std::distance(Nodes
.begin(), It
);
252 Seq
.resize(Nodes
[IID
].Depth
);
253 for (unsigned I
= Seq
.size(), E
= 0; I
> E
; --I
) {
254 const DGNode
&N
= Nodes
[IID
];
255 Seq
[I
- 1] = &N
.CriticalPredecessor
;
256 IID
= N
.CriticalPredecessor
.FromIID
;
260 static void printInstruction(formatted_raw_ostream
&FOS
,
261 const MCSubtargetInfo
&STI
, MCInstPrinter
&MCIP
,
263 bool UseDifferentColor
= false) {
264 std::string Instruction
;
265 raw_string_ostream
InstrStream(Instruction
);
269 MCIP
.printInst(&MCI
, InstrStream
, "", STI
);
272 if (UseDifferentColor
)
273 FOS
.changeColor(raw_ostream::CYAN
, true, false);
274 FOS
<< StringRef(Instruction
).ltrim();
275 if (UseDifferentColor
)
279 void BottleneckAnalysis::printCriticalSequence(raw_ostream
&OS
) const {
280 SmallVector
<const DependencyEdge
*, 16> Seq
;
281 DG
.getCriticalSequence(Seq
);
285 OS
<< "\nCritical sequence based on the simulation:\n\n";
287 const DependencyEdge
&FirstEdge
= *Seq
[0];
288 unsigned FromIID
= FirstEdge
.FromIID
% Source
.size();
289 unsigned ToIID
= FirstEdge
.ToIID
% Source
.size();
290 bool IsLoopCarried
= FromIID
>= ToIID
;
292 formatted_raw_ostream
FOS(OS
);
294 FOS
<< "Instruction";
296 FOS
<< "Dependency Information";
298 bool HasColors
= FOS
.has_colors();
300 unsigned CurrentIID
= 0;
302 FOS
<< "\n +----< " << FromIID
<< ".";
303 printInstruction(FOS
, STI
, MCIP
, Source
[FromIID
], HasColors
);
304 FOS
<< "\n |\n | < loop carried > \n |";
306 while (CurrentIID
< FromIID
) {
307 FOS
<< "\n " << CurrentIID
<< ".";
308 printInstruction(FOS
, STI
, MCIP
, Source
[CurrentIID
]);
312 FOS
<< "\n +----< " << CurrentIID
<< ".";
313 printInstruction(FOS
, STI
, MCIP
, Source
[CurrentIID
], HasColors
);
317 for (const DependencyEdge
*&DE
: Seq
) {
318 ToIID
= DE
->ToIID
% Source
.size();
319 unsigned LastIID
= CurrentIID
> ToIID
? Source
.size() : ToIID
;
321 while (CurrentIID
< LastIID
) {
322 FOS
<< "\n | " << CurrentIID
<< ".";
323 printInstruction(FOS
, STI
, MCIP
, Source
[CurrentIID
]);
327 if (CurrentIID
== ToIID
) {
328 FOS
<< "\n +----> " << ToIID
<< ".";
329 printInstruction(FOS
, STI
, MCIP
, Source
[CurrentIID
], HasColors
);
331 FOS
<< "\n |\n | < loop carried > \n |"
332 << "\n +----> " << ToIID
<< ".";
333 printInstruction(FOS
, STI
, MCIP
, Source
[ToIID
], HasColors
);
337 const DependencyEdge::Dependency
&Dep
= DE
->Dep
;
339 FOS
.changeColor(raw_ostream::SAVEDCOLOR
, true, false);
341 if (Dep
.Type
== DependencyEdge::DT_REGISTER
) {
342 FOS
<< "## REGISTER dependency: ";
344 FOS
.changeColor(raw_ostream::MAGENTA
, true, false);
345 MCIP
.printRegName(FOS
, Dep
.ResourceOrRegID
);
346 } else if (Dep
.Type
== DependencyEdge::DT_MEMORY
) {
347 FOS
<< "## MEMORY dependency.";
349 assert(Dep
.Type
== DependencyEdge::DT_RESOURCE
&&
350 "Unsupported dependency type!");
351 FOS
<< "## RESOURCE interference: ";
353 FOS
.changeColor(raw_ostream::MAGENTA
, true, false);
354 FOS
<< Tracker
.resolveResourceName(Dep
.ResourceOrRegID
);
357 FOS
.changeColor(raw_ostream::SAVEDCOLOR
, true, false);
359 FOS
<< " [ probability: " << ((DE
->Frequency
* 100) / Iterations
)
367 while (CurrentIID
< Source
.size()) {
368 FOS
<< "\n " << CurrentIID
<< ".";
369 printInstruction(FOS
, STI
, MCIP
, Source
[CurrentIID
]);
378 void DependencyGraph::dump(raw_ostream
&OS
, MCInstPrinter
&MCIP
) const {
379 OS
<< "\nREG DEPS\n";
380 for (const DGNode
&Node
: Nodes
)
381 for (const DependencyEdge
&DE
: Node
.OutgoingEdges
)
382 if (DE
.Dep
.Type
== DependencyEdge::DT_REGISTER
)
383 dumpDependencyEdge(OS
, DE
, MCIP
);
385 OS
<< "\nMEM DEPS\n";
386 for (const DGNode
&Node
: Nodes
)
387 for (const DependencyEdge
&DE
: Node
.OutgoingEdges
)
388 if (DE
.Dep
.Type
== DependencyEdge::DT_MEMORY
)
389 dumpDependencyEdge(OS
, DE
, MCIP
);
391 OS
<< "\nRESOURCE DEPS\n";
392 for (const DGNode
&Node
: Nodes
)
393 for (const DependencyEdge
&DE
: Node
.OutgoingEdges
)
394 if (DE
.Dep
.Type
== DependencyEdge::DT_RESOURCE
)
395 dumpDependencyEdge(OS
, DE
, MCIP
);
399 void DependencyGraph::addDependency(unsigned From
, unsigned To
,
400 DependencyEdge::Dependency
&&Dep
) {
401 DGNode
&NodeFrom
= Nodes
[From
];
402 DGNode
&NodeTo
= Nodes
[To
];
403 SmallVectorImpl
<DependencyEdge
> &Vec
= NodeFrom
.OutgoingEdges
;
405 auto It
= find_if(Vec
, [To
, Dep
](DependencyEdge
&DE
) {
406 return DE
.ToIID
== To
&& DE
.Dep
.ResourceOrRegID
== Dep
.ResourceOrRegID
;
409 if (It
!= Vec
.end()) {
410 It
->Dep
.Cost
+= Dep
.Cost
;
415 DependencyEdge DE
= {Dep
, From
, To
, 1};
416 Vec
.emplace_back(DE
);
417 NodeTo
.NumPredecessors
++;
420 BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo
&sti
,
421 MCInstPrinter
&Printer
,
422 ArrayRef
<MCInst
> S
, unsigned NumIter
)
423 : STI(sti
), MCIP(Printer
), Tracker(STI
.getSchedModel()), DG(S
.size() * 3),
424 Source(S
), Iterations(NumIter
), TotalCycles(0),
425 PressureIncreasedBecauseOfResources(false),
426 PressureIncreasedBecauseOfRegisterDependencies(false),
427 PressureIncreasedBecauseOfMemoryDependencies(false),
428 SeenStallCycles(false), BPI() {}
430 void BottleneckAnalysis::addRegisterDep(unsigned From
, unsigned To
,
431 unsigned RegID
, unsigned Cost
) {
432 bool IsLoopCarried
= From
>= To
;
433 unsigned SourceSize
= Source
.size();
435 Cost
*= Iterations
/ 2;
436 DG
.addRegisterDep(From
, To
+ SourceSize
, RegID
, Cost
);
437 DG
.addRegisterDep(From
+ SourceSize
, To
+ (SourceSize
* 2), RegID
, Cost
);
440 DG
.addRegisterDep(From
+ SourceSize
, To
+ SourceSize
, RegID
, Cost
);
443 void BottleneckAnalysis::addMemoryDep(unsigned From
, unsigned To
,
445 bool IsLoopCarried
= From
>= To
;
446 unsigned SourceSize
= Source
.size();
448 Cost
*= Iterations
/ 2;
449 DG
.addMemoryDep(From
, To
+ SourceSize
, Cost
);
450 DG
.addMemoryDep(From
+ SourceSize
, To
+ (SourceSize
* 2), Cost
);
453 DG
.addMemoryDep(From
+ SourceSize
, To
+ SourceSize
, Cost
);
456 void BottleneckAnalysis::addResourceDep(unsigned From
, unsigned To
,
457 uint64_t Mask
, unsigned Cost
) {
458 bool IsLoopCarried
= From
>= To
;
459 unsigned SourceSize
= Source
.size();
461 Cost
*= Iterations
/ 2;
462 DG
.addResourceDep(From
, To
+ SourceSize
, Mask
, Cost
);
463 DG
.addResourceDep(From
+ SourceSize
, To
+ (SourceSize
* 2), Mask
, Cost
);
466 DG
.addResourceDep(From
+ SourceSize
, To
+ SourceSize
, Mask
, Cost
);
469 void BottleneckAnalysis::onEvent(const HWInstructionEvent
&Event
) {
470 const unsigned IID
= Event
.IR
.getSourceIndex();
471 if (Event
.Type
== HWInstructionEvent::Dispatched
) {
472 Tracker
.onInstructionDispatched(IID
);
475 if (Event
.Type
== HWInstructionEvent::Executed
) {
476 Tracker
.onInstructionExecuted(IID
);
480 if (Event
.Type
!= HWInstructionEvent::Issued
)
483 const Instruction
&IS
= *Event
.IR
.getInstruction();
484 unsigned To
= IID
% Source
.size();
486 unsigned Cycles
= 2 * Tracker
.getResourcePressureCycles(IID
);
487 uint64_t ResourceMask
= IS
.getCriticalResourceMask();
488 SmallVector
<std::pair
<unsigned, unsigned>, 4> Users
;
489 while (ResourceMask
) {
490 uint64_t Current
= ResourceMask
& (-ResourceMask
);
491 Tracker
.getResourceUsers(Current
, Users
);
492 for (const std::pair
<unsigned, unsigned> &U
: Users
)
493 addResourceDep(U
.first
% Source
.size(), To
, Current
, U
.second
+ Cycles
);
495 ResourceMask
^= Current
;
498 const CriticalDependency
&RegDep
= IS
.getCriticalRegDep();
500 Cycles
= RegDep
.Cycles
+ 2 * Tracker
.getRegisterPressureCycles(IID
);
501 unsigned From
= RegDep
.IID
% Source
.size();
502 addRegisterDep(From
, To
, RegDep
.RegID
, Cycles
);
505 const CriticalDependency
&MemDep
= IS
.getCriticalMemDep();
507 Cycles
= MemDep
.Cycles
+ 2 * Tracker
.getMemoryPressureCycles(IID
);
508 unsigned From
= MemDep
.IID
% Source
.size();
509 addMemoryDep(From
, To
, Cycles
);
512 Tracker
.handleInstructionIssuedEvent(
513 static_cast<const HWInstructionIssuedEvent
&>(Event
));
515 // Check if this is the last simulated instruction.
516 if (IID
== ((Iterations
* Source
.size()) - 1))
520 void BottleneckAnalysis::onEvent(const HWPressureEvent
&Event
) {
521 assert(Event
.Reason
!= HWPressureEvent::INVALID
&&
522 "Unexpected invalid event!");
524 Tracker
.handlePressureEvent(Event
);
526 switch (Event
.Reason
) {
530 case HWPressureEvent::RESOURCES
:
531 PressureIncreasedBecauseOfResources
= true;
533 case HWPressureEvent::REGISTER_DEPS
:
534 PressureIncreasedBecauseOfRegisterDependencies
= true;
536 case HWPressureEvent::MEMORY_DEPS
:
537 PressureIncreasedBecauseOfMemoryDependencies
= true;
542 void BottleneckAnalysis::onCycleEnd() {
545 bool PressureIncreasedBecauseOfDataDependencies
=
546 PressureIncreasedBecauseOfRegisterDependencies
||
547 PressureIncreasedBecauseOfMemoryDependencies
;
548 if (!PressureIncreasedBecauseOfResources
&&
549 !PressureIncreasedBecauseOfDataDependencies
)
552 ++BPI
.PressureIncreaseCycles
;
553 if (PressureIncreasedBecauseOfRegisterDependencies
)
554 ++BPI
.RegisterDependencyCycles
;
555 if (PressureIncreasedBecauseOfMemoryDependencies
)
556 ++BPI
.MemoryDependencyCycles
;
557 if (PressureIncreasedBecauseOfDataDependencies
)
558 ++BPI
.DataDependencyCycles
;
559 if (PressureIncreasedBecauseOfResources
)
560 ++BPI
.ResourcePressureCycles
;
561 PressureIncreasedBecauseOfResources
= false;
562 PressureIncreasedBecauseOfRegisterDependencies
= false;
563 PressureIncreasedBecauseOfMemoryDependencies
= false;
566 void BottleneckAnalysis::printBottleneckHints(raw_ostream
&OS
) const {
567 if (!SeenStallCycles
|| !BPI
.PressureIncreaseCycles
) {
568 OS
<< "\n\nNo resource or data dependency bottlenecks discovered.\n";
572 double PressurePerCycle
=
573 (double)BPI
.PressureIncreaseCycles
* 100 / TotalCycles
;
574 double ResourcePressurePerCycle
=
575 (double)BPI
.ResourcePressureCycles
* 100 / TotalCycles
;
576 double DDPerCycle
= (double)BPI
.DataDependencyCycles
* 100 / TotalCycles
;
577 double RegDepPressurePerCycle
=
578 (double)BPI
.RegisterDependencyCycles
* 100 / TotalCycles
;
579 double MemDepPressurePerCycle
=
580 (double)BPI
.MemoryDependencyCycles
* 100 / TotalCycles
;
582 OS
<< "\n\nCycles with backend pressure increase [ "
583 << format("%.2f", floor((PressurePerCycle
* 100) + 0.5) / 100) << "% ]";
585 OS
<< "\nThroughput Bottlenecks: "
586 << "\n Resource Pressure [ "
587 << format("%.2f", floor((ResourcePressurePerCycle
* 100) + 0.5) / 100)
590 if (BPI
.PressureIncreaseCycles
) {
591 ArrayRef
<unsigned> Distribution
= Tracker
.getResourcePressureDistribution();
592 const MCSchedModel
&SM
= STI
.getSchedModel();
593 for (unsigned I
= 0, E
= Distribution
.size(); I
< E
; ++I
) {
594 unsigned ResourceCycles
= Distribution
[I
];
595 if (ResourceCycles
) {
596 double Frequency
= (double)ResourceCycles
* 100 / TotalCycles
;
597 const MCProcResourceDesc
&PRDesc
= *SM
.getProcResource(I
);
598 OS
<< "\n - " << PRDesc
.Name
<< " [ "
599 << format("%.2f", floor((Frequency
* 100) + 0.5) / 100) << "% ]";
604 OS
<< "\n Data Dependencies: [ "
605 << format("%.2f", floor((DDPerCycle
* 100) + 0.5) / 100) << "% ]";
606 OS
<< "\n - Register Dependencies [ "
607 << format("%.2f", floor((RegDepPressurePerCycle
* 100) + 0.5) / 100)
609 OS
<< "\n - Memory Dependencies [ "
610 << format("%.2f", floor((MemDepPressurePerCycle
* 100) + 0.5) / 100)
614 void BottleneckAnalysis::printView(raw_ostream
&OS
) const {
616 raw_string_ostream
TempStream(Buffer
);
617 printBottleneckHints(TempStream
);
620 printCriticalSequence(OS
);