[llvm] [MCA] Optimize memory consumption in resource pressure view (NFC) (PR #124904)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 29 03:10:29 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-tools-llvm-mca

Author: Anton Sidorenko (asi-sc)

<details>
<summary>Changes</summary>

ResourceUsage is a very sparse table. On large input asm sequences it consumes a lot of memory utilizing only a few percents of it (~4% on my benchmark). Reorganization of ResourceUsage to keep only used fields allows saving up to 18% of total memory use by mca or ~850% of input file size (~1.1GB in absolute values in my case).

NB: I use mca rather in unusual way by providing really big input asm sequences and I don't think there would be any noticeable change for small input files, but I still think it worth fixing.

---
Full diff: https://github.com/llvm/llvm-project/pull/124904.diff


2 Files Affected:

- (modified) llvm/tools/llvm-mca/Views/ResourcePressureView.cpp (+57-18) 
- (modified) llvm/tools/llvm-mca/Views/ResourcePressureView.h (+7-2) 


``````````diff
diff --git a/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp b/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp
index f39350f3b45852..26150849243764 100644
--- a/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp
+++ b/llvm/tools/llvm-mca/Views/ResourcePressureView.cpp
@@ -37,8 +37,16 @@ ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti,
   }
 
   NumResourceUnits = R2VIndex;
-  ResourceUsage.resize(NumResourceUnits * (getSource().size() + 1));
-  std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0.0);
+  ResourceUsage.resize(getSource().size());
+
+  ResourceReleaseAtCycles InitValue{0, 0};
+  auto Generator = [&InitValue]() {
+    auto Old = InitValue;
+    ++InitValue.ResourceIdx;
+    return Old;
+  };
+  std::generate_n(std::back_inserter(CommonResourceUsage), NumResourceUnits,
+                  Generator);
 }
 
 void ResourcePressureView::onEvent(const HWInstructionEvent &Event) {
@@ -60,8 +68,19 @@ void ResourcePressureView::onEvent(const HWInstructionEvent &Event) {
     assert(Resource2VecIndex.contains(RR.first));
     unsigned R2VIndex = Resource2VecIndex[RR.first];
     R2VIndex += llvm::countr_zero(RR.second);
-    ResourceUsage[R2VIndex + NumResourceUnits * SourceIdx] += Use.second;
-    ResourceUsage[R2VIndex + NumResourceUnits * Source.size()] += Use.second;
+
+    InstResourceUsage &RU = ResourceUsage[SourceIdx];
+    ResourceReleaseAtCycles NewUsage{R2VIndex, Use.second};
+    auto ResCyclesIt =
+        lower_bound(RU, NewUsage, [](const auto &L, const auto &R) {
+          return L.ResourceIdx < R.ResourceIdx;
+        });
+    if (ResCyclesIt != RU.end() && ResCyclesIt->ResourceIdx == R2VIndex)
+      ResCyclesIt->Cycles += NewUsage.Cycles;
+    else
+      RU.insert(ResCyclesIt, std::move(NewUsage));
+
+    CommonResourceUsage[R2VIndex].Cycles += NewUsage.Cycles;
   }
 }
 
@@ -135,10 +154,17 @@ void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const {
 
   ArrayRef<llvm::MCInst> Source = getSource();
   const unsigned Executions = LastInstructionIdx / Source.size() + 1;
+  auto UsageEntryEnd = CommonResourceUsage.end();
+  auto UsageEntryIt = CommonResourceUsage.begin();
   for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) {
-    double Usage = ResourceUsage[I + Source.size() * E];
-    printResourcePressure(FOS, Usage / Executions, (I + 1) * 7);
+    double Pressure = 0.0;
+    if (UsageEntryIt != UsageEntryEnd && UsageEntryIt->ResourceIdx == I) {
+      Pressure = UsageEntryIt->Cycles / Executions;
+      ++UsageEntryIt;
+    }
+    printResourcePressure(FOS, Pressure, (I + 1) * 7);
   }
+  assert(UsageEntryIt == UsageEntryEnd);
 
   FOS.flush();
   OS << Buffer;
@@ -157,11 +183,17 @@ void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const {
   ArrayRef<llvm::MCInst> Source = getSource();
   const unsigned Executions = LastInstructionIdx / Source.size() + 1;
   for (const MCInst &MCI : Source) {
-    unsigned BaseEltIdx = InstrIndex * NumResourceUnits;
+    auto UsageEntryEnd = ResourceUsage[InstrIndex].end();
+    auto UsageEntryIt = ResourceUsage[InstrIndex].begin();
     for (unsigned J = 0; J < NumResourceUnits; ++J) {
-      double Usage = ResourceUsage[J + BaseEltIdx];
-      printResourcePressure(FOS, Usage / Executions, (J + 1) * 7);
+      double Pressure = 0.0;
+      if (UsageEntryIt != UsageEntryEnd && UsageEntryIt->ResourceIdx == J) {
+        Pressure = UsageEntryIt->Cycles / Executions;
+        ++UsageEntryIt;
+      }
+      printResourcePressure(FOS, Pressure, (J + 1) * 7);
     }
+    assert(UsageEntryIt == UsageEntryEnd);
 
     FOS << printInstructionString(MCI) << '\n';
     FOS.flush();
@@ -180,17 +212,24 @@ json::Value ResourcePressureView::toJSON() const {
   // non-zero values.
   ArrayRef<llvm::MCInst> Source = getSource();
   const unsigned Executions = LastInstructionIdx / Source.size() + 1;
-  for (const auto &R : enumerate(ResourceUsage)) {
-    const ReleaseAtCycles &RU = R.value();
-    if (RU.getNumerator() == 0)
-      continue;
-    unsigned InstructionIndex = R.index() / NumResourceUnits;
-    unsigned ResourceIndex = R.index() % NumResourceUnits;
-    double Usage = RU / Executions;
+
+  auto AddToJSON = [&ResourcePressureInfo, Executions](
+                       const ResourceReleaseAtCycles &RU, unsigned InstIndex) {
+    assert(RU.Cycles.getNumerator() != 0);
+    double Usage = RU.Cycles / Executions;
     ResourcePressureInfo.push_back(
-        json::Object({{"InstructionIndex", InstructionIndex},
-                      {"ResourceIndex", ResourceIndex},
+        json::Object({{"InstructionIndex", InstIndex},
+                      {"ResourceIndex", RU.ResourceIdx},
                       {"ResourceUsage", Usage}}));
+  };
+  for (const auto &R : enumerate(ResourceUsage)) {
+    unsigned InstructionIndex = R.index();
+    for (const auto &RU : R.value())
+      AddToJSON(RU, InstructionIndex);
+  }
+  for (const auto &RU : CommonResourceUsage) {
+    if (RU.Cycles.getNumerator() != 0)
+      AddToJSON(RU, Source.size());
   }
 
   json::Object JO({{"ResourcePressureInfo", std::move(ResourcePressureInfo)}});
diff --git a/llvm/tools/llvm-mca/Views/ResourcePressureView.h b/llvm/tools/llvm-mca/Views/ResourcePressureView.h
index be8ad04102fd05..3deb9e8a21d5ea 100644
--- a/llvm/tools/llvm-mca/Views/ResourcePressureView.h
+++ b/llvm/tools/llvm-mca/Views/ResourcePressureView.h
@@ -77,8 +77,13 @@ class ResourcePressureView : public InstructionView {
   // resource ID.
   llvm::DenseMap<unsigned, unsigned> Resource2VecIndex;
 
-  // Table of resources used by instructions.
-  std::vector<ReleaseAtCycles> ResourceUsage;
+  struct ResourceReleaseAtCycles {
+    unsigned ResourceIdx;
+    ReleaseAtCycles Cycles;
+  };
+  using InstResourceUsage = std::vector<ResourceReleaseAtCycles>;
+  std::vector<InstResourceUsage> ResourceUsage;
+  InstResourceUsage CommonResourceUsage;
   unsigned NumResourceUnits;
 
   void printResourcePressurePerIter(llvm::raw_ostream &OS) const;

``````````

</details>


https://github.com/llvm/llvm-project/pull/124904


More information about the llvm-commits mailing list