From a3f2e483dda162cb5cd284c87bc09d72812b4dd4 Mon Sep 17 00:00:00 2001 From: Andrea Di Biagio Date: Tue, 20 Mar 2018 18:20:39 +0000 Subject: [PATCH] [llvm-mca] Move the logic that computes the scheduler's queue usage to the BackendStatistics view. This patch introduces two new callbacks in the event listener interface to handle the "buffered resource reserved" event and the "buffered resource released" event. Every time a buffered resource is used, an event is generated. Before this patch, the Scheduler (with the help of the ResourceManager) was responsible for tracking the scheduler's queue usage. However, that design forced the Scheduler to 'publish' scheduler's queue pressure information through the Backend interface. The goal of this patch is to break the dependency between the BackendStatistics view, and the Backend. Now the Scheduler knows how to notify "buffer reserved/released" events. The scheduler's queue usage analysis has been moved to the BackendStatistics. Differential Revision: https://reviews.llvm.org/D44686 llvm-svn: 328011 --- llvm/tools/llvm-mca/Backend.cpp | 10 +++++ llvm/tools/llvm-mca/Backend.h | 5 +-- llvm/tools/llvm-mca/BackendStatistics.cpp | 41 ++++++++++++----- llvm/tools/llvm-mca/BackendStatistics.h | 28 +++++++++--- llvm/tools/llvm-mca/HWEventListener.h | 5 +++ llvm/tools/llvm-mca/Scheduler.cpp | 54 ++++++++++++++++------- llvm/tools/llvm-mca/Scheduler.h | 35 +++++---------- 7 files changed, 119 insertions(+), 59 deletions(-) diff --git a/llvm/tools/llvm-mca/Backend.cpp b/llvm/tools/llvm-mca/Backend.cpp index cad7db3c438eaf..7d694667d7f335 100644 --- a/llvm/tools/llvm-mca/Backend.cpp +++ b/llvm/tools/llvm-mca/Backend.cpp @@ -75,6 +75,16 @@ void Backend::notifyResourceAvailable(const ResourceRef &RR) { Listener->onResourceAvailable(RR); } +void Backend::notifyReservedBuffers(ArrayRef Buffers) { + for (HWEventListener *Listener : Listeners) + Listener->onReservedBuffers(Buffers); +} + +void Backend::notifyReleasedBuffers(ArrayRef Buffers) { + for (HWEventListener *Listener : Listeners) + Listener->onReleasedBuffers(Buffers); +} + void Backend::notifyCycleEnd(unsigned Cycle) { DEBUG(dbgs() << "[E] Cycle end: " << Cycle << "\n\n"); for (HWEventListener *Listener : Listeners) diff --git a/llvm/tools/llvm-mca/Backend.h b/llvm/tools/llvm-mca/Backend.h index e9cf529682ed6f..513904bf6c648b 100644 --- a/llvm/tools/llvm-mca/Backend.h +++ b/llvm/tools/llvm-mca/Backend.h @@ -94,15 +94,14 @@ class Backend { unsigned getMaxUsedRegisterMappings() const { return DU->getMaxUsedRegisterMappings(); } - void getBuffersUsage(std::vector &Usage) const { - return HWS->getBuffersUsage(Usage); - } void addEventListener(HWEventListener *Listener); void notifyCycleBegin(unsigned Cycle); void notifyInstructionEvent(const HWInstructionEvent &Event); void notifyStallEvent(const HWStallEvent &Event); void notifyResourceAvailable(const ResourceRef &RR); + void notifyReservedBuffers(llvm::ArrayRef Buffers); + void notifyReleasedBuffers(llvm::ArrayRef Buffers); void notifyCycleEnd(unsigned Cycle); }; diff --git a/llvm/tools/llvm-mca/BackendStatistics.cpp b/llvm/tools/llvm-mca/BackendStatistics.cpp index 274f79913f2183..64b8be11e4d6d3 100644 --- a/llvm/tools/llvm-mca/BackendStatistics.cpp +++ b/llvm/tools/llvm-mca/BackendStatistics.cpp @@ -36,6 +36,29 @@ void BackendStatistics::onInstructionEvent(const HWInstructionEvent &Event) { } } +void BackendStatistics::onReservedBuffers(ArrayRef Buffers) { + for (const unsigned Buffer : Buffers) { + if (BufferedResources.find(Buffer) != BufferedResources.end()) { + BufferUsage &BU = BufferedResources[Buffer]; + BU.SlotsInUse++; + BU.MaxUsedSlots = std::max(BU.MaxUsedSlots, BU.SlotsInUse); + continue; + } + + BufferedResources.insert( + std::pair(Buffer, {1U, 1U})); + } +} + +void BackendStatistics::onReleasedBuffers(ArrayRef Buffers) { + for (const unsigned Buffer : Buffers) { + assert(BufferedResources.find(Buffer) != BufferedResources.end() && + "Buffered resource not in map?"); + BufferUsage &BU = BufferedResources[Buffer]; + BU.SlotsInUse--; + } +} + void BackendStatistics::printRetireUnitStatistics(llvm::raw_ostream &OS) const { std::string Buffer; raw_string_ostream TempStream(Buffer); @@ -126,15 +149,13 @@ void BackendStatistics::printDispatchStalls(raw_ostream &OS) const { OS << Buffer; } -void BackendStatistics::printSchedulerUsage( - raw_ostream &OS, const MCSchedModel &SM, - const ArrayRef &Usage) const { - +void BackendStatistics::printSchedulerUsage(raw_ostream &OS, + const MCSchedModel &SM) const { std::string Buffer; raw_string_ostream TempStream(Buffer); TempStream << "\n\nScheduler's queue usage:\n"; // Early exit if no buffered resources were consumed. - if (Usage.empty()) { + if (BufferedResources.empty()) { TempStream << "No scheduler resources used.\n"; TempStream.flush(); OS << Buffer; @@ -143,17 +164,15 @@ void BackendStatistics::printSchedulerUsage( for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) { const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); - if (!ProcResource.BufferSize) + if (ProcResource.BufferSize <= 0) continue; - for (const BufferUsageEntry &Entry : Usage) - if (I == Entry.first) - TempStream << ProcResource.Name << ", " << Entry.second << '/' - << ProcResource.BufferSize << '\n'; + const BufferUsage &BU = BufferedResources.lookup(I); + TempStream << ProcResource.Name << ", " << BU.MaxUsedSlots << '/' + << ProcResource.BufferSize << '\n'; } TempStream.flush(); OS << Buffer; } - } // namespace mca diff --git a/llvm/tools/llvm-mca/BackendStatistics.h b/llvm/tools/llvm-mca/BackendStatistics.h index dacf5b0cac1071..0def7226faeba6 100644 --- a/llvm/tools/llvm-mca/BackendStatistics.h +++ b/llvm/tools/llvm-mca/BackendStatistics.h @@ -85,6 +85,17 @@ class BackendStatistics : public View { // is one counter for every generic stall kind (see class HWStallEvent). llvm::SmallVector HWStalls; + // Tracks the usage of a scheduler's queue. + struct BufferUsage { + unsigned SlotsInUse; + unsigned MaxUsedSlots; + }; + + // There is a map entry for each buffered resource in the scheduling model. + // Every time a buffer is consumed/freed, this view updates the corresponding + // entry. + llvm::DenseMap BufferedResources; + void updateHistograms() { DispatchGroupSizePerCycle[NumDispatched]++; IssuedPerCycle[NumIssued]++; @@ -107,8 +118,8 @@ class BackendStatistics : public View { unsigned Cycles) const; void printIssuePerCycle(const Histogram &IssuePerCycle, unsigned TotalCycles) const; - void printSchedulerUsage(llvm::raw_ostream &OS, const llvm::MCSchedModel &SM, - const llvm::ArrayRef &Usage) const; + void printSchedulerUsage(llvm::raw_ostream &OS, + const llvm::MCSchedModel &SM) const; public: BackendStatistics(const Backend &backend, const llvm::MCSubtargetInfo &sti) @@ -126,6 +137,14 @@ class BackendStatistics : public View { HWStalls[Event.Type]++; } + // Increases the number of used scheduler queue slots of every buffered + // resource in the Buffers set. + void onReservedBuffers(llvm::ArrayRef Buffers); + + // Decreases by one the number of used scheduler queue slots of every + // buffered resource in the Buffers set. + void onReleasedBuffers(llvm::ArrayRef Buffers); + void printView(llvm::raw_ostream &OS) const override { printDispatchStalls(OS); printRATStatistics(OS, B.getTotalRegisterMappingsCreated(), @@ -134,12 +153,9 @@ class BackendStatistics : public View { printSchedulerStatistics(OS); printRetireUnitStatistics(OS); - std::vector Usage; - B.getBuffersUsage(Usage); - printSchedulerUsage(OS, STI.getSchedModel(), Usage); + printSchedulerUsage(OS, STI.getSchedModel()); } }; - } // namespace mca #endif diff --git a/llvm/tools/llvm-mca/HWEventListener.h b/llvm/tools/llvm-mca/HWEventListener.h index e5fb79414e0d7b..9b8ba124ce48ee 100644 --- a/llvm/tools/llvm-mca/HWEventListener.h +++ b/llvm/tools/llvm-mca/HWEventListener.h @@ -104,6 +104,11 @@ class HWEventListener { using ResourceRef = std::pair; virtual void onResourceAvailable(const ResourceRef &RRef) {} + // Events generated by the Scheduler when buffered resources are + // consumed/freed. + virtual void onReservedBuffers(llvm::ArrayRef Buffers) {} + virtual void onReleasedBuffers(llvm::ArrayRef Buffers) {} + virtual ~HWEventListener() {} private: diff --git a/llvm/tools/llvm-mca/Scheduler.cpp b/llvm/tools/llvm-mca/Scheduler.cpp index 4d7f1a222ca86f..8a42cd0877a863 100644 --- a/llvm/tools/llvm-mca/Scheduler.cpp +++ b/llvm/tools/llvm-mca/Scheduler.cpp @@ -181,7 +181,6 @@ bool ResourceManager::mustIssueImmediately(const InstrDesc &Desc) { void ResourceManager::issueInstruction( unsigned Index, const InstrDesc &Desc, SmallVectorImpl> &Pipes) { - releaseBuffers(Desc.Buffers); for (const std::pair &R : Desc.Resources) { const CycleSegment &CS = R.second.CS; if (!CS.size()) { @@ -252,11 +251,14 @@ void Scheduler::scheduleInstruction(unsigned Idx, Instruction &MCIS) { // Consume entries in the reservation stations. const InstrDesc &Desc = MCIS.getDesc(); - // Reserve a slot in each buffered resource. Also, mark units with - // BufferSize=0 as reserved. Resources with a buffer size of zero will only be - // released after MCIS is issued, and all the ResourceCycles for those units - // have been consumed. - Resources->reserveBuffers(Desc.Buffers); + if (!Desc.Buffers.empty()) { + // Reserve a slot in each buffered resource. Also, mark units with + // BufferSize=0 as reserved. Resources with a buffer size of zero will only + // be released after MCIS is issued, and all the ResourceCycles for those + // units have been consumed. + Resources->reserveBuffers(Desc.Buffers); + notifyReservedBuffers(Desc.Buffers); + } bool MayLoad = Desc.MayLoad; bool MayStore = Desc.MayStore; @@ -331,6 +333,13 @@ Scheduler::Event Scheduler::canBeDispatched(const InstrDesc &Desc) const { } void Scheduler::issueInstruction(Instruction &IS, unsigned InstrIndex) { + const InstrDesc &D = IS.getDesc(); + + if (!D.Buffers.empty()) { + Resources->releaseBuffers(D.Buffers); + notifyReleasedBuffers(D.Buffers); + } + // Issue the instruction and collect all the consumed resources // into a vector. That vector is then used to notify the listener. // Most instructions consume very few resurces (typically one or @@ -338,8 +347,6 @@ void Scheduler::issueInstruction(Instruction &IS, unsigned InstrIndex) { // initialize its capacity to 4. This should address the majority of // the cases. SmallVector, 4> UsedResources; - - const InstrDesc &D = IS.getDesc(); Resources->issueInstruction(InstrIndex, D, UsedResources); // Notify the instruction that it started executing. // This updates the internal state of each write. @@ -417,13 +424,14 @@ void Scheduler::updateIssuedQueue() { void Scheduler::notifyInstructionIssued( unsigned Index, const ArrayRef> &Used) { - DEBUG(dbgs() << "[E] Instruction Issued: " << Index << '\n'; - for (const std::pair &Resource - : Used) { - dbgs() << "[E] Resource Used: [" << Resource.first.first << '.' - << Resource.first.second << "]\n"; - dbgs() << " cycles: " << Resource.second << '\n'; - }); + DEBUG({ + dbgs() << "[E] Instruction Issued: " << Index << '\n'; + for (const std::pair &Resource : Used) { + dbgs() << "[E] Resource Used: [" << Resource.first.first << '.' + << Resource.first.second << "]\n"; + dbgs() << " cycles: " << Resource.second << '\n'; + } + }); Owner->notifyInstructionEvent(HWInstructionIssuedEvent(Index, Used)); } @@ -446,4 +454,20 @@ void Scheduler::notifyInstructionReady(unsigned Index) { void Scheduler::notifyResourceAvailable(const ResourceRef &RR) { Owner->notifyResourceAvailable(RR); } + +void Scheduler::notifyReservedBuffers(ArrayRef Buffers) { + SmallVector BufferIDs(Buffers.begin(), Buffers.end()); + std::transform( + Buffers.begin(), Buffers.end(), BufferIDs.begin(), + [&](uint64_t Op) { return Resources->resolveResourceMask(Op); }); + Owner->notifyReservedBuffers(BufferIDs); +} + +void Scheduler::notifyReleasedBuffers(ArrayRef Buffers) { + SmallVector BufferIDs(Buffers.begin(), Buffers.end()); + std::transform( + Buffers.begin(), Buffers.end(), BufferIDs.begin(), + [&](uint64_t Op) { return Resources->resolveResourceMask(Op); }); + Owner->notifyReleasedBuffers(BufferIDs); +} } // namespace mca diff --git a/llvm/tools/llvm-mca/Scheduler.h b/llvm/tools/llvm-mca/Scheduler.h index c74d3f1e196607..93c5295e8d599d 100644 --- a/llvm/tools/llvm-mca/Scheduler.h +++ b/llvm/tools/llvm-mca/Scheduler.h @@ -138,11 +138,6 @@ class ResourceState { // Available slots in the buffer (zero, if this is not a buffered resource). unsigned AvailableSlots; - // Maximum number of buffer slots seen used during one cycle. - // This helps tracking dynamic dispatch stalls caused by the lack of - // entries in the scheduler's queue. - unsigned MaxUsedSlots; - // True if this is resource is currently unavailable. // An instruction may "reserve" a resource for a number of cycles. // During those cycles, the reserved resource cannot be used for other @@ -182,14 +177,12 @@ class ResourceState { ReadyMask = ResourceSizeMask; BufferSize = Desc.BufferSize; AvailableSlots = BufferSize == -1 ? 0U : static_cast(BufferSize); - MaxUsedSlots = 0; Unavailable = false; } unsigned getProcResourceID() const { return ProcResourceDescIndex; } uint64_t getResourceMask() const { return ResourceMask; } int getBufferSize() const { return BufferSize; } - unsigned getMaxUsedSlots() const { return MaxUsedSlots; } bool isBuffered() const { return BufferSize > 0; } bool isInOrder() const { return BufferSize == 1; } @@ -244,8 +237,6 @@ class ResourceState { void reserveBuffer() { if (AvailableSlots) AvailableSlots--; - unsigned UsedSlots = static_cast(BufferSize) - AvailableSlots; - MaxUsedSlots = std::max(MaxUsedSlots, UsedSlots); } void releaseBuffer() { @@ -339,8 +330,12 @@ class ResourceManager { // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if // there are enough available slots in the buffers. - ResourceStateEvent - canBeDispatched(const llvm::ArrayRef Buffers) const; + ResourceStateEvent canBeDispatched(llvm::ArrayRef Buffers) const; + + // Return the processor resource identifier associated to this Mask. + unsigned resolveResourceMask(uint64_t Mask) const { + return Resources.find(Mask)->second->getProcResourceID(); + } // Consume a slot in every buffered resource from array 'Buffers'. Resource // units that are dispatch hazards (i.e. BufferSize=0) are marked as reserved. @@ -372,15 +367,6 @@ class ResourceManager { void cycleEvent(llvm::SmallVectorImpl &ResourcesFreed); - void getBuffersUsage(std::vector &Usage) const { - for (const std::pair &Resource : Resources) { - const ResourceState &RS = *Resource.second; - if (RS.isBuffered()) - Usage.emplace_back(std::pair(RS.getProcResourceID(), - RS.getMaxUsedSlots())); - } - } - #ifndef NDEBUG void dump() const { for (const std::pair &Resource : Resources) @@ -439,6 +425,11 @@ class Scheduler { void notifyInstructionReady(unsigned Index); void notifyResourceAvailable(const ResourceRef &RR); + // Notify the Backend that buffered resources were consumed. + void notifyReservedBuffers(llvm::ArrayRef Buffers); + // Notify the Backend that buffered resources were freed. + void notifyReleasedBuffers(llvm::ArrayRef Buffers); + /// Issue instructions from the ready queue by giving priority to older /// instructions. void issue(); @@ -498,10 +489,6 @@ class Scheduler { void cycleEvent(unsigned Cycle); - void getBuffersUsage(std::vector &Usage) const { - Resources->getBuffersUsage(Usage); - } - #ifndef NDEBUG void dump() const; #endif