Skip to content

Commit c348e26

Browse files
[memprof] Use CallStackRadixTreeBuilder in the V3 format (#94708)
This patch integrates CallStackRadixTreeBuilder into the V3 format, reducing the profile size to about 27% of the V2 profile size. - Serialization: writeMemProfCallStackArray just needs to write out the radix tree array prepared by CallStackRadixTreeBuilder. Mappings from CallStackIds to LinearCallStackIds are moved by new function CallStackRadixTreeBuilder::takeCallStackPos. - Deserialization: Deserializing a call stack is the same as deserializing an array encoded in the obvious manner -- the length followed by the payload, except that we need to follow a pointer to the parent to take advantage of common prefixes once in a while. This patch teaches LinearCallStackIdConverter to how to handle those pointers.
1 parent 55bdb36 commit c348e26

File tree

3 files changed

+24
-24
lines changed

3 files changed

+24
-24
lines changed

llvm/include/llvm/ProfileData/MemProf.h

+13-5
Original file line numberDiff line numberDiff line change
@@ -900,9 +900,18 @@ struct LinearCallStackIdConverter {
900900
Frames.reserve(NumFrames);
901901
for (; NumFrames; --NumFrames) {
902902
LinearFrameId Elem =
903-
support::endian::readNext<LinearFrameId, llvm::endianness::little>(
904-
Ptr);
903+
support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
904+
// Follow a pointer to the parent, if any. See comments below on
905+
// CallStackRadixTreeBuilder for the description of the radix tree format.
906+
if (static_cast<std::make_signed_t<LinearFrameId>>(Elem) < 0) {
907+
Ptr += (-Elem) * sizeof(LinearFrameId);
908+
Elem =
909+
support::endian::read<LinearFrameId, llvm::endianness::little>(Ptr);
910+
}
911+
// We shouldn't encounter another pointer.
912+
assert(static_cast<std::make_signed_t<LinearFrameId>>(Elem) >= 0);
905913
Frames.push_back(FrameIdToFrame(Elem));
914+
Ptr += sizeof(LinearFrameId);
906915
}
907916

908917
return Frames;
@@ -1024,9 +1033,8 @@ class CallStackRadixTreeBuilder {
10241033

10251034
const std::vector<LinearFrameId> &getRadixArray() const { return RadixArray; }
10261035

1027-
const llvm::DenseMap<CallStackId, LinearCallStackId> &
1028-
getCallStackPos() const {
1029-
return CallStackPos;
1036+
llvm::DenseMap<CallStackId, LinearCallStackId> takeCallStackPos() {
1037+
return std::move(CallStackPos);
10301038
}
10311039
};
10321040

llvm/lib/ProfileData/InstrProfWriter.cpp

+7-15
Original file line numberDiff line numberDiff line change
@@ -547,19 +547,11 @@ writeMemProfCallStackArray(
547547
llvm::DenseMap<memprof::CallStackId, memprof::LinearCallStackId>
548548
MemProfCallStackIndexes;
549549

550-
MemProfCallStackIndexes.reserve(MemProfCallStackData.size());
551-
uint64_t CallStackBase = OS.tell();
552-
for (const auto &[CSId, CallStack] : MemProfCallStackData) {
553-
memprof::LinearCallStackId CallStackIndex =
554-
(OS.tell() - CallStackBase) / sizeof(memprof::LinearCallStackId);
555-
MemProfCallStackIndexes.insert({CSId, CallStackIndex});
556-
const llvm::SmallVector<memprof::FrameId> CS = CallStack;
557-
OS.write32(CS.size());
558-
for (const auto F : CS) {
559-
assert(MemProfFrameIndexes.contains(F));
560-
OS.write32(MemProfFrameIndexes[F]);
561-
}
562-
}
550+
memprof::CallStackRadixTreeBuilder Builder;
551+
Builder.build(std::move(MemProfCallStackData), MemProfFrameIndexes);
552+
for (auto I : Builder.getRadixArray())
553+
OS.write32(I);
554+
MemProfCallStackIndexes = Builder.takeCallStackPos();
563555

564556
// Release the memory of this vector as it is no longer needed.
565557
MemProfCallStackData.clear();
@@ -695,8 +687,8 @@ static Error writeMemProfV2(ProfOStream &OS,
695687
// uint64_t Schema entry 1
696688
// ....
697689
// uint64_t Schema entry N - 1
698-
// OnDiskChainedHashTable MemProfFrameData
699-
// OnDiskChainedHashTable MemProfCallStackData
690+
// Frames serialized one after another
691+
// Call stacks encoded as a radix tree
700692
// OnDiskChainedHashTable MemProfRecordData
701693
static Error writeMemProfV3(ProfOStream &OS,
702694
memprof::IndexedMemProfData &MemProfData,

llvm/unittests/ProfileData/MemProfTest.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ TEST(MemProf, RadixTreeBuilderEmpty) {
670670
llvm::memprof::CallStackRadixTreeBuilder Builder;
671671
Builder.build(std::move(MemProfCallStackData), MemProfFrameIndexes);
672672
ASSERT_THAT(Builder.getRadixArray(), testing::IsEmpty());
673-
const auto &Mappings = Builder.getCallStackPos();
673+
const auto Mappings = Builder.takeCallStackPos();
674674
ASSERT_THAT(Mappings, testing::IsEmpty());
675675
}
676676

@@ -689,7 +689,7 @@ TEST(MemProf, RadixTreeBuilderOne) {
689689
2U, // MemProfFrameIndexes[12]
690690
1U // MemProfFrameIndexes[11]
691691
}));
692-
const auto &Mappings = Builder.getCallStackPos();
692+
const auto Mappings = Builder.takeCallStackPos();
693693
ASSERT_THAT(Mappings, SizeIs(1));
694694
EXPECT_THAT(Mappings, testing::Contains(testing::Pair(
695695
llvm::memprof::hashCallStack(CS1), 0U)));
@@ -715,7 +715,7 @@ TEST(MemProf, RadixTreeBuilderTwo) {
715715
2U, // MemProfFrameIndexes[12]
716716
1U // MemProfFrameIndexes[11]
717717
}));
718-
const auto &Mappings = Builder.getCallStackPos();
718+
const auto Mappings = Builder.takeCallStackPos();
719719
ASSERT_THAT(Mappings, SizeIs(2));
720720
EXPECT_THAT(Mappings, testing::Contains(testing::Pair(
721721
llvm::memprof::hashCallStack(CS1), 0U)));
@@ -758,7 +758,7 @@ TEST(MemProf, RadixTreeBuilderSuccessiveJumps) {
758758
2U, // MemProfFrameIndexes[12]
759759
1U // MemProfFrameIndexes[11]
760760
}));
761-
const auto &Mappings = Builder.getCallStackPos();
761+
const auto Mappings = Builder.takeCallStackPos();
762762
ASSERT_THAT(Mappings, SizeIs(4));
763763
EXPECT_THAT(Mappings, testing::Contains(testing::Pair(
764764
llvm::memprof::hashCallStack(CS1), 0U)));

0 commit comments

Comments
 (0)