Skip to content

Commit

Permalink
[llvm-mca] Move the logic that computes the scheduler's queue usage t…
Browse files Browse the repository at this point in the history
…o 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
  • Loading branch information
Andrea Di Biagio authored and Andrea Di Biagio committed Mar 20, 2018
1 parent 7baac21 commit a3f2e48
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 59 deletions.
10 changes: 10 additions & 0 deletions llvm/tools/llvm-mca/Backend.cpp
Expand Up @@ -75,6 +75,16 @@ void Backend::notifyResourceAvailable(const ResourceRef &RR) {
Listener->onResourceAvailable(RR);
}

void Backend::notifyReservedBuffers(ArrayRef<unsigned> Buffers) {
for (HWEventListener *Listener : Listeners)
Listener->onReservedBuffers(Buffers);
}

void Backend::notifyReleasedBuffers(ArrayRef<unsigned> 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)
Expand Down
5 changes: 2 additions & 3 deletions llvm/tools/llvm-mca/Backend.h
Expand Up @@ -94,15 +94,14 @@ class Backend {
unsigned getMaxUsedRegisterMappings() const {
return DU->getMaxUsedRegisterMappings();
}
void getBuffersUsage(std::vector<BufferUsageEntry> &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<unsigned> Buffers);
void notifyReleasedBuffers(llvm::ArrayRef<unsigned> Buffers);
void notifyCycleEnd(unsigned Cycle);
};

Expand Down
41 changes: 30 additions & 11 deletions llvm/tools/llvm-mca/BackendStatistics.cpp
Expand Up @@ -36,6 +36,29 @@ void BackendStatistics::onInstructionEvent(const HWInstructionEvent &Event) {
}
}

void BackendStatistics::onReservedBuffers(ArrayRef<unsigned> 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<unsigned, BufferUsage>(Buffer, {1U, 1U}));
}
}

void BackendStatistics::onReleasedBuffers(ArrayRef<unsigned> 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);
Expand Down Expand Up @@ -126,15 +149,13 @@ void BackendStatistics::printDispatchStalls(raw_ostream &OS) const {
OS << Buffer;
}

void BackendStatistics::printSchedulerUsage(
raw_ostream &OS, const MCSchedModel &SM,
const ArrayRef<BufferUsageEntry> &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;
Expand All @@ -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
28 changes: 22 additions & 6 deletions llvm/tools/llvm-mca/BackendStatistics.h
Expand Up @@ -85,6 +85,17 @@ class BackendStatistics : public View {
// is one counter for every generic stall kind (see class HWStallEvent).
llvm::SmallVector<unsigned, 8> 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<unsigned, BufferUsage> BufferedResources;

void updateHistograms() {
DispatchGroupSizePerCycle[NumDispatched]++;
IssuedPerCycle[NumIssued]++;
Expand All @@ -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<BufferUsageEntry> &Usage) const;
void printSchedulerUsage(llvm::raw_ostream &OS,
const llvm::MCSchedModel &SM) const;

public:
BackendStatistics(const Backend &backend, const llvm::MCSubtargetInfo &sti)
Expand All @@ -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<unsigned> Buffers);

// Decreases by one the number of used scheduler queue slots of every
// buffered resource in the Buffers set.
void onReleasedBuffers(llvm::ArrayRef<unsigned> Buffers);

void printView(llvm::raw_ostream &OS) const override {
printDispatchStalls(OS);
printRATStatistics(OS, B.getTotalRegisterMappingsCreated(),
Expand All @@ -134,12 +153,9 @@ class BackendStatistics : public View {
printSchedulerStatistics(OS);
printRetireUnitStatistics(OS);

std::vector<BufferUsageEntry> Usage;
B.getBuffersUsage(Usage);
printSchedulerUsage(OS, STI.getSchedModel(), Usage);
printSchedulerUsage(OS, STI.getSchedModel());
}
};

} // namespace mca

#endif
5 changes: 5 additions & 0 deletions llvm/tools/llvm-mca/HWEventListener.h
Expand Up @@ -104,6 +104,11 @@ class HWEventListener {
using ResourceRef = std::pair<uint64_t, uint64_t>;
virtual void onResourceAvailable(const ResourceRef &RRef) {}

// Events generated by the Scheduler when buffered resources are
// consumed/freed.
virtual void onReservedBuffers(llvm::ArrayRef<unsigned> Buffers) {}
virtual void onReleasedBuffers(llvm::ArrayRef<unsigned> Buffers) {}

virtual ~HWEventListener() {}

private:
Expand Down
54 changes: 39 additions & 15 deletions llvm/tools/llvm-mca/Scheduler.cpp
Expand Up @@ -181,7 +181,6 @@ bool ResourceManager::mustIssueImmediately(const InstrDesc &Desc) {
void ResourceManager::issueInstruction(
unsigned Index, const InstrDesc &Desc,
SmallVectorImpl<std::pair<ResourceRef, unsigned>> &Pipes) {
releaseBuffers(Desc.Buffers);
for (const std::pair<uint64_t, ResourceUsage> &R : Desc.Resources) {
const CycleSegment &CS = R.second.CS;
if (!CS.size()) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -331,15 +333,20 @@ 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
// two resources). We use a small vector here, and conservatively
// initialize its capacity to 4. This should address the majority of
// the cases.
SmallVector<std::pair<ResourceRef, unsigned>, 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.
Expand Down Expand Up @@ -417,13 +424,14 @@ void Scheduler::updateIssuedQueue() {

void Scheduler::notifyInstructionIssued(
unsigned Index, const ArrayRef<std::pair<ResourceRef, unsigned>> &Used) {
DEBUG(dbgs() << "[E] Instruction Issued: " << Index << '\n';
for (const std::pair<ResourceRef, unsigned> &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<ResourceRef, unsigned> &Resource : Used) {
dbgs() << "[E] Resource Used: [" << Resource.first.first << '.'
<< Resource.first.second << "]\n";
dbgs() << " cycles: " << Resource.second << '\n';
}
});
Owner->notifyInstructionEvent(HWInstructionIssuedEvent(Index, Used));
}

Expand All @@ -446,4 +454,20 @@ void Scheduler::notifyInstructionReady(unsigned Index) {
void Scheduler::notifyResourceAvailable(const ResourceRef &RR) {
Owner->notifyResourceAvailable(RR);
}

void Scheduler::notifyReservedBuffers(ArrayRef<uint64_t> Buffers) {
SmallVector<unsigned, 4> 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<uint64_t> Buffers) {
SmallVector<unsigned, 4> 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
35 changes: 11 additions & 24 deletions llvm/tools/llvm-mca/Scheduler.h
Expand Up @@ -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
Expand Down Expand Up @@ -182,14 +177,12 @@ class ResourceState {
ReadyMask = ResourceSizeMask;
BufferSize = Desc.BufferSize;
AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(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; }
Expand Down Expand Up @@ -244,8 +237,6 @@ class ResourceState {
void reserveBuffer() {
if (AvailableSlots)
AvailableSlots--;
unsigned UsedSlots = static_cast<unsigned>(BufferSize) - AvailableSlots;
MaxUsedSlots = std::max(MaxUsedSlots, UsedSlots);
}

void releaseBuffer() {
Expand Down Expand Up @@ -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<uint64_t> Buffers) const;
ResourceStateEvent canBeDispatched(llvm::ArrayRef<uint64_t> 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.
Expand Down Expand Up @@ -372,15 +367,6 @@ class ResourceManager {

void cycleEvent(llvm::SmallVectorImpl<ResourceRef> &ResourcesFreed);

void getBuffersUsage(std::vector<BufferUsageEntry> &Usage) const {
for (const std::pair<uint64_t, UniqueResourceState> &Resource : Resources) {
const ResourceState &RS = *Resource.second;
if (RS.isBuffered())
Usage.emplace_back(std::pair<unsigned, unsigned>(RS.getProcResourceID(),
RS.getMaxUsedSlots()));
}
}

#ifndef NDEBUG
void dump() const {
for (const std::pair<uint64_t, UniqueResourceState> &Resource : Resources)
Expand Down Expand Up @@ -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<uint64_t> Buffers);
// Notify the Backend that buffered resources were freed.
void notifyReleasedBuffers(llvm::ArrayRef<uint64_t> Buffers);

/// Issue instructions from the ready queue by giving priority to older
/// instructions.
void issue();
Expand Down Expand Up @@ -498,10 +489,6 @@ class Scheduler {

void cycleEvent(unsigned Cycle);

void getBuffersUsage(std::vector<BufferUsageEntry> &Usage) const {
Resources->getBuffersUsage(Usage);
}

#ifndef NDEBUG
void dump() const;
#endif
Expand Down

0 comments on commit a3f2e48

Please sign in to comment.