[llvm] 3c1311e - [ProfileData] Introduce SampleProfileFuncOffsetTable (NFC) (#205045)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 27 05:27:00 PDT 2026
Author: Kazu Hirata
Date: 2026-06-27T05:26:55-07:00
New Revision: 3c1311e8f0985b6a399c92b6e4a4ebe7f07c4d8b
URL: https://github.com/llvm/llvm-project/commit/3c1311e8f0985b6a399c92b6e4a4ebe7f07c4d8b
DIFF: https://github.com/llvm/llvm-project/commit/3c1311e8f0985b6a399c92b6e4a4ebe7f07c4d8b.diff
LOG: [ProfileData] Introduce SampleProfileFuncOffsetTable (NFC) (#205045)
This patch introduces SampleProfileFuncOffsetTable, a unified wrapper
representing the function offset table.
Currently, the offset table is always a DenseMap. To support the
upcoming on-disk hash table (v104) for faster sample profile loading,
this patch abstracts the offset table representation. The new class
can delegate lookups to either the in-memory DenseMap or the on-disk
OnDiskIterableChainedHashTable.
This patch updates the reader to use the new wrapper's lookup and
insert interfaces. Since the on-disk path is not yet active, this
change is a non-functional change (NFC).
RFC:
https://discourse.llvm.org/t/rfc-faster-sample-profile-loading/90957/4
Added:
Modified:
llvm/include/llvm/ProfileData/SampleProfReader.h
llvm/lib/ProfileData/SampleProfReader.cpp
llvm/unittests/ProfileData/SampleProfTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index e4655fb877d8a..251e6a8f4843a 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -908,6 +908,78 @@ class FuncOffsetHashTableInfo {
}
};
+/// Tags to select the initialization mode of SampleProfileFuncOffsetTable.
+struct InMemoryModeT {};
+struct OnDiskModeT {};
+inline constexpr InMemoryModeT InMemoryMode{};
+inline constexpr OnDiskModeT OnDiskMode{};
+
+/// A unified wrapper representing the function offset table.
+///
+/// This class abstracts away the physical representation of the offset table,
+/// which can either be:
+///
+/// - An llvm::DenseMap mapping function GUIDs (or context hashes) to their
+/// profile offsets, populated when reading the array of offsets in
+/// context-sensitive (CS) profiles or version 103 profiles.
+///
+/// - An OnDiskIterableChainedHashTable providing the same mapping directly from
+/// the file in (non-context-sensitive) version 104 profiles.
+///
+/// It exposes a single, type-agnostic lookup interface, shielding the reader
+/// from the underlying container types. To prevent hybrid-state corruption, the
+/// table's mode is locked at construction time, and assertions prevent
+/// modification in on-disk mode.
+class SampleProfileFuncOffsetTable {
+public:
+ using OnDiskTableType =
+ llvm::OnDiskIterableChainedHashTable<FuncOffsetHashTableInfo>;
+
+ explicit SampleProfileFuncOffsetTable(InMemoryModeT,
+ size_t InitialCapacity = 0) {
+ InMemoryTable.reserve(InitialCapacity);
+ }
+
+ /// Insert a function GUID and its profile offset into the in-memory map.
+ /// Enforces that the on-disk table must not have been set first.
+ void insert(uint64_t GUID, uint64_t Offset) {
+ assert(!OnDiskTable &&
+ "Cannot insert in-memory elements after on-disk table has been set");
+ InMemoryTable[GUID] = Offset;
+ }
+
+ /// Instantiate the on-disk chained hash table using raw stream pointers.
+ SampleProfileFuncOffsetTable(OnDiskModeT, const uint8_t *Buckets,
+ const uint8_t *Payload, const uint8_t *Base) {
+ OnDiskTable.reset(OnDiskTableType::Create(Buckets, Payload, Base));
+ }
+
+ /// Query the offset table for the profile offset associated with the given
+ /// GUID. Returns the offset if found, or std::nullopt if the key is missing.
+ std::optional<uint64_t> lookup(uint64_t GUID) const {
+ if (OnDiskTable) {
+ auto Iter = OnDiskTable->find(GUID);
+ if (Iter != OnDiskTable->end())
+ return *Iter;
+ } else {
+ auto Iter = InMemoryTable.find(GUID);
+ if (Iter != InMemoryTable.end())
+ return Iter->second;
+ }
+ return std::nullopt;
+ }
+
+ /// Clear the in-memory map and release the on-disk table.
+ void clear() {
+ InMemoryTable.clear();
+ OnDiskTable.reset();
+ }
+
+private:
+ llvm::DenseMap<hash_code, uint64_t> InMemoryTable;
+ std::unique_ptr<OnDiskTableType> OnDiskTable;
+};
+
/// SampleProfileReaderExtBinaryBase/SampleProfileWriterExtBinaryBase defines
/// the basic structure of the extensible binary format.
/// The format is organized in sections except the magic and version number
@@ -971,7 +1043,7 @@ class LLVM_ABI SampleProfileReaderExtBinaryBase
/// The table mapping from a function context's MD5 to the offset of its
/// FunctionSample towards file start.
/// At most one of FuncOffsetTable and FuncOffsetList is populated.
- DenseMap<hash_code, uint64_t> FuncOffsetTable;
+ std::optional<SampleProfileFuncOffsetTable> FuncOffsetTable;
/// The list version of FuncOffsetTable. This is used if every entry is
/// being accessed.
@@ -983,7 +1055,9 @@ class LLVM_ABI SampleProfileReaderExtBinaryBase
public:
SampleProfileReaderExtBinaryBase(std::unique_ptr<MemoryBuffer> B,
LLVMContext &C, SampleProfileFormat Format)
- : SampleProfileReaderBinary(std::move(B), C, Format) {}
+ : SampleProfileReaderBinary(std::move(B), C, Format) {
+ FuncOffsetTable.emplace(InMemoryMode);
+ }
/// Read sample profiles in extensible format from the associated file.
std::error_code readImpl() override;
diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp
index e5d28a75ce69c..14fdaf6581efe 100644
--- a/llvm/lib/ProfileData/SampleProfReader.cpp
+++ b/llvm/lib/ProfileData/SampleProfReader.cpp
@@ -984,7 +984,7 @@ bool SampleProfileReaderExtBinaryBase::collectFuncsFromModule() {
std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() {
// If there are more than one function offset section, the profile associated
// with the previous section has to be done reading before next one is read.
- FuncOffsetTable.clear();
+ FuncOffsetTable.reset();
FuncOffsetList.clear();
auto Size = readNumber<uint64_t>();
@@ -995,7 +995,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() {
if (UseFuncOffsetList)
FuncOffsetList.reserve(*Size);
else
- FuncOffsetTable.reserve(*Size);
+ FuncOffsetTable.emplace(InMemoryMode, *Size);
for (uint64_t I = 0; I < *Size; ++I) {
auto FContextHash(readSampleContextFromTable());
@@ -1012,7 +1012,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() {
else
// Because Porfiles replace existing value with new value if collision
// happens, we also use the latest offset so that they are consistent.
- FuncOffsetTable[Hash] = *Offset;
+ FuncOffsetTable->insert(Hash, *Offset);
}
return sampleprof_error::success;
@@ -1074,12 +1074,11 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles(
assert(!useFuncOffsetList());
for (auto Name : FuncsToUse) {
auto GUID = MD5Hash(Name);
- auto iter = FuncOffsetTable.find(GUID);
- if (iter == FuncOffsetTable.end())
- continue;
- const uint8_t *FuncProfileAddr = Start + iter->second;
- if (std::error_code EC = readFuncProfile(FuncProfileAddr, Profiles))
- return EC;
+ if (auto Offset = FuncOffsetTable->lookup(GUID)) {
+ const uint8_t *FuncProfileAddr = Start + *Offset;
+ if (std::error_code EC = readFuncProfile(FuncProfileAddr, Profiles))
+ return EC;
+ }
}
} else if (Remapper) {
assert(useFuncOffsetList());
@@ -1096,13 +1095,11 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles(
} else {
assert(!useFuncOffsetList());
for (auto Name : FuncsToUse) {
-
- auto iter = FuncOffsetTable.find(MD5Hash(Name));
- if (iter == FuncOffsetTable.end())
- continue;
- const uint8_t *FuncProfileAddr = Start + iter->second;
- if (std::error_code EC = readFuncProfile(FuncProfileAddr, Profiles))
- return EC;
+ if (auto Offset = FuncOffsetTable->lookup(MD5Hash(Name))) {
+ const uint8_t *FuncProfileAddr = Start + *Offset;
+ if (std::error_code EC = readFuncProfile(FuncProfileAddr, Profiles))
+ return EC;
+ }
}
}
diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index 00f5627683616..0158b584f8504 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -534,4 +534,66 @@ TEST_F(SampleProfTest, FuncOffsetHashTable) {
ASSERT_TRUE(Iter == Table->end());
}
+TEST_F(SampleProfTest, SampleProfileFuncOffsetTableInMemory) {
+ SampleProfileFuncOffsetTable Table(InMemoryMode, 2);
+
+ // Test empty table
+ EXPECT_EQ(Table.lookup(0x1111ULL), std::nullopt);
+
+ // Test insert and lookup
+ Table.insert(0x11112222ULL, 100);
+ Table.insert(0x33334444ULL, 200);
+
+ EXPECT_EQ(Table.lookup(0x11112222ULL), 100);
+ EXPECT_EQ(Table.lookup(0x33334444ULL), 200);
+ EXPECT_EQ(Table.lookup(0x55556666ULL), std::nullopt);
+
+ // Test clear
+ Table.clear();
+ EXPECT_EQ(Table.lookup(0x11112222ULL), std::nullopt);
+}
+
+TEST_F(SampleProfTest, SampleProfileFuncOffsetTableOnDisk) {
+ std::vector<std::pair<uint64_t, uint64_t>> TestData = {
+ {0x1111111122222222ULL, 100},
+ {0x3333333344444444ULL, 250},
+ {0x5555555566666666ULL, 1000},
+ };
+
+ SmallVector<char, 128> Buffer;
+ raw_svector_ostream OS(Buffer);
+
+ FuncOffsetHashTableWriterInfo WriterInfo;
+ OnDiskChainedHashTableGenerator<FuncOffsetHashTableWriterInfo> Generator;
+
+ for (const auto &[Name, Offset] : TestData)
+ Generator.insert(Name, Offset);
+
+ // Add padding to avoid bucket offset 0.
+ OS.write("PAD ", 4);
+ uint32_t BucketTableOffset = Generator.Emit(OS, WriterInfo);
+
+ const unsigned char *Start =
+ reinterpret_cast<const unsigned char *>(Buffer.data());
+
+ const unsigned char *Buckets = Start + BucketTableOffset;
+ const unsigned char *Payload = Start + 4;
+
+ SampleProfileFuncOffsetTable Table(OnDiskMode, Buckets, Payload, Start);
+
+ // Test lookup
+ for (const auto &[Name, Offset] : TestData) {
+ auto LookupResult = Table.lookup(Name);
+ ASSERT_TRUE(LookupResult.has_value());
+ EXPECT_EQ(*LookupResult, Offset);
+ }
+
+ // Test non-existent key
+ EXPECT_EQ(Table.lookup(0x9999999999999999ULL), std::nullopt);
+
+ // Test clear
+ Table.clear();
+ EXPECT_EQ(Table.lookup(0x1111111122222222ULL), std::nullopt);
+}
+
} // end anonymous namespace
More information about the llvm-commits
mailing list