1 //===--------------------- BottleneckAnalysis.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 functionalities used by the BottleneckAnalysis 11 /// to report bottleneck info. 12 /// 13 //===----------------------------------------------------------------------===// 14 15 #include "Views/BottleneckAnalysis.h" 16 #include "llvm/MC/MCInst.h" 17 #include "llvm/MCA/Support.h" 18 #include "llvm/Support/Format.h" 19 20 namespace llvm { 21 namespace mca { 22 23 #define DEBUG_TYPE "llvm-mca" 24 25 PressureTracker::PressureTracker(const MCSchedModel &Model) 26 : SM(Model), 27 ResourcePressureDistribution(Model.getNumProcResourceKinds(), 0), 28 ProcResID2Mask(Model.getNumProcResourceKinds(), 0), 29 ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0), 30 ProcResID2ResourceUsersIndex(Model.getNumProcResourceKinds(), 0) { 31 computeProcResourceMasks(SM, ProcResID2Mask); 32 33 // Ignore the invalid resource at index zero. 34 unsigned NextResourceUsersIdx = 0; 35 for (unsigned I = 1, E = Model.getNumProcResourceKinds(); I < E; ++I) { 36 const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); 37 ProcResID2ResourceUsersIndex[I] = NextResourceUsersIdx; 38 NextResourceUsersIdx += ProcResource.NumUnits; 39 uint64_t ResourceMask = ProcResID2Mask[I]; 40 ResIdx2ProcResID[getResourceStateIndex(ResourceMask)] = I; 41 } 42 43 ResourceUsers.resize(NextResourceUsersIdx); 44 std::fill(ResourceUsers.begin(), ResourceUsers.end(), 45 std::make_pair<unsigned, unsigned>(~0U, 0U)); 46 } 47 48 void PressureTracker::getResourceUsers(uint64_t ResourceMask, 49 SmallVectorImpl<User> &Users) const { 50 unsigned Index = getResourceStateIndex(ResourceMask); 51 unsigned ProcResID = ResIdx2ProcResID[Index]; 52 const MCProcResourceDesc &PRDesc = *SM.getProcResource(ProcResID); 53 for (unsigned I = 0, E = PRDesc.NumUnits; I < E; ++I) { 54 const User U = getResourceUser(ProcResID, I); 55 if (U.second && IPI.find(U.first) != IPI.end()) 56 Users.emplace_back(U); 57 } 58 } 59 60 void PressureTracker::onInstructionDispatched(unsigned IID) { 61 IPI.insert(std::make_pair(IID, InstructionPressureInfo())); 62 } 63 64 void PressureTracker::onInstructionExecuted(unsigned IID) { IPI.erase(IID); } 65 66 void PressureTracker::handleInstructionIssuedEvent( 67 const HWInstructionIssuedEvent &Event) { 68 unsigned IID = Event.IR.getSourceIndex(); 69 for (const ResourceUse &Use : Event.UsedResources) { 70 const ResourceRef &RR = Use.first; 71 unsigned Index = ProcResID2ResourceUsersIndex[RR.first]; 72 Index += countTrailingZeros(RR.second); 73 ResourceUsers[Index] = std::make_pair(IID, Use.second.getNumerator()); 74 } 75 } 76 77 void PressureTracker::updateResourcePressureDistribution( 78 uint64_t CumulativeMask) { 79 while (CumulativeMask) { 80 uint64_t Current = CumulativeMask & (-CumulativeMask); 81 unsigned ResIdx = getResourceStateIndex(Current); 82 unsigned ProcResID = ResIdx2ProcResID[ResIdx]; 83 uint64_t Mask = ProcResID2Mask[ProcResID]; 84 85 if (Mask == Current) { 86 ResourcePressureDistribution[ProcResID]++; 87 CumulativeMask ^= Current; 88 continue; 89 } 90 91 Mask ^= Current; 92 while (Mask) { 93 uint64_t SubUnit = Mask & (-Mask); 94 ResIdx = getResourceStateIndex(SubUnit); 95 ProcResID = ResIdx2ProcResID[ResIdx]; 96 ResourcePressureDistribution[ProcResID]++; 97 Mask ^= SubUnit; 98 } 99 100 CumulativeMask ^= Current; 101 } 102 } 103 104 void PressureTracker::handlePressureEvent(const HWPressureEvent &Event) { 105 assert(Event.Reason != HWPressureEvent::INVALID && 106 "Unexpected invalid event!"); 107 108 switch (Event.Reason) { 109 default: 110 break; 111 112 case HWPressureEvent::RESOURCES: { 113 const uint64_t ResourceMask = Event.ResourceMask; 114 updateResourcePressureDistribution(Event.ResourceMask); 115 116 for (const InstRef &IR : Event.AffectedInstructions) { 117 const Instruction &IS = *IR.getInstruction(); 118 unsigned BusyResources = IS.getCriticalResourceMask() & ResourceMask; 119 if (!BusyResources) 120 continue; 121 122 unsigned IID = IR.getSourceIndex(); 123 IPI[IID].ResourcePressureCycles++; 124 } 125 break; 126 } 127 128 case HWPressureEvent::REGISTER_DEPS: 129 for (const InstRef &IR : Event.AffectedInstructions) { 130 unsigned IID = IR.getSourceIndex(); 131 IPI[IID].RegisterPressureCycles++; 132 } 133 break; 134 135 case HWPressureEvent::MEMORY_DEPS: 136 for (const InstRef &IR : Event.AffectedInstructions) { 137 unsigned IID = IR.getSourceIndex(); 138 IPI[IID].MemoryPressureCycles++; 139 } 140 } 141 } 142 143 #ifndef NDEBUG 144 void DependencyGraph::dumpDependencyEdge(raw_ostream &OS, 145 const DependencyEdge &DepEdge, 146 MCInstPrinter &MCIP) const { 147 unsigned FromIID = DepEdge.FromIID; 148 unsigned ToIID = DepEdge.ToIID; 149 assert(FromIID < ToIID && "Graph should be acyclic!"); 150 151 const DependencyEdge::Dependency &DE = DepEdge.Dep; 152 assert(DE.Type != DependencyEdge::DT_INVALID && "Unexpected invalid edge!"); 153 154 OS << " FROM: " << FromIID << " TO: " << ToIID << " "; 155 if (DE.Type == DependencyEdge::DT_REGISTER) { 156 OS << " - REGISTER: "; 157 MCIP.printRegName(OS, DE.ResourceOrRegID); 158 } else if (DE.Type == DependencyEdge::DT_MEMORY) { 159 OS << " - MEMORY"; 160 } else { 161 assert(DE.Type == DependencyEdge::DT_RESOURCE && 162 "Unsupported dependency type!"); 163 OS << " - RESOURCE MASK: " << DE.ResourceOrRegID; 164 } 165 OS << " - COST: " << DE.Cost << '\n'; 166 } 167 #endif // NDEBUG 168 169 void DependencyGraph::pruneEdges(unsigned Iterations) { 170 for (DGNode &N : Nodes) { 171 unsigned NumPruned = 0; 172 const unsigned Size = N.OutgoingEdges.size(); 173 // Use a cut-off threshold to prune edges with a low frequency. 174 for (unsigned I = 0, E = Size; I < E; ++I) { 175 DependencyEdge &Edge = N.OutgoingEdges[I]; 176 if (Edge.Frequency == Iterations) 177 continue; 178 double Factor = (double)Edge.Frequency / Iterations; 179 if (0.10 < Factor) 180 continue; 181 Nodes[Edge.ToIID].NumPredecessors--; 182 std::swap(Edge, N.OutgoingEdges[E - 1]); 183 --E; 184 ++NumPruned; 185 } 186 187 if (NumPruned) 188 N.OutgoingEdges.resize(Size - NumPruned); 189 } 190 } 191 192 void DependencyGraph::initializeRootSet( 193 SmallVectorImpl<unsigned> &RootSet) const { 194 for (unsigned I = 0, E = Nodes.size(); I < E; ++I) { 195 const DGNode &N = Nodes[I]; 196 if (N.NumPredecessors == 0 && !N.OutgoingEdges.empty()) 197 RootSet.emplace_back(I); 198 } 199 } 200 201 void DependencyGraph::propagateThroughEdges( 202 SmallVectorImpl<unsigned> &RootSet, unsigned Iterations) { 203 SmallVector<unsigned, 8> ToVisit; 204 205 // A critical sequence is computed as the longest path from a node of the 206 // RootSet to a leaf node (i.e. a node with no successors). The RootSet is 207 // composed of nodes with at least one successor, and no predecessors. 208 // 209 // Each node of the graph starts with an initial default cost of zero. The 210 // cost of a node is a measure of criticality: the higher the cost, the bigger 211 // is the performance impact. 212 // For register and memory dependencies, the cost is a function of the write 213 // latency as well as the actual delay (in cycles) caused to users. 214 // For processor resource dependencies, the cost is a function of the resource 215 // pressure. Resource interferences with low frequency values are ignored. 216 // 217 // This algorithm is very similar to a (reverse) Dijkstra. Every iteration of 218 // the inner loop selects (i.e. visits) a node N from a set of `unvisited 219 // nodes`, and then propagates the cost of N to all its neighbors. 220 // 221 // The `unvisited nodes` set initially contains all the nodes from the 222 // RootSet. A node N is added to the `unvisited nodes` if all its 223 // predecessors have been visited already. 224 // 225 // For simplicity, every node tracks the number of unvisited incoming edges in 226 // field `NumVisitedPredecessors`. When the value of that field drops to 227 // zero, then the corresponding node is added to a `ToVisit` set. 228 // 229 // At the end of every iteration of the outer loop, set `ToVisit` becomes our 230 // new `unvisited nodes` set. 231 // 232 // The algorithm terminates when the set of unvisited nodes (i.e. our RootSet) 233 // is empty. This algorithm works under the assumption that the graph is 234 // acyclic. 235 do { 236 for (unsigned IID : RootSet) { 237 const DGNode &N = Nodes[IID]; 238 for (const DependencyEdge &DepEdge : N.OutgoingEdges) { 239 unsigned ToIID = DepEdge.ToIID; 240 DGNode &To = Nodes[ToIID]; 241 uint64_t Cost = N.Cost + DepEdge.Dep.Cost; 242 // Check if this is the most expensive incoming edge seen so far. In 243 // case, update the total cost of the destination node (ToIID), as well 244 // its field `CriticalPredecessor`. 245 if (Cost > To.Cost) { 246 To.CriticalPredecessor = DepEdge; 247 To.Cost = Cost; 248 To.Depth = N.Depth + 1; 249 } 250 To.NumVisitedPredecessors++; 251 if (To.NumVisitedPredecessors == To.NumPredecessors) 252 ToVisit.emplace_back(ToIID); 253 } 254 } 255 256 std::swap(RootSet, ToVisit); 257 ToVisit.clear(); 258 } while (!RootSet.empty()); 259 } 260 261 void DependencyGraph::getCriticalSequence( 262 SmallVectorImpl<const DependencyEdge *> &Seq) const { 263 // At this stage, nodes of the graph have been already visited, and costs have 264 // been propagated through the edges (see method `propagateThroughEdges()`). 265 266 // Identify the node N with the highest cost in the graph. By construction, 267 // that node is the last instruction of our critical sequence. 268 // Field N.Depth would tell us the total length of the sequence. 269 // 270 // To obtain the sequence of critical edges, we simply follow the chain of critical 271 // predecessors starting from node N (field DGNode::CriticalPredecessor). 272 const auto It = std::max_element( 273 Nodes.begin(), Nodes.end(), 274 [](const DGNode &Lhs, const DGNode &Rhs) { return Lhs.Cost < Rhs.Cost; }); 275 unsigned IID = std::distance(Nodes.begin(), It); 276 Seq.resize(Nodes[IID].Depth); 277 for (unsigned I = Seq.size(), E = 0; I > E; --I) { 278 const DGNode &N = Nodes[IID]; 279 Seq[I - 1] = &N.CriticalPredecessor; 280 IID = N.CriticalPredecessor.FromIID; 281 } 282 } 283 284 void BottleneckAnalysis::printInstruction(formatted_raw_ostream &FOS, 285 const MCInst &MCI, 286 bool UseDifferentColor) const { 287 FOS.PadToColumn(14); 288 if (UseDifferentColor) 289 FOS.changeColor(raw_ostream::CYAN, true, false); 290 FOS << printInstructionString(MCI); 291 if (UseDifferentColor) 292 FOS.resetColor(); 293 } 294 295 void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const { 296 // Early exit if no bottlenecks were found during the simulation. 297 if (!SeenStallCycles || !BPI.PressureIncreaseCycles) 298 return; 299 300 SmallVector<const DependencyEdge *, 16> Seq; 301 DG.getCriticalSequence(Seq); 302 if (Seq.empty()) 303 return; 304 305 OS << "\nCritical sequence based on the simulation:\n\n"; 306 307 const DependencyEdge &FirstEdge = *Seq[0]; 308 ArrayRef<llvm::MCInst> Source = getSource(); 309 unsigned FromIID = FirstEdge.FromIID % Source.size(); 310 unsigned ToIID = FirstEdge.ToIID % Source.size(); 311 bool IsLoopCarried = FromIID >= ToIID; 312 313 formatted_raw_ostream FOS(OS); 314 FOS.PadToColumn(14); 315 FOS << "Instruction"; 316 FOS.PadToColumn(58); 317 FOS << "Dependency Information"; 318 319 bool HasColors = FOS.has_colors(); 320 321 unsigned CurrentIID = 0; 322 if (IsLoopCarried) { 323 FOS << "\n +----< " << FromIID << "."; 324 printInstruction(FOS, Source[FromIID], HasColors); 325 FOS << "\n |\n | < loop carried > \n |"; 326 } else { 327 while (CurrentIID < FromIID) { 328 FOS << "\n " << CurrentIID << "."; 329 printInstruction(FOS, Source[CurrentIID]); 330 CurrentIID++; 331 } 332 333 FOS << "\n +----< " << CurrentIID << "."; 334 printInstruction(FOS, Source[CurrentIID], HasColors); 335 CurrentIID++; 336 } 337 338 for (const DependencyEdge *&DE : Seq) { 339 ToIID = DE->ToIID % Source.size(); 340 unsigned LastIID = CurrentIID > ToIID ? Source.size() : ToIID; 341 342 while (CurrentIID < LastIID) { 343 FOS << "\n | " << CurrentIID << "."; 344 printInstruction(FOS, Source[CurrentIID]); 345 CurrentIID++; 346 } 347 348 if (CurrentIID == ToIID) { 349 FOS << "\n +----> " << ToIID << "."; 350 printInstruction(FOS, Source[CurrentIID], HasColors); 351 } else { 352 FOS << "\n |\n | < loop carried > \n |" 353 << "\n +----> " << ToIID << "."; 354 printInstruction(FOS, Source[ToIID], HasColors); 355 } 356 FOS.PadToColumn(58); 357 358 const DependencyEdge::Dependency &Dep = DE->Dep; 359 if (HasColors) 360 FOS.changeColor(raw_ostream::SAVEDCOLOR, true, false); 361 362 if (Dep.Type == DependencyEdge::DT_REGISTER) { 363 FOS << "## REGISTER dependency: "; 364 if (HasColors) 365 FOS.changeColor(raw_ostream::MAGENTA, true, false); 366 getInstPrinter().printRegName(FOS, Dep.ResourceOrRegID); 367 } else if (Dep.Type == DependencyEdge::DT_MEMORY) { 368 FOS << "## MEMORY dependency."; 369 } else { 370 assert(Dep.Type == DependencyEdge::DT_RESOURCE && 371 "Unsupported dependency type!"); 372 FOS << "## RESOURCE interference: "; 373 if (HasColors) 374 FOS.changeColor(raw_ostream::MAGENTA, true, false); 375 FOS << Tracker.resolveResourceName(Dep.ResourceOrRegID); 376 if (HasColors) { 377 FOS.resetColor(); 378 FOS.changeColor(raw_ostream::SAVEDCOLOR, true, false); 379 } 380 FOS << " [ probability: " << ((DE->Frequency * 100) / Iterations) 381 << "% ]"; 382 } 383 if (HasColors) 384 FOS.resetColor(); 385 ++CurrentIID; 386 } 387 388 while (CurrentIID < Source.size()) { 389 FOS << "\n " << CurrentIID << "."; 390 printInstruction(FOS, Source[CurrentIID]); 391 CurrentIID++; 392 } 393 394 FOS << '\n'; 395 FOS.flush(); 396 } 397 398 #ifndef NDEBUG 399 void DependencyGraph::dump(raw_ostream &OS, MCInstPrinter &MCIP) const { 400 OS << "\nREG DEPS\n"; 401 for (const DGNode &Node : Nodes) 402 for (const DependencyEdge &DE : Node.OutgoingEdges) 403 if (DE.Dep.Type == DependencyEdge::DT_REGISTER) 404 dumpDependencyEdge(OS, DE, MCIP); 405 406 OS << "\nMEM DEPS\n"; 407 for (const DGNode &Node : Nodes) 408 for (const DependencyEdge &DE : Node.OutgoingEdges) 409 if (DE.Dep.Type == DependencyEdge::DT_MEMORY) 410 dumpDependencyEdge(OS, DE, MCIP); 411 412 OS << "\nRESOURCE DEPS\n"; 413 for (const DGNode &Node : Nodes) 414 for (const DependencyEdge &DE : Node.OutgoingEdges) 415 if (DE.Dep.Type == DependencyEdge::DT_RESOURCE) 416 dumpDependencyEdge(OS, DE, MCIP); 417 } 418 #endif // NDEBUG 419 420 void DependencyGraph::addDependency(unsigned From, unsigned To, 421 DependencyEdge::Dependency &&Dep) { 422 DGNode &NodeFrom = Nodes[From]; 423 DGNode &NodeTo = Nodes[To]; 424 SmallVectorImpl<DependencyEdge> &Vec = NodeFrom.OutgoingEdges; 425 426 auto It = find_if(Vec, [To, Dep](DependencyEdge &DE) { 427 return DE.ToIID == To && DE.Dep.ResourceOrRegID == Dep.ResourceOrRegID; 428 }); 429 430 if (It != Vec.end()) { 431 It->Dep.Cost += Dep.Cost; 432 It->Frequency++; 433 return; 434 } 435 436 DependencyEdge DE = {Dep, From, To, 1}; 437 Vec.emplace_back(DE); 438 NodeTo.NumPredecessors++; 439 } 440 441 BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo &sti, 442 MCInstPrinter &Printer, 443 ArrayRef<MCInst> S, unsigned NumIter) 444 : InstructionView(sti, Printer, S), Tracker(sti.getSchedModel()), 445 DG(S.size() * 3), Iterations(NumIter), TotalCycles(0), 446 PressureIncreasedBecauseOfResources(false), 447 PressureIncreasedBecauseOfRegisterDependencies(false), 448 PressureIncreasedBecauseOfMemoryDependencies(false), 449 SeenStallCycles(false), BPI() {} 450 451 void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To, 452 unsigned RegID, unsigned Cost) { 453 bool IsLoopCarried = From >= To; 454 unsigned SourceSize = getSource().size(); 455 if (IsLoopCarried) { 456 DG.addRegisterDep(From, To + SourceSize, RegID, Cost); 457 DG.addRegisterDep(From + SourceSize, To + (SourceSize * 2), RegID, Cost); 458 return; 459 } 460 DG.addRegisterDep(From + SourceSize, To + SourceSize, RegID, Cost); 461 } 462 463 void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To, 464 unsigned Cost) { 465 bool IsLoopCarried = From >= To; 466 unsigned SourceSize = getSource().size(); 467 if (IsLoopCarried) { 468 DG.addMemoryDep(From, To + SourceSize, Cost); 469 DG.addMemoryDep(From + SourceSize, To + (SourceSize * 2), Cost); 470 return; 471 } 472 DG.addMemoryDep(From + SourceSize, To + SourceSize, Cost); 473 } 474 475 void BottleneckAnalysis::addResourceDep(unsigned From, unsigned To, 476 uint64_t Mask, unsigned Cost) { 477 bool IsLoopCarried = From >= To; 478 unsigned SourceSize = getSource().size(); 479 if (IsLoopCarried) { 480 DG.addResourceDep(From, To + SourceSize, Mask, Cost); 481 DG.addResourceDep(From + SourceSize, To + (SourceSize * 2), Mask, Cost); 482 return; 483 } 484 DG.addResourceDep(From + SourceSize, To + SourceSize, Mask, Cost); 485 } 486 487 void BottleneckAnalysis::onEvent(const HWInstructionEvent &Event) { 488 const unsigned IID = Event.IR.getSourceIndex(); 489 if (Event.Type == HWInstructionEvent::Dispatched) { 490 Tracker.onInstructionDispatched(IID); 491 return; 492 } 493 if (Event.Type == HWInstructionEvent::Executed) { 494 Tracker.onInstructionExecuted(IID); 495 return; 496 } 497 498 if (Event.Type != HWInstructionEvent::Issued) 499 return; 500 501 ArrayRef<llvm::MCInst> Source = getSource(); 502 const Instruction &IS = *Event.IR.getInstruction(); 503 unsigned To = IID % Source.size(); 504 505 unsigned Cycles = 2 * Tracker.getResourcePressureCycles(IID); 506 uint64_t ResourceMask = IS.getCriticalResourceMask(); 507 SmallVector<std::pair<unsigned, unsigned>, 4> Users; 508 while (ResourceMask) { 509 uint64_t Current = ResourceMask & (-ResourceMask); 510 Tracker.getResourceUsers(Current, Users); 511 for (const std::pair<unsigned, unsigned> &U : Users) 512 addResourceDep(U.first % Source.size(), To, Current, U.second + Cycles); 513 Users.clear(); 514 ResourceMask ^= Current; 515 } 516 517 const CriticalDependency &RegDep = IS.getCriticalRegDep(); 518 if (RegDep.Cycles) { 519 Cycles = RegDep.Cycles + 2 * Tracker.getRegisterPressureCycles(IID); 520 unsigned From = RegDep.IID % Source.size(); 521 addRegisterDep(From, To, RegDep.RegID, Cycles); 522 } 523 524 const CriticalDependency &MemDep = IS.getCriticalMemDep(); 525 if (MemDep.Cycles) { 526 Cycles = MemDep.Cycles + 2 * Tracker.getMemoryPressureCycles(IID); 527 unsigned From = MemDep.IID % Source.size(); 528 addMemoryDep(From, To, Cycles); 529 } 530 531 Tracker.handleInstructionIssuedEvent( 532 static_cast<const HWInstructionIssuedEvent &>(Event)); 533 534 // Check if this is the last simulated instruction. 535 if (IID == ((Iterations * Source.size()) - 1)) 536 DG.finalizeGraph(Iterations); 537 } 538 539 void BottleneckAnalysis::onEvent(const HWPressureEvent &Event) { 540 assert(Event.Reason != HWPressureEvent::INVALID && 541 "Unexpected invalid event!"); 542 543 Tracker.handlePressureEvent(Event); 544 545 switch (Event.Reason) { 546 default: 547 break; 548 549 case HWPressureEvent::RESOURCES: 550 PressureIncreasedBecauseOfResources = true; 551 break; 552 case HWPressureEvent::REGISTER_DEPS: 553 PressureIncreasedBecauseOfRegisterDependencies = true; 554 break; 555 case HWPressureEvent::MEMORY_DEPS: 556 PressureIncreasedBecauseOfMemoryDependencies = true; 557 break; 558 } 559 } 560 561 void BottleneckAnalysis::onCycleEnd() { 562 ++TotalCycles; 563 564 bool PressureIncreasedBecauseOfDataDependencies = 565 PressureIncreasedBecauseOfRegisterDependencies || 566 PressureIncreasedBecauseOfMemoryDependencies; 567 if (!PressureIncreasedBecauseOfResources && 568 !PressureIncreasedBecauseOfDataDependencies) 569 return; 570 571 ++BPI.PressureIncreaseCycles; 572 if (PressureIncreasedBecauseOfRegisterDependencies) 573 ++BPI.RegisterDependencyCycles; 574 if (PressureIncreasedBecauseOfMemoryDependencies) 575 ++BPI.MemoryDependencyCycles; 576 if (PressureIncreasedBecauseOfDataDependencies) 577 ++BPI.DataDependencyCycles; 578 if (PressureIncreasedBecauseOfResources) 579 ++BPI.ResourcePressureCycles; 580 PressureIncreasedBecauseOfResources = false; 581 PressureIncreasedBecauseOfRegisterDependencies = false; 582 PressureIncreasedBecauseOfMemoryDependencies = false; 583 } 584 585 void BottleneckAnalysis::printBottleneckHints(raw_ostream &OS) const { 586 if (!SeenStallCycles || !BPI.PressureIncreaseCycles) { 587 OS << "\n\nNo resource or data dependency bottlenecks discovered.\n"; 588 return; 589 } 590 591 double PressurePerCycle = 592 (double)BPI.PressureIncreaseCycles * 100 / TotalCycles; 593 double ResourcePressurePerCycle = 594 (double)BPI.ResourcePressureCycles * 100 / TotalCycles; 595 double DDPerCycle = (double)BPI.DataDependencyCycles * 100 / TotalCycles; 596 double RegDepPressurePerCycle = 597 (double)BPI.RegisterDependencyCycles * 100 / TotalCycles; 598 double MemDepPressurePerCycle = 599 (double)BPI.MemoryDependencyCycles * 100 / TotalCycles; 600 601 OS << "\n\nCycles with backend pressure increase [ " 602 << format("%.2f", floor((PressurePerCycle * 100) + 0.5) / 100) << "% ]"; 603 604 OS << "\nThroughput Bottlenecks: " 605 << "\n Resource Pressure [ " 606 << format("%.2f", floor((ResourcePressurePerCycle * 100) + 0.5) / 100) 607 << "% ]"; 608 609 if (BPI.PressureIncreaseCycles) { 610 ArrayRef<unsigned> Distribution = Tracker.getResourcePressureDistribution(); 611 const MCSchedModel &SM = getSubTargetInfo().getSchedModel(); 612 for (unsigned I = 0, E = Distribution.size(); I < E; ++I) { 613 unsigned ResourceCycles = Distribution[I]; 614 if (ResourceCycles) { 615 double Frequency = (double)ResourceCycles * 100 / TotalCycles; 616 const MCProcResourceDesc &PRDesc = *SM.getProcResource(I); 617 OS << "\n - " << PRDesc.Name << " [ " 618 << format("%.2f", floor((Frequency * 100) + 0.5) / 100) << "% ]"; 619 } 620 } 621 } 622 623 OS << "\n Data Dependencies: [ " 624 << format("%.2f", floor((DDPerCycle * 100) + 0.5) / 100) << "% ]"; 625 OS << "\n - Register Dependencies [ " 626 << format("%.2f", floor((RegDepPressurePerCycle * 100) + 0.5) / 100) 627 << "% ]"; 628 OS << "\n - Memory Dependencies [ " 629 << format("%.2f", floor((MemDepPressurePerCycle * 100) + 0.5) / 100) 630 << "% ]\n"; 631 } 632 633 void BottleneckAnalysis::printView(raw_ostream &OS) const { 634 std::string Buffer; 635 raw_string_ostream TempStream(Buffer); 636 printBottleneckHints(TempStream); 637 TempStream.flush(); 638 OS << Buffer; 639 printCriticalSequence(OS); 640 } 641 642 } // namespace mca. 643 } // namespace llvm 644