[llvm] [BOLT] Reduce NameResolver memory usage during file object discovery (PR #212625)
Rafael Auler via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 28 17:54:51 PDT 2026
https://github.com/rafaelauler updated https://github.com/llvm/llvm-project/pull/212625
>From 4af90c760b90eb838722b5fee3b70d98b56126bd Mon Sep 17 00:00:00 2001
From: Rafael Auler <rafaelauler at fb.com>
Date: Fri, 24 Jul 2026 18:16:30 -0700
Subject: [PATCH 1/2] [BOLT] Reduce NameResolver memory usage during file
object discovery
Summary:
NameResolver used a StringMap<uint64_t> to count duplicate names. StringMap
owns its keys, so every uniquify()/getUniquifiedNameCount() query allocated a
full copy of each (potentially large, mangled) symbol name. During
discoverFileObjects on a large binary, this string-key duplication accounted
for ~2 GB (1 to 2% of RSS) of allocations in
StringMap::try_emplace_with_hash -> StringMapEntry::create -> allocateWithKey.
Replace the StringMap with a DenseMap<pair<uint64_t,uint64_t>, uint64_t> keyed
by a 128-bit xxh3 hash of the name. No string is ever stored: each distinct
name costs a fixed-size entry regardless of length. A 128-bit hash makes
collisions effectively impossible, so the per-name counts (and therefore the
generated 'Name/ID' unique names) are identical to the string-keyed map and
remain reproducible to match profile (fdata) names.
Also clear the map at the end of discoverFileObjects, since the resolver is not
needed afterwards; all NR.uniquify()/getUniquifiedNameCount() calls occur
within that function's dynamic extent (including processRelocations and
registerFragments).
---
bolt/include/bolt/Utils/NameResolver.h | 31 ++++++++++++++++++++------
bolt/lib/Rewrite/RewriteInstance.cpp | 4 ++++
2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/bolt/include/bolt/Utils/NameResolver.h b/bolt/include/bolt/Utils/NameResolver.h
index 9719ce1297a7f..4924c2c4b432c 100644
--- a/bolt/include/bolt/Utils/NameResolver.h
+++ b/bolt/include/bolt/Utils/NameResolver.h
@@ -13,26 +13,39 @@
#ifndef BOLT_UTILS_NAME_RESOLVER_H
#define BOLT_UTILS_NAME_RESOLVER_H
-#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Support/xxhash.h"
namespace llvm {
namespace bolt {
class NameResolver {
- /// Track the number of duplicate names.
- StringMap<uint64_t> Counters;
+ /// Track the number of duplicate names, keyed by a 128-bit hash of the name
+ /// rather than by the name itself. Storing hashes instead of the full strings
+ /// avoids duplicating potentially large (mangled) symbol names, which is a
+ /// significant source of memory use while processing the symbol table. Using
+ /// a 128-bit hash makes collisions effectively impossible, so the counts (and
+ /// therefore the generated unique names) are identical to a string-keyed map
+ /// and remain reproducible to match profile (fdata) names.
+ DenseMap<std::pair<uint64_t, uint64_t>, uint64_t> Counters;
/// Character guaranteed not to be used by any "native" name passed to
/// uniquify() function.
static constexpr char Sep = '/';
+ /// Return the map key used to track occurrences of \p Name.
+ static std::pair<uint64_t, uint64_t> getKey(StringRef Name) {
+ const XXH128_hash_t Hash = llvm::xxh3_128bits(
+ {reinterpret_cast<const uint8_t *>(Name.data()), Name.size()});
+ return {Hash.low64, Hash.high64};
+ }
+
public:
/// Return the number of uniquified versions of a given \p Name.
uint64_t getUniquifiedNameCount(StringRef Name) const {
- if (Counters.contains(Name))
- return Counters.at(Name);
- return 0;
+ return Counters.lookup(getKey(Name));
}
/// Return unique version of the \p Name in the form "Name<Sep><ID>".
@@ -43,10 +56,14 @@ class NameResolver {
/// Register new version of \p Name and return unique version in the form
/// "Name<Sep><Number>".
std::string uniquify(StringRef Name) {
- const uint64_t ID = ++Counters[Name];
+ const uint64_t ID = ++Counters[getKey(Name)];
return getUniqueName(Name, ID);
}
+ /// Release the memory used to track name occurrences. Call once no more names
+ /// need to be uniquified (e.g. after file object discovery is complete).
+ void clear() { Counters.clear(); }
+
/// For uniquified \p Name, return the original form (that may no longer be
/// unique).
static StringRef restore(StringRef Name) {
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index 86b078e1de177..acd361097ef39 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -1451,6 +1451,10 @@ void RewriteInstance::discoverFileObjects() {
FileSymRefs.clear();
discoverBOLTReserved();
+
+ // The name resolver is only needed while discovering and disambiguating file
+ // objects. Release its memory now that all names have been uniquified.
+ NR.clear();
}
void RewriteInstance::discoverBOLTReserved() {
>From 6e44afa478bde3c6fd109513dbcbf7a650d6a7f7 Mon Sep 17 00:00:00 2001
From: Rafael Auler <rafaelauler at fb.com>
Date: Tue, 28 Jul 2026 17:54:29 -0700
Subject: [PATCH 2/2] Don't use ADT convenience xxh3_128bits variant
---
bolt/include/bolt/Utils/NameResolver.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bolt/include/bolt/Utils/NameResolver.h b/bolt/include/bolt/Utils/NameResolver.h
index 4924c2c4b432c..34b91f0bf401b 100644
--- a/bolt/include/bolt/Utils/NameResolver.h
+++ b/bolt/include/bolt/Utils/NameResolver.h
@@ -38,7 +38,7 @@ class NameResolver {
/// Return the map key used to track occurrences of \p Name.
static std::pair<uint64_t, uint64_t> getKey(StringRef Name) {
const XXH128_hash_t Hash = llvm::xxh3_128bits(
- {reinterpret_cast<const uint8_t *>(Name.data()), Name.size()});
+ reinterpret_cast<const uint8_t *>(Name.data()), Name.size());
return {Hash.low64, Hash.high64};
}
More information about the llvm-commits
mailing list