[llvm] 86fb40d - [ProfileData] Implement contains in SampleProfileNameTable (NFC) (#211995)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 29 12:51:21 PDT 2026
Author: Kazu Hirata
Date: 2026-07-29T12:51:15-07:00
New Revision: 86fb40ddfa3c7cd5c32d758f4b1709d01028a7db
URL: https://github.com/llvm/llvm-project/commit/86fb40ddfa3c7cd5c32d758f4b1709d01028a7db
DIFF: https://github.com/llvm/llvm-project/commit/86fb40ddfa3c7cd5c32d758f4b1709d01028a7db.diff
LOG: [ProfileData] Implement contains in SampleProfileNameTable (NFC) (#211995)
This patch implements contains(StringRef) and contains(uint64_t) in
SampleProfileNameTable and SampleProfileReader to serve symbol
membership queries directly from the reader -- "is this symbol in the
name table?".
Without this patch, users of the sample profile reader, namely
SampleProfileLoader::doInitialization and SampleProfileNameSet, each
construct their own StringSet<> containing all name table entries.
That is, we end up with two instances of StringSet<> with identical
contents. Since these instances hold their own copies of symbol
strings on the heap, both the constructor and destructor take up a
large portion of compilation time.
This patch teaches SampleProfileReader::contains to directly serve
symbol membership queries.
- For EytzingerSampleProfileNameTable, contains performs binary search
directly across the three concatenated Eytzinger table spans
(CSKeys, FlatKeys, and Inlinees) in a cache-friendly manner.
- Other representations of the name table lazily construct an internal
DenseSet on demand.
This patch updates existing customers to call Reader->contains.
RFC:
https://discourse.llvm.org/t/rfc-faster-sample-profile-loading/90957/8
Assisted-by: Antigravity
Added:
Modified:
llvm/include/llvm/ProfileData/SampleProfReader.h
llvm/lib/Transforms/IPO/SampleProfile.cpp
llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
llvm/unittests/ProfileData/SampleProfTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index 0003d06e60373..75cc8545c37dd 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -226,6 +226,8 @@
#define LLVM_PROFILEDATA_SAMPLEPROFREADER_H
#include "llvm/ADT/Eytzinger.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
@@ -395,9 +397,37 @@ class SampleProfileNameTable {
virtual size_t size() const = 0;
bool empty() const { return size() == 0; }
virtual FunctionId operator[](size_t Idx) const = 0;
+ virtual bool contains(StringRef Key) const {
+ return contains(FunctionId(Key).getHashCode());
+ }
+ virtual bool contains(uint64_t GUID) const {
+ return getOrCreateSet(GUIDSet, *this, GetFunctionIdHash).contains(GUID);
+ }
iterator begin() const { return iterator(this, 0); }
iterator end() const { return iterator(this, size()); }
+
+protected:
+ mutable std::optional<DenseSet<uint64_t>> GUIDSet;
+
+ static constexpr auto GetFunctionIdHash = [](FunctionId F) {
+ return F.getHashCode();
+ };
+ static constexpr auto GetFunctionIdString = [](FunctionId F) {
+ return F.stringRef();
+ };
+
+ template <typename SetT, typename RangeT, typename ProjT = llvm::identity>
+ static const SetT &getOrCreateSet(std::optional<SetT> &Set,
+ const RangeT &Range, ProjT Proj = ProjT()) {
+ if (!Set) {
+ Set.emplace();
+ Set->reserve(Range.size());
+ for (const auto &Item : Range)
+ Set->insert(Proj(Item));
+ }
+ return *Set;
+ }
};
class LazySampleProfileNameTable final : public SampleProfileNameTable {
@@ -416,10 +446,17 @@ class LazySampleProfileNameTable final : public SampleProfileNameTable {
return FunctionId(endian::read<uint64_t, unaligned>(
Start + Idx * sizeof(uint64_t), endianness::little));
}
+
+ bool contains(uint64_t GUID) const override {
+ ArrayRef<support::ulittle64_t> Table(
+ reinterpret_cast<const support::ulittle64_t *>(Start), Size);
+ return getOrCreateSet(GUIDSet, Table).contains(GUID);
+ }
};
class StringSampleProfileNameTable final : public SampleProfileNameTable {
std::vector<FunctionId> Vec;
+ mutable std::optional<DenseSet<StringRef>> NameSet;
public:
explicit StringSampleProfileNameTable(std::vector<FunctionId> &&Vec)
@@ -433,6 +470,10 @@ class StringSampleProfileNameTable final : public SampleProfileNameTable {
assert(Idx < Vec.size() && "Index out of bounds");
return Vec[Idx];
}
+
+ bool contains(StringRef Key) const override {
+ return getOrCreateSet(NameSet, Vec, GetFunctionIdString).contains(Key);
+ }
};
class MD5SampleProfileNameTable final : public SampleProfileNameTable {
@@ -454,18 +495,29 @@ class MD5SampleProfileNameTable final : public SampleProfileNameTable {
class EytzingerSampleProfileNameTable final : public SampleProfileNameTable {
ArrayRef<support::ulittle64_t> Array;
+ std::array<EytzingerTableSpan<support::ulittle64_t>,
+ static_cast<size_t>(EytzingerSpan::NumSpans)>
+ Spans;
public:
EytzingerSampleProfileNameTable(const support::ulittle64_t *Data,
uint64_t NumCS, uint64_t NumFlat,
uint64_t NumInlinees)
- : Array(Data, NumCS + NumFlat + NumInlinees) {}
+ : Array(Data, NumCS + NumFlat + NumInlinees),
+ Spans{{{Data, NumCS},
+ {Data + NumCS, NumFlat},
+ {Data + NumCS + NumFlat, NumInlinees}}} {}
size_t size() const override { return Array.size(); }
FunctionId operator[](size_t Idx) const override {
return FunctionId(Array[Idx]);
}
+
+ bool contains(uint64_t GUID) const override {
+ return llvm::any_of(Spans,
+ [&](const auto &Span) { return Span.contains(GUID); });
+ }
};
class SampleProfileReader {
@@ -630,6 +682,8 @@ class SampleProfileReader {
SampleProfileNameTable::iterator()};
}
virtual bool dumpSectionInfo(raw_ostream &OS = dbgs()) { return false; };
+ virtual bool contains(StringRef Key) const { return false; }
+ virtual bool contains(uint64_t GUID) const { return false; }
/// Return whether names in the profile are all MD5 numbers.
bool useMD5() const { return ProfileIsMD5; }
@@ -793,6 +847,16 @@ class LLVM_ABI SampleProfileReaderBinary : public SampleProfileReader {
return {NameTable->begin(), NameTable->end()};
}
+ bool contains(StringRef Key) const override {
+ assert(NameTable && "NameTable should be populated before querying");
+ return NameTable->contains(Key);
+ }
+
+ bool contains(uint64_t GUID) const override {
+ assert(NameTable && "NameTable should be populated before querying");
+ return NameTable->contains(GUID);
+ }
+
protected:
/// Read a numeric value of type T from the profile.
///
@@ -1191,23 +1255,6 @@ class LLVM_ABI SampleProfileReaderGCC : public SampleProfileReader {
static const uint32_t GCOVTagAFDOFunction = 0xac000000;
};
-/// A helper class that wraps a local set of string names from NameTable.
-class SampleProfileNameSet {
- const SampleProfileReader &Reader;
- StringSet<> NamesInProfile;
-
-public:
- explicit SampleProfileNameSet(const SampleProfileReader &R) : Reader(R) {
- for (FunctionId Name : Reader.getNameTable())
- NamesInProfile.insert(Name.stringRef());
- }
-
- /// Check if a canonical function name exists in the profile name table.
- bool contains(StringRef CanonName) const {
- return NamesInProfile.contains(CanonName);
- }
-};
-
} // end namespace sampleprof
} // end namespace llvm
diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp
index 4234e05430dbf..cb11372183bc8 100644
--- a/llvm/lib/Transforms/IPO/SampleProfile.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp
@@ -573,15 +573,6 @@ class SampleProfileLoader final : public SampleProfileLoaderBaseImpl<Function> {
// all the function symbols defined or declared in current module.
DenseMap<uint64_t, StringRef> GUIDToFuncNameMap;
- // All the Names used in FunctionSamples including outline function
- // names, inline instance names and call target names.
- StringSet<> NamesInProfile;
- // MD5 version of NamesInProfile. Either NamesInProfile or GUIDsInProfile is
- // populated, depends on whether the profile uses MD5. Because the name table
- // generally contains several magnitude more entries than the number of
- // functions, we do not want to convert all names from one form to another.
- llvm::DenseSet<uint64_t> GUIDsInProfile;
-
// For symbol in profile symbol list, whether to regard their profiles
// to be accurate. It is mainly decided by existance of profile symbol
// list and -profile-accurate-for-symsinlist flag, but it can be
@@ -1986,19 +1977,8 @@ bool SampleProfileLoader::doInitialization(Module &M,
// While profile-sample-accurate is on, ignore symbol list.
ProfAccForSymsInList =
ProfileAccurateForSymsInList && PSL && !ProfileSampleAccurate;
- if (ProfAccForSymsInList) {
- NamesInProfile.clear();
- GUIDsInProfile.clear();
- auto NameTable = Reader->getNameTable();
- if (FunctionSamples::UseMD5) {
- for (FunctionId Name : NameTable)
- GUIDsInProfile.insert(Name.getHashCode());
- } else {
- for (FunctionId Name : NameTable)
- NamesInProfile.insert(Name.stringRef());
- }
+ if (ProfAccForSymsInList)
CoverageTracker.setProfAccForSymsInList(true);
- }
if (FAM && !ProfileInlineReplayFile.empty()) {
ExternalInlineAdvisor = getReplayInlineAdvisor(
@@ -2280,10 +2260,10 @@ bool SampleProfileLoader::runOnFunction(Function &F,
// but not cold accumulatively...), so the outline function showing up as
// cold in sampled binary will actually not be cold after current build.
StringRef CanonName = FunctionSamples::getCanonicalFnName(F);
- if ((FunctionSamples::UseMD5 &&
- GUIDsInProfile.count(
- Function::getGUIDAssumingExternalLinkage(CanonName))) ||
- (!FunctionSamples::UseMD5 && NamesInProfile.count(CanonName)))
+ if (FunctionSamples::UseMD5
+ ? Reader->contains(
+ Function::getGUIDAssumingExternalLinkage(CanonName))
+ : Reader->contains(CanonName))
initialEntryCount = -1;
}
diff --git a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
index d3aec9d7c363c..ffd6a265dafc8 100644
--- a/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfileMatcher.cpp
@@ -771,7 +771,6 @@ void SampleProfileMatcher::findFunctionsWithoutProfile() {
// TODO: Support MD5 profile.
if (FunctionSamples::UseMD5)
return;
- SampleProfileNameSet NamesInProfile(Reader);
for (auto &F : M) {
// Skip declarations, as even if the function can be matched, we have
@@ -787,7 +786,7 @@ void SampleProfileMatcher::findFunctionsWithoutProfile() {
// For extended binary, functions fully inlined may not be loaded in the
// top-level profile, so check the NameTable which has the all symbol names
// in profile.
- if (NamesInProfile.contains(CanonFName))
+ if (Reader.contains(CanonFName))
continue;
// For extended binary, non-profiled function symbols are in the profile
diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index 50fe3a1951577..d35427478e129 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -456,6 +456,17 @@ struct SampleProfTest : ::testing::Test {
if (Samples != nullptr)
Esamples = Samples->getTotalSamples();
ASSERT_EQ(I->getValue(), Esamples);
+
+ if (Format == SampleProfileFormat::SPF_Ext_Binary) {
+ ASSERT_TRUE(Reader->contains(I->getKey()));
+ ASSERT_TRUE(Reader->contains(FunctionId(I->getKey()).getHashCode()));
+ }
+ }
+
+ if (Format == SampleProfileFormat::SPF_Ext_Binary) {
+ StringRef FakeSymbol = "non_existent_symbol_for_test";
+ ASSERT_FALSE(Reader->contains(FakeSymbol));
+ ASSERT_FALSE(Reader->contains(FunctionId(FakeSymbol).getHashCode()));
}
}
};
More information about the llvm-commits
mailing list