[llvm] r340536 - [llvm-mca] Allow the definition of custom strategies for selecting processor resource units.

Andrea Di Biagio via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 23 08:04:53 PDT 2018


Author: adibiagio
Date: Thu Aug 23 08:04:52 2018
New Revision: 340536

URL: http://llvm.org/viewvc/llvm-project?rev=340536&view=rev
Log:
[llvm-mca] Allow the definition of custom strategies for selecting processor resource units.

With this patch, users can now customize the pipeline selection strategy for
scheduler resources. The resource selection strategy can be defined at processor
resource granularity. This enables the definition of different strategies for
different hardware schedulers.

To override the strategy associated with a processor resource, users can call
method ResourceManager::setCustomStrategy(), and pass a 'ResourceStrategy'
object in input.

Class ResourceStrategy is an abstract class which declares virtual method
`ResourceStrategy::select()`. Method select() is meant to implement the actual
strategy; it is responsible for picking the next best resource from a set of
available pipeline resources.  Custom strategy must simply override that method.

By default, processor resources are associated with instances of
'DefaultResourceStrategy'.  A 'DefaultResourceStrategy' internally implements a
simple round-robin selector. For more details, please refer to the code comments
in Scheduler.h.

Modified:
    llvm/trunk/tools/llvm-mca/Scheduler.cpp
    llvm/trunk/tools/llvm-mca/Scheduler.h

Modified: llvm/trunk/tools/llvm-mca/Scheduler.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-mca/Scheduler.cpp?rev=340536&r1=340535&r2=340536&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-mca/Scheduler.cpp (original)
+++ llvm/trunk/tools/llvm-mca/Scheduler.cpp Thu Aug 23 08:04:52 2018
@@ -22,17 +22,43 @@ using namespace llvm;
 
 #define DEBUG_TYPE "llvm-mca"
 
+ResourceStrategy::~ResourceStrategy() = default;
+
+void DefaultResourceStrategy::skipMask(uint64_t Mask) {
+  NextInSequenceMask &= (~Mask);
+  if (!NextInSequenceMask) {
+    NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;
+    RemovedFromNextInSequence = 0;
+  }
+}
+
+uint64_t DefaultResourceStrategy::select(uint64_t ReadyMask) {
+  // This method assumes that ReadyMask cannot be zero.
+  uint64_t CandidateMask = llvm::PowerOf2Floor(NextInSequenceMask);
+  while (!(ReadyMask & CandidateMask)) {
+    skipMask(CandidateMask);
+    CandidateMask = llvm::PowerOf2Floor(NextInSequenceMask);
+  }
+  return CandidateMask;
+}
+
+void DefaultResourceStrategy::used(uint64_t Mask) {
+  if (Mask > NextInSequenceMask) {
+    RemovedFromNextInSequence |= Mask;
+    return;
+  }
+  skipMask(Mask);
+}
+
 ResourceState::ResourceState(const llvm::MCProcResourceDesc &Desc,
                              unsigned Index, uint64_t Mask)
-    : ProcResourceDescIndex(Index), ResourceMask(Mask) {
+    : ProcResourceDescIndex(Index), ResourceMask(Mask),
+      BufferSize(Desc.BufferSize) {
   if (llvm::countPopulation(ResourceMask) > 1)
     ResourceSizeMask = ResourceMask ^ llvm::PowerOf2Floor(ResourceMask);
   else
     ResourceSizeMask = (1ULL << Desc.NumUnits) - 1;
-  NextInSequenceMask = ResourceSizeMask;
-  RemovedFromNextInSequence = 0;
   ReadyMask = ResourceSizeMask;
-  BufferSize = Desc.BufferSize;
   AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(BufferSize);
   Unavailable = false;
 }
@@ -50,74 +76,75 @@ ResourceStateEvent ResourceState::isBuff
   return RS_BUFFER_UNAVAILABLE;
 }
 
-uint64_t ResourceState::selectNextInSequence() {
-  assert(isReady());
-  uint64_t Next = getNextInSequence();
-  while (!isSubResourceReady(Next)) {
-    updateNextInSequence();
-    Next = getNextInSequence();
-  }
-  return Next;
-}
-
 #ifndef NDEBUG
 void ResourceState::dump() const {
   dbgs() << "MASK: " << ResourceMask << ", SIZE_MASK: " << ResourceSizeMask
-         << ", NEXT: " << NextInSequenceMask << ", RDYMASK: " << ReadyMask
-         << ", BufferSize=" << BufferSize
+         << ", RDYMASK: " << ReadyMask << ", BufferSize=" << BufferSize
          << ", AvailableSlots=" << AvailableSlots
          << ", Reserved=" << Unavailable << '\n';
 }
 #endif
 
-unsigned getResourceStateIndex(uint64_t Mask) {
+static unsigned getResourceStateIndex(uint64_t Mask) {
   return std::numeric_limits<uint64_t>::digits - llvm::countLeadingZeros(Mask);
 }
 
-unsigned ResourceManager::resolveResourceMask(uint64_t Mask) const {
-  return Resources[getResourceStateIndex(Mask)]->getProcResourceID();
-}
-
-unsigned ResourceManager::getNumUnits(uint64_t ResourceID) const {
-  return Resources[getResourceStateIndex(ResourceID)]->getNumUnits();
+static std::unique_ptr<ResourceStrategy>
+getStrategyFor(const ResourceState &RS) {
+  if (RS.isAResourceGroup() || RS.getNumUnits() > 1)
+    return llvm::make_unique<DefaultResourceStrategy>(RS.getReadyMask());
+  return std::unique_ptr<ResourceStrategy>(nullptr);
 }
 
-void ResourceManager::initialize(const llvm::MCSchedModel &SM) {
+ResourceManager::ResourceManager(const llvm::MCSchedModel &SM)
+    : ProcResID2Mask(SM.getNumProcResourceKinds()) {
   computeProcResourceMasks(SM, ProcResID2Mask);
   Resources.resize(SM.getNumProcResourceKinds());
+  Strategies.resize(SM.getNumProcResourceKinds());
 
   for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
     uint64_t Mask = ProcResID2Mask[I];
-    Resources[getResourceStateIndex(Mask)] =
+    unsigned Index = getResourceStateIndex(Mask);
+    Resources[Index] =
         llvm::make_unique<ResourceState>(*SM.getProcResource(I), I, Mask);
+    Strategies[Index] = getStrategyFor(*Resources[Index]);
   }
 }
 
+void ResourceManager::setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,
+                                            uint64_t ResourceID) {
+  unsigned Index = getResourceStateIndex(ResourceID);
+  assert(Index < Resources.size() && "Invalid processor resource index!");
+  Strategies[Index].reset(S.get());
+}
+
+unsigned ResourceManager::resolveResourceMask(uint64_t Mask) const {
+  return Resources[getResourceStateIndex(Mask)]->getProcResourceID();
+}
+
+unsigned ResourceManager::getNumUnits(uint64_t ResourceID) const {
+  return Resources[getResourceStateIndex(ResourceID)]->getNumUnits();
+}
+
 // Returns the actual resource consumed by this Use.
 // First, is the primary resource ID.
 // Second, is the specific sub-resource ID.
-std::pair<uint64_t, uint64_t> ResourceManager::selectPipe(uint64_t ResourceID) {
-  ResourceState &RS = *Resources[getResourceStateIndex(ResourceID)];
-  uint64_t SubResourceID = RS.selectNextInSequence();
+ResourceRef ResourceManager::selectPipe(uint64_t ResourceID) {
+  unsigned Index = getResourceStateIndex(ResourceID);
+  ResourceState &RS = *Resources[Index];
+  assert(RS.isReady() && "No available units to select!");
+
+  // Special case where RS is not a group, and it only declares a single
+  // resource unit.
+  if (!RS.isAResourceGroup() && RS.getNumUnits() == 1)
+    return std::make_pair(ResourceID, RS.getReadyMask());
+
+  uint64_t SubResourceID = Strategies[Index]->select(RS.getReadyMask());
   if (RS.isAResourceGroup())
     return selectPipe(SubResourceID);
   return std::make_pair(ResourceID, SubResourceID);
 }
 
-void ResourceState::removeFromNextInSequence(uint64_t ID) {
-  assert(NextInSequenceMask);
-  assert(countPopulation(ID) == 1);
-  if (ID > getNextInSequence())
-    RemovedFromNextInSequence |= ID;
-  NextInSequenceMask = NextInSequenceMask & (~ID);
-  if (!NextInSequenceMask) {
-    NextInSequenceMask = ResourceSizeMask;
-    assert(NextInSequenceMask != RemovedFromNextInSequence);
-    NextInSequenceMask ^= RemovedFromNextInSequence;
-    RemovedFromNextInSequence = 0;
-  }
-}
-
 void ResourceManager::use(const ResourceRef &RR) {
   // Mark the sub-resource referenced by RR as used.
   ResourceState &RS = *Resources[getResourceStateIndex(RR.first)];
@@ -128,14 +155,15 @@ void ResourceManager::use(const Resource
     return;
 
   // Notify to other resources that RR.first is no longer available.
-  for (UniqueResourceState &Res : Resources) {
+  for (std::unique_ptr<ResourceState> &Res : Resources) {
     ResourceState &Current = *Res;
     if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first)
       continue;
 
     if (Current.containsResource(RR.first)) {
+      unsigned Index = getResourceStateIndex(Current.getResourceMask());
       Current.markSubResourceAsUsed(RR.first);
-      Current.removeFromNextInSequence(RR.first);
+      Strategies[Index]->used(RR.first);
     }
   }
 }
@@ -147,7 +175,7 @@ void ResourceManager::release(const Reso
   if (!WasFullyUsed)
     return;
 
-  for (UniqueResourceState &Res : Resources) {
+  for (std::unique_ptr<ResourceState> &Res : Resources) {
     ResourceState &Current = *Res;
     if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first)
       continue;

Modified: llvm/trunk/tools/llvm-mca/Scheduler.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-mca/Scheduler.h?rev=340536&r1=340535&r2=340536&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-mca/Scheduler.h (original)
+++ llvm/trunk/tools/llvm-mca/Scheduler.h Thu Aug 23 08:04:52 2018
@@ -46,6 +46,92 @@ enum ResourceStateEvent {
   RS_RESERVED
 };
 
+/// Resource allocation strategy used by hardware scheduler resources.
+class ResourceStrategy {
+  ResourceStrategy(const ResourceStrategy &) = delete;
+  ResourceStrategy &operator=(const ResourceStrategy &) = delete;
+
+public:
+  ResourceStrategy() {}
+  virtual ~ResourceStrategy();
+
+  /// Selects a processor resource unit from a ReadyMask.
+  virtual uint64_t select(uint64_t ReadyMask) = 0;
+
+  /// Called by the ResourceManager when a processor resource group, or a
+  /// processor resource with multiple units has become unavailable.
+  ///
+  /// The default strategy uses this information to bias its selection logic.
+  virtual void used(uint64_t ResourceMask) {}
+};
+
+/// Default resource allocation strategy used by processor resource groups and
+/// processor resources with multiple units.
+class DefaultResourceStrategy final : public ResourceStrategy {
+  /// A Mask of resource unit identifiers.
+  ///
+  /// There is one bit set for every available resource unit.
+  /// It defaults to the value of field ResourceSizeMask in ResourceState.
+  const unsigned ResourceUnitMask;
+
+  /// A simple round-robin selector for processor resource units.
+  /// Each bit of this mask identifies a sub resource within a group.
+  ///
+  /// As an example, lets assume that this is a default policy for a
+  /// processor resource group composed by the following three units:
+  ///   ResourceA -- 0b001
+  ///   ResourceB -- 0b010
+  ///   ResourceC -- 0b100
+  ///
+  /// Field NextInSequenceMask is used by class ResourceManager to select the
+  /// "next available resource" from the set.
+  /// It defaults to the value of field `ResourceUnitMasks`. In this example, it
+  /// defaults to mask '0b111'.
+  ///
+  /// The round-robin selector would firstly select 'ResourceC', then
+  /// 'ResourceB', and eventually 'ResourceA'.  When a resource R is used, the
+  /// corresponding bit in NextInSequenceMask is cleared.  For example, if
+  /// 'ResourceC' is selected, then the new value of NextInSequenceMask becomes
+  /// 0xb011.
+  ///
+  /// When NextInSequenceMask becomes zero, it is automatically reset to the
+  /// default value (i.e. ResourceUnitMask).
+  uint64_t NextInSequenceMask;
+
+  /// This field is used to track resource units that are used (i.e. selected)
+  /// by other groups other than this one.
+  ///
+  /// In LLVM processor resource groups are allowed to partially (or fully)
+  /// overlap. That means, a same unit may be visible to multiple groups.
+  /// This field keeps track of uses that have been initiated from outside of
+  /// this group. The idea is to bias the selection strategy, so that resources
+  /// that haven't been used by other groups get prioritized.
+  ///
+  /// The end goal is to (try to) keep the resource distribution as much uniform
+  /// as possible. By construction, this mask only tracks one-level of resource
+  /// usage. Therefore, this strategy is expected to be less accurate when same
+  /// units are used multiple times from outside of this group during a single
+  /// selection round.
+  ///
+  /// Note: an LRU selector would have a better accuracy at the cost of being
+  /// slightly more expensive (mostly in terms of runtime cost). Methods
+  /// 'select' and 'used', are always in the hot execution path of llvm-mca.
+  /// Therefore, a slow implementation of 'select' would have a negative impact
+  /// on the overall performance of the tool.
+  uint64_t RemovedFromNextInSequence;
+
+  void skipMask(uint64_t Mask);
+
+public:
+  DefaultResourceStrategy(uint64_t UnitMask)
+      : ResourceStrategy(), ResourceUnitMask(UnitMask),
+        NextInSequenceMask(UnitMask), RemovedFromNextInSequence(0) {}
+  virtual ~DefaultResourceStrategy() = default;
+
+  uint64_t select(uint64_t ReadyMask) override;
+  void used(uint64_t Mask) override;
+};
+
 /// A processor resource descriptor.
 ///
 /// There is an instance of this class for every processor resource defined by
@@ -54,10 +140,10 @@ enum ResourceStateEvent {
 /// resource units.
 class ResourceState {
   /// An index to the MCProcResourceDesc entry in the processor model.
-  unsigned ProcResourceDescIndex;
+  const unsigned ProcResourceDescIndex;
   /// A resource mask. This is generated by the tool with the help of
   /// function `mca::createProcResourceMasks' (see Support.h).
-  uint64_t ResourceMask;
+  const uint64_t ResourceMask;
 
   /// A ProcResource can have multiple units.
   ///
@@ -69,53 +155,6 @@ class ResourceState {
   /// 'NumUnits' in 'ProcResourceUnits').
   uint64_t ResourceSizeMask;
 
-  // A simple round-robin selector for processor resources.
-  // Each bit of the mask identifies a sub resource within this group.
-  //
-  // As an example, lets assume that this ResourceState describes a
-  // processor resource group composed of the following three units:
-  //   ResourceA -- 0b001
-  //   ResourceB -- 0b010
-  //   ResourceC -- 0b100
-  //
-  // Each unit is identified by a ResourceMask which always contains a
-  // single bit set. Field NextInSequenceMask is initially set to value
-  // 0xb111. That value is obtained by OR'ing the resource masks of
-  // processor resource that are part of the group.
-  //
-  //   NextInSequenceMask  -- 0b111
-  //
-  // Field NextInSequenceMask is used by class ResourceManager to select the
-  // "next available resource" from the set. The algorithm would prioritizes
-  // resources with a bigger ResourceMask value.
-  //
-  // In this example, there are three resources in the set, and 'ResourceC'
-  // has the highest mask value. The round-robin selector would firstly select
-  //  'ResourceC', then 'ResourceB', and eventually 'ResourceA'.
-  //
-  // When a resource R is used, its corresponding bit is cleared from the set.
-  //
-  // Back to the example:
-  // If 'ResourceC' is selected, then the new value of NextInSequenceMask
-  // becomes 0xb011.
-  //
-  // When NextInSequenceMask becomes zero, it is reset to its original value
-  // (in this example, that value would be 0b111).
-  uint64_t NextInSequenceMask;
-
-  // Some instructions can only be issued on very specific pipeline resources.
-  // For those instructions, we know exactly which resource would be consumed
-  // without having to dynamically select it using field 'NextInSequenceMask'.
-  //
-  // The resource mask bit associated to the (statically) selected
-  // processor resource is still cleared from the 'NextInSequenceMask'.
-  // If that bit was already zero in NextInSequenceMask, then we update
-  // mask 'RemovedFromNextInSequence'.
-  //
-  // When NextInSequenceMask is reset back to its initial value, the algorithm
-  // removes any bits which are set in RemoveFromNextInSequence.
-  uint64_t RemovedFromNextInSequence;
-
   /// A mask of ready units.
   uint64_t ReadyMask;
 
@@ -132,7 +171,7 @@ class ResourceState {
   /// Also, no other instruction ca be dispatched/issue while this resource is
   /// in use. Only when all the "resource cycles" are consumed (after the issue
   /// event), a new instruction ca be dispatched.
-  int BufferSize;
+  const int BufferSize;
 
   /// Available slots in the buffer (zero, if this is not a buffered resource).
   unsigned AvailableSlots;
@@ -150,26 +189,13 @@ class ResourceState {
     return ReadyMask & SubResMask;
   }
 
-  /// Returns the mask identifier of the next available resource in the set.
-  uint64_t getNextInSequence() const {
-    assert(NextInSequenceMask);
-    return llvm::PowerOf2Floor(NextInSequenceMask);
-  }
-
-  /// Returns the mask of the next available resource within the set,
-  /// and updates the resource selector.
-  void updateNextInSequence() {
-    NextInSequenceMask ^= getNextInSequence();
-    if (!NextInSequenceMask)
-      NextInSequenceMask = ResourceSizeMask;
-  }
-
 public:
   ResourceState(const llvm::MCProcResourceDesc &Desc, unsigned Index,
                 uint64_t Mask);
 
   unsigned getProcResourceID() const { return ProcResourceDescIndex; }
   uint64_t getResourceMask() const { return ResourceMask; }
+  uint64_t getReadyMask() const { return ReadyMask; }
   int getBufferSize() const { return BufferSize; }
 
   bool isBuffered() const { return BufferSize > 0; }
@@ -190,9 +216,7 @@ public:
     return llvm::countPopulation(ResourceMask) > 1;
   }
 
-  bool containsResource(uint64_t ID) const {
-    return ResourceMask & ID;
-  }
+  bool containsResource(uint64_t ID) const { return ResourceMask & ID; }
 
   void markSubResourceAsUsed(uint64_t ID) {
     assert(isSubResourceReady(ID));
@@ -208,9 +232,6 @@ public:
     return isAResourceGroup() ? 1U : llvm::countPopulation(ResourceSizeMask);
   }
 
-  uint64_t selectNextInSequence();
-  void removeFromNextInSequence(uint64_t ID);
-
   /// Checks if there is an available slot in the resource buffer.
   ///
   /// Returns RS_BUFFER_AVAILABLE if this is not a buffered resource, or if
@@ -261,8 +282,8 @@ typedef std::pair<unsigned, unsigned> Bu
 /// public interface.
 class ResourceManager {
   // The resource manager owns all the ResourceState.
-  using UniqueResourceState = std::unique_ptr<ResourceState>;
-  std::vector<UniqueResourceState> Resources;
+  std::vector<std::unique_ptr<ResourceState>> Resources;
+  std::vector<std::unique_ptr<ResourceStrategy>> Strategies;
 
   // Keeps track of which resources are busy, and how many cycles are left
   // before those become usable again.
@@ -271,9 +292,6 @@ class ResourceManager {
   // A table to map processor resource IDs to processor resource masks.
   llvm::SmallVector<uint64_t, 8> ProcResID2Mask;
 
-  // Populate resource descriptors.
-  void initialize(const llvm::MCSchedModel &SM);
-
   // Returns the actual resource unit that will be used.
   ResourceRef selectPipe(uint64_t ResourceID);
 
@@ -282,10 +300,22 @@ class ResourceManager {
 
   unsigned getNumUnits(uint64_t ResourceID) const;
 
+  // Overrides the selection strategy for the processor resource with the given
+  // mask.
+  void setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,
+                             uint64_t ResourceMask);
+
 public:
-  ResourceManager(const llvm::MCSchedModel &SM)
-      : ProcResID2Mask(SM.getNumProcResourceKinds()) {
-    initialize(SM);
+  ResourceManager(const llvm::MCSchedModel &SM);
+  virtual ~ResourceManager() = default;
+
+  // Overrides the selection strategy for the resource at index ResourceID in
+  // the MCProcResourceDesc table.
+  void setCustomStrategy(std::unique_ptr<ResourceStrategy> S,
+                         unsigned ResourceID) {
+    assert(ResourceID < ProcResID2Mask.size() &&
+           "Invalid resource index in input!");
+    return setCustomStrategy(std::move(S), ProcResID2Mask[ResourceID]);
   }
 
   // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if
@@ -323,7 +353,7 @@ public:
 
 #ifndef NDEBUG
   void dump() const {
-    for (const UniqueResourceState &Resource : Resources)
+    for (const std::unique_ptr<ResourceState> &Resource : Resources)
       Resource->dump();
   }
 #endif




More information about the llvm-commits mailing list