Skip to content

Commit 8137bd9

Browse files
[memprof] Use CSId to construct MemProfRecord (#88362)
We are in the process of referring to call stacks with CallStackId in IndexedMemProfRecord and IndexedAllocationInfo instead of holding call stacks inline (both in memory and the serialized format). Doing so deduplicates call stacks and reduces the MemProf profile file size. Before we can eliminate the two fields holding call stacks inline: - IndexedAllocationInfo::CallStack - IndexedMemProfRecord::CallSites we need to eliminate all the read operations on them. This patch is a step toward that direction. Specifically, we eliminate the read operations in the context of MemProfReader and RawMemProfReader. A subsequent patch will eliminate the read operations during the serialization.
1 parent 22bba85 commit 8137bd9

File tree

5 files changed

+153
-4
lines changed

5 files changed

+153
-4
lines changed

llvm/include/llvm/ProfileData/MemProf.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
namespace llvm {
1717
namespace memprof {
1818

19+
struct MemProfRecord;
20+
1921
// The versions of the indexed MemProf format
2022
enum IndexedVersion : uint64_t {
2123
// Version 0: This version didn't have a version field.
@@ -392,6 +394,12 @@ struct IndexedMemProfRecord {
392394
const unsigned char *Buffer,
393395
IndexedVersion Version);
394396

397+
// Convert IndexedMemProfRecord to MemProfRecord. Callback is used to
398+
// translate CallStackId to call stacks with frames inline.
399+
MemProfRecord toMemProfRecord(
400+
std::function<const llvm::SmallVector<Frame>(const CallStackId)> Callback)
401+
const;
402+
395403
// Returns the GUID for the function name after canonicalization. For
396404
// memprof, we remove any .llvm suffix added by LTO. MemProfRecords are
397405
// mapped to functions using this GUID.

llvm/include/llvm/ProfileData/MemProfReader.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,20 @@ class MemProfReader {
7070
Callback =
7171
std::bind(&MemProfReader::idToFrame, this, std::placeholders::_1);
7272

73+
auto CallStackCallback = [&](CallStackId CSId) {
74+
llvm::SmallVector<Frame> CallStack;
75+
auto Iter = CSIdToCallStack.find(CSId);
76+
assert(Iter != CSIdToCallStack.end());
77+
for (FrameId Id : Iter->second)
78+
CallStack.push_back(Callback(Id));
79+
return CallStack;
80+
};
81+
7382
const IndexedMemProfRecord &IndexedRecord = Iter->second;
74-
GuidRecord = {Iter->first, MemProfRecord(IndexedRecord, Callback)};
83+
GuidRecord = {
84+
Iter->first,
85+
IndexedRecord.toMemProfRecord(CallStackCallback),
86+
};
7587
Iter++;
7688
return Error::success();
7789
}
@@ -84,9 +96,7 @@ class MemProfReader {
8496
// Initialize the MemProfReader with the frame mappings and profile contents.
8597
MemProfReader(
8698
llvm::DenseMap<FrameId, Frame> FrameIdMap,
87-
llvm::MapVector<GlobalValue::GUID, IndexedMemProfRecord> ProfData)
88-
: IdToFrame(std::move(FrameIdMap)),
89-
FunctionProfileData(std::move(ProfData)) {}
99+
llvm::MapVector<GlobalValue::GUID, IndexedMemProfRecord> ProfData);
90100

91101
protected:
92102
// A helper method to extract the frame from the IdToFrame map.
@@ -97,6 +107,8 @@ class MemProfReader {
97107
}
98108
// A mapping from FrameId (a hash of the contents) to the frame.
99109
llvm::DenseMap<FrameId, Frame> IdToFrame;
110+
// A mapping from CallStackId to the call stack.
111+
llvm::DenseMap<CallStackId, llvm::SmallVector<FrameId>> CSIdToCallStack;
100112
// A mapping from function GUID, hash of the canonical function symbol to the
101113
// memprof profile data for that function, i.e allocation and callsite info.
102114
llvm::MapVector<GlobalValue::GUID, IndexedMemProfRecord> FunctionProfileData;

llvm/lib/ProfileData/MemProf.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,24 @@ IndexedMemProfRecord::deserialize(const MemProfSchema &Schema,
224224
llvm_unreachable("unsupported MemProf version");
225225
}
226226

227+
MemProfRecord IndexedMemProfRecord::toMemProfRecord(
228+
std::function<const llvm::SmallVector<Frame>(const CallStackId)> Callback)
229+
const {
230+
MemProfRecord Record;
231+
232+
for (const memprof::IndexedAllocationInfo &IndexedAI : AllocSites) {
233+
memprof::AllocationInfo AI;
234+
AI.Info = IndexedAI.Info;
235+
AI.CallStack = Callback(IndexedAI.CSId);
236+
Record.AllocSites.push_back(AI);
237+
}
238+
239+
for (memprof::CallStackId CSId : CallSiteIds)
240+
Record.CallSites.push_back(Callback(CSId));
241+
242+
return Record;
243+
}
244+
227245
GlobalValue::GUID IndexedMemProfRecord::getGUID(const StringRef FunctionName) {
228246
// Canonicalize the function name to drop suffixes such as ".llvm.". Note
229247
// we do not drop any ".__uniq." suffixes, as getCanonicalFnName does not drop

llvm/lib/ProfileData/MemProfReader.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,28 @@ std::string getBuildIdString(const SegmentEntry &Entry) {
183183
}
184184
} // namespace
185185

186+
MemProfReader::MemProfReader(
187+
llvm::DenseMap<FrameId, Frame> FrameIdMap,
188+
llvm::MapVector<GlobalValue::GUID, IndexedMemProfRecord> ProfData)
189+
: IdToFrame(std::move(FrameIdMap)),
190+
FunctionProfileData(std::move(ProfData)) {
191+
// Populate CSId in each IndexedAllocationInfo and IndexedMemProfRecord
192+
// while storing CallStack in CSIdToCallStack.
193+
for (auto &KV : FunctionProfileData) {
194+
IndexedMemProfRecord &Record = KV.second;
195+
for (auto &AS : Record.AllocSites) {
196+
CallStackId CSId = hashCallStack(AS.CallStack);
197+
AS.CSId = CSId;
198+
CSIdToCallStack.insert({CSId, AS.CallStack});
199+
}
200+
for (auto &CS : Record.CallSites) {
201+
CallStackId CSId = hashCallStack(CS);
202+
Record.CallSiteIds.push_back(CSId);
203+
CSIdToCallStack.insert({CSId, CS});
204+
}
205+
}
206+
}
207+
186208
Expected<std::unique_ptr<RawMemProfReader>>
187209
RawMemProfReader::create(const Twine &Path, const StringRef ProfiledBinary,
188210
bool KeepName) {
@@ -445,6 +467,7 @@ Error RawMemProfReader::mapRawProfileToRecords() {
445467
}
446468

447469
CallStackId CSId = hashCallStack(Callstack);
470+
CSIdToCallStack.insert({CSId, Callstack});
448471

449472
// We attach the memprof record to each function bottom-up including the
450473
// first non-inline frame.
@@ -467,7 +490,10 @@ Error RawMemProfReader::mapRawProfileToRecords() {
467490
auto Result = FunctionProfileData.insert({Id, IndexedMemProfRecord()});
468491
IndexedMemProfRecord &Record = Result.first->second;
469492
for (LocationPtr Loc : Locs) {
493+
CallStackId CSId = hashCallStack(*Loc);
494+
CSIdToCallStack.insert({CSId, *Loc});
470495
Record.CallSites.push_back(*Loc);
496+
Record.CallSiteIds.push_back(CSId);
471497
}
472498
}
473499

llvm/unittests/ProfileData/MemProfTest.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ using ::llvm::DILineInfo;
2121
using ::llvm::DILineInfoSpecifier;
2222
using ::llvm::DILocal;
2323
using ::llvm::StringRef;
24+
using ::llvm::memprof::CallStackId;
2425
using ::llvm::memprof::CallStackMap;
2526
using ::llvm::memprof::Frame;
2627
using ::llvm::memprof::FrameId;
28+
using ::llvm::memprof::IndexedAllocationInfo;
2729
using ::llvm::memprof::IndexedMemProfRecord;
2830
using ::llvm::memprof::MemInfoBlock;
2931
using ::llvm::memprof::MemProfReader;
@@ -36,6 +38,7 @@ using ::llvm::memprof::SegmentEntry;
3638
using ::llvm::object::SectionedAddress;
3739
using ::llvm::symbolize::SymbolizableModule;
3840
using ::testing::Return;
41+
using ::testing::SizeIs;
3942

4043
class MockSymbolizer : public SymbolizableModule {
4144
public:
@@ -432,4 +435,86 @@ TEST(MemProf, BaseMemProfReader) {
432435
EXPECT_THAT(Records[0].AllocSites[0].CallStack[1],
433436
FrameContains("bar", 10U, 2U, false));
434437
}
438+
439+
TEST(MemProf, IndexedMemProfRecordToMemProfRecord) {
440+
// Verify that MemProfRecord can be constructed from IndexedMemProfRecord with
441+
// CallStackIds only.
442+
443+
llvm::DenseMap<FrameId, Frame> FrameIdMap;
444+
Frame F1(1, 0, 0, false);
445+
Frame F2(2, 0, 0, false);
446+
Frame F3(3, 0, 0, false);
447+
Frame F4(4, 0, 0, false);
448+
FrameIdMap.insert({F1.hash(), F1});
449+
FrameIdMap.insert({F2.hash(), F2});
450+
FrameIdMap.insert({F3.hash(), F3});
451+
FrameIdMap.insert({F4.hash(), F4});
452+
453+
llvm::DenseMap<CallStackId, llvm::SmallVector<FrameId>> CallStackIdMap;
454+
llvm::SmallVector<FrameId> CS1 = {F1.hash(), F2.hash()};
455+
llvm::SmallVector<FrameId> CS2 = {F1.hash(), F3.hash()};
456+
llvm::SmallVector<FrameId> CS3 = {F2.hash(), F3.hash()};
457+
llvm::SmallVector<FrameId> CS4 = {F2.hash(), F4.hash()};
458+
CallStackIdMap.insert({llvm::memprof::hashCallStack(CS1), CS1});
459+
CallStackIdMap.insert({llvm::memprof::hashCallStack(CS2), CS2});
460+
CallStackIdMap.insert({llvm::memprof::hashCallStack(CS3), CS3});
461+
CallStackIdMap.insert({llvm::memprof::hashCallStack(CS4), CS4});
462+
463+
IndexedMemProfRecord IndexedRecord;
464+
IndexedAllocationInfo AI;
465+
AI.CSId = llvm::memprof::hashCallStack(CS1);
466+
IndexedRecord.AllocSites.push_back(AI);
467+
AI.CSId = llvm::memprof::hashCallStack(CS2);
468+
IndexedRecord.AllocSites.push_back(AI);
469+
IndexedRecord.CallSiteIds.push_back(llvm::memprof::hashCallStack(CS3));
470+
IndexedRecord.CallSiteIds.push_back(llvm::memprof::hashCallStack(CS4));
471+
472+
bool CSIdMissing = false;
473+
bool FrameIdMissing = false;
474+
475+
auto Callback = [&](CallStackId CSId) -> llvm::SmallVector<Frame> {
476+
llvm::SmallVector<Frame> CallStack;
477+
llvm::SmallVector<FrameId> FrameIds;
478+
479+
auto Iter = CallStackIdMap.find(CSId);
480+
if (Iter == CallStackIdMap.end())
481+
CSIdMissing = true;
482+
else
483+
FrameIds = Iter->second;
484+
485+
for (FrameId Id : FrameIds) {
486+
Frame F(0, 0, 0, false);
487+
auto Iter = FrameIdMap.find(Id);
488+
if (Iter == FrameIdMap.end())
489+
FrameIdMissing = true;
490+
else
491+
F = Iter->second;
492+
CallStack.push_back(F);
493+
}
494+
495+
return CallStack;
496+
};
497+
498+
MemProfRecord Record = IndexedRecord.toMemProfRecord(Callback);
499+
500+
// Make sure that all lookups are successful.
501+
ASSERT_FALSE(CSIdMissing);
502+
ASSERT_FALSE(FrameIdMissing);
503+
504+
// Verify the contents of Record.
505+
ASSERT_THAT(Record.AllocSites, SizeIs(2));
506+
ASSERT_THAT(Record.AllocSites[0].CallStack, SizeIs(2));
507+
EXPECT_EQ(Record.AllocSites[0].CallStack[0].hash(), F1.hash());
508+
EXPECT_EQ(Record.AllocSites[0].CallStack[1].hash(), F2.hash());
509+
ASSERT_THAT(Record.AllocSites[1].CallStack, SizeIs(2));
510+
EXPECT_EQ(Record.AllocSites[1].CallStack[0].hash(), F1.hash());
511+
EXPECT_EQ(Record.AllocSites[1].CallStack[1].hash(), F3.hash());
512+
ASSERT_THAT(Record.CallSites, SizeIs(2));
513+
ASSERT_THAT(Record.CallSites[0], SizeIs(2));
514+
EXPECT_EQ(Record.CallSites[0][0].hash(), F2.hash());
515+
EXPECT_EQ(Record.CallSites[0][1].hash(), F3.hash());
516+
ASSERT_THAT(Record.CallSites[1], SizeIs(2));
517+
EXPECT_EQ(Record.CallSites[1][0].hash(), F2.hash());
518+
EXPECT_EQ(Record.CallSites[1][1].hash(), F4.hash());
519+
}
435520
} // namespace

0 commit comments

Comments
 (0)