Skip to content

Commit 55bbb36

Browse files
teresajohnsongoogle-yfyang
authored andcommitted
[MemProf] Add basic summary section support (llvm#141805)
This patch adds support for a basic MemProf summary section, which is built along with the indexed MemProf profile (e.g. when reading the raw or YAML profiles), and serialized through the indexed profile just after the header. Currently only 6 fields are written, specifically the number of contexts (total, cold, hot), and the max context size (cold, warm, hot). To support forwards and backwards compatibility for added fields in the indexed profile, the number of fields serialized first. The code is written to support forwards compatibility (reading newer profiles with additional summary fields), and comments indicate how to implement backwards compatibility (reading older profiles with fewer summary fields) as needed. Support is added to print the summary as YAML comments when displaying both the raw and indexed profiles via `llvm-profdata show`. Because they are YAML comments, the YAML reader ignores these (the summary is always recomputed when building the indexed profile as described above). This necessitated moving some options and a couple of interfaces out of Analysis/MemoryProfileInfo.cpp and into the new ProfileData/MemProfSummary.cpp file, as we need to classify context hotness earlier and also compute context ids to build the summary from older indexed profiles.
1 parent c9a4b28 commit 55bbb36

File tree

20 files changed

+507
-154
lines changed

20 files changed

+507
-154
lines changed

llvm/include/llvm/Analysis/MemoryProfileInfo.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@
2121
namespace llvm {
2222
namespace memprof {
2323

24-
/// Return the allocation type for a given set of memory profile values.
25-
LLVM_ABI AllocationType getAllocType(uint64_t TotalLifetimeAccessDensity,
26-
uint64_t AllocCount,
27-
uint64_t TotalLifetime);
28-
2924
/// Build callstack metadata from the provided list of call stack ids. Returns
3025
/// the resulting metadata node.
3126
LLVM_ABI MDNode *buildCallstackMetadata(ArrayRef<uint64_t> CallStack,

llvm/include/llvm/ProfileData/IndexedMemProfData.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
namespace llvm {
2626
namespace memprof {
27+
class MemProfSummary;
2728
struct IndexedMemProfData {
2829
// A map to hold memprof data per function. The lower 64 bits obtained from
2930
// the md5 hash of the function name is used to index into the map.
@@ -89,7 +90,7 @@ struct IndexedMemProfData {
8990
Error writeMemProf(
9091
ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
9192
memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema,
92-
std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData);
93-
93+
std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,
94+
memprof::MemProfSummary *MemProfSum);
9495
} // namespace llvm
9596
#endif

llvm/include/llvm/ProfileData/InstrProfReader.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/ProfileData/InstrProf.h"
2323
#include "llvm/ProfileData/InstrProfCorrelator.h"
2424
#include "llvm/ProfileData/MemProf.h"
25+
#include "llvm/ProfileData/MemProfSummary.h"
2526
#include "llvm/ProfileData/MemProfYAML.h"
2627
#include "llvm/Support/Endian.h"
2728
#include "llvm/Support/Error.h"
@@ -690,6 +691,8 @@ class IndexedMemProfReader {
690691
/// The MemProf version.
691692
memprof::IndexedVersion Version =
692693
static_cast<memprof::IndexedVersion>(memprof::MinimumSupportedVersion);
694+
/// MemProf summary (if available, version >= 4).
695+
std::unique_ptr<memprof::MemProfSummary> MemProfSum;
693696
/// MemProf profile schema (if available).
694697
memprof::MemProfSchema Schema;
695698
/// MemProf record profile data on-disk indexed via llvm::md5(FunctionName).
@@ -725,6 +728,8 @@ class IndexedMemProfReader {
725728

726729
// Return the entire MemProf profile.
727730
memprof::AllMemProfData getAllMemProfData() const;
731+
732+
memprof::MemProfSummary *getSummary() const { return MemProfSum.get(); }
728733
};
729734

730735
/// Reader for the indexed binary instrprof format.
@@ -887,6 +892,11 @@ class IndexedInstrProfReader : public InstrProfReader {
887892
}
888893
}
889894

895+
/// Return the MemProf summary. Will be null if unavailable (version < 4).
896+
memprof::MemProfSummary *getMemProfSummary() const {
897+
return MemProfReader.getSummary();
898+
}
899+
890900
Error readBinaryIds(std::vector<llvm::object::BuildID> &BinaryIds) override;
891901
Error printBinaryIds(raw_ostream &OS) override;
892902
};

llvm/include/llvm/ProfileData/InstrProfWriter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/ProfileData/DataAccessProf.h"
2323
#include "llvm/ProfileData/IndexedMemProfData.h"
2424
#include "llvm/ProfileData/InstrProf.h"
25+
#include "llvm/ProfileData/MemProfSummaryBuilder.h"
2526
#include "llvm/Support/Error.h"
2627
#include <cstdint>
2728
#include <memory>
@@ -84,6 +85,10 @@ class InstrProfWriter {
8485

8586
std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData;
8687

88+
// MemProf summary builder to which records are added as MemProf data is added
89+
// to the writer.
90+
memprof::MemProfSummaryBuilder MemProfSumBuilder;
91+
8792
public:
8893
// For memprof testing, random hotness can be assigned to the contexts if
8994
// MemprofGenerateRandomHotness is enabled. The random seed can be either
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===- MemProfSummary.h - MemProf summary support ---------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains MemProf summary support and related interfaces.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_PROFILEDATA_MEMPROFSUMMARY_H
14+
#define LLVM_PROFILEDATA_MEMPROFSUMMARY_H
15+
16+
#include "llvm/IR/ModuleSummaryIndex.h"
17+
#include "llvm/ProfileData/InstrProf.h"
18+
#include "llvm/ProfileData/MemProf.h"
19+
20+
namespace llvm {
21+
namespace memprof {
22+
23+
/// Return the allocation type for a given set of memory profile values.
24+
AllocationType getAllocType(uint64_t TotalLifetimeAccessDensity,
25+
uint64_t AllocCount, uint64_t TotalLifetime);
26+
27+
/// Helper to generate a single hash id for a given callstack, used for emitting
28+
/// matching statistics and useful for uniquing such statistics across modules.
29+
/// Also used to dedup contexts when computing the summary.
30+
uint64_t computeFullStackId(ArrayRef<Frame> CallStack);
31+
32+
class MemProfSummary {
33+
private:
34+
/// The number of summary fields below, which is used to enable some forwards
35+
/// and backwards compatibility for the summary when serialized in the indexed
36+
/// MemProf format. As long as no existing summary fields are removed or
37+
/// reordered, and new summary fields are added after existing summary fields,
38+
/// the MemProf indexed profile version does not need to be bumped to
39+
/// accommodate new summary fields.
40+
static constexpr unsigned NumSummaryFields = 6;
41+
42+
const uint64_t NumContexts, NumColdContexts, NumHotContexts;
43+
const uint64_t MaxColdTotalSize, MaxWarmTotalSize, MaxHotTotalSize;
44+
45+
public:
46+
MemProfSummary(uint64_t NumContexts, uint64_t NumColdContexts,
47+
uint64_t NumHotContexts, uint64_t MaxColdTotalSize,
48+
uint64_t MaxWarmTotalSize, uint64_t MaxHotTotalSize)
49+
: NumContexts(NumContexts), NumColdContexts(NumColdContexts),
50+
NumHotContexts(NumHotContexts), MaxColdTotalSize(MaxColdTotalSize),
51+
MaxWarmTotalSize(MaxWarmTotalSize), MaxHotTotalSize(MaxHotTotalSize) {}
52+
53+
static constexpr unsigned getNumSummaryFields() { return NumSummaryFields; }
54+
uint64_t getNumContexts() const { return NumContexts; }
55+
uint64_t getNumColdContexts() const { return NumColdContexts; }
56+
uint64_t getNumHotContexts() const { return NumHotContexts; }
57+
uint64_t getMaxColdTotalSize() const { return MaxColdTotalSize; }
58+
uint64_t getMaxWarmTotalSize() const { return MaxWarmTotalSize; }
59+
uint64_t getMaxHotTotalSize() const { return MaxHotTotalSize; }
60+
void printSummaryYaml(raw_ostream &OS) const;
61+
/// Write to indexed MemProf profile.
62+
void write(ProfOStream &OS) const;
63+
/// Read from indexed MemProf profile.
64+
static std::unique_ptr<MemProfSummary> deserialize(const unsigned char *&);
65+
};
66+
67+
} // namespace memprof
68+
} // namespace llvm
69+
70+
#endif // LLVM_PROFILEDATA_MEMPROFSUMMARY_H
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===- MemProfSummaryBuilder.h - MemProf summary building -------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains MemProf summary builder.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_PROFILEDATA_MEMPROFSUMMARYBUILDER_H
14+
#define LLVM_PROFILEDATA_MEMPROFSUMMARYBUILDER_H
15+
16+
#include "llvm/ProfileData/MemProf.h"
17+
#include "llvm/ProfileData/MemProfSummary.h"
18+
19+
namespace llvm {
20+
namespace memprof {
21+
22+
class MemProfSummaryBuilder {
23+
private:
24+
// The set of full context IDs that we've recorded so far. This is needed to
25+
// dedup the MIBs, which are duplicated between functions containing inline
26+
// instances of the same allocations.
27+
DenseSet<uint64_t> Contexts;
28+
29+
void addRecord(uint64_t, const PortableMemInfoBlock &);
30+
31+
protected:
32+
uint64_t MaxColdTotalSize = 0;
33+
uint64_t MaxWarmTotalSize = 0;
34+
uint64_t MaxHotTotalSize = 0;
35+
uint64_t NumContexts = 0;
36+
uint64_t NumColdContexts = 0;
37+
uint64_t NumHotContexts = 0;
38+
39+
public:
40+
MemProfSummaryBuilder() = default;
41+
~MemProfSummaryBuilder() = default;
42+
43+
void addRecord(const IndexedMemProfRecord &);
44+
void addRecord(const MemProfRecord &);
45+
std::unique_ptr<MemProfSummary> getSummary();
46+
};
47+
48+
} // namespace memprof
49+
} // namespace llvm
50+
51+
#endif // LLVM_PROFILEDATA_MEMPROFSUMMARYBUILDER_H

llvm/lib/Analysis/MemoryProfileInfo.cpp

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,6 @@ using namespace llvm::memprof;
2121

2222
#define DEBUG_TYPE "memory-profile-info"
2323

24-
// Upper bound on lifetime access density (accesses per byte per lifetime sec)
25-
// for marking an allocation cold.
26-
LLVM_ABI cl::opt<float> MemProfLifetimeAccessDensityColdThreshold(
27-
"memprof-lifetime-access-density-cold-threshold", cl::init(0.05),
28-
cl::Hidden,
29-
cl::desc("The threshold the lifetime access density (accesses per byte per "
30-
"lifetime sec) must be under to consider an allocation cold"));
31-
32-
// Lower bound on lifetime to mark an allocation cold (in addition to accesses
33-
// per byte per sec above). This is to avoid pessimizing short lived objects.
34-
LLVM_ABI cl::opt<unsigned> MemProfAveLifetimeColdThreshold(
35-
"memprof-ave-lifetime-cold-threshold", cl::init(200), cl::Hidden,
36-
cl::desc("The average lifetime (s) for an allocation to be considered "
37-
"cold"));
38-
39-
// Lower bound on average lifetime accesses density (total life time access
40-
// density / alloc count) for marking an allocation hot.
41-
LLVM_ABI cl::opt<unsigned> MemProfMinAveLifetimeAccessDensityHotThreshold(
42-
"memprof-min-ave-lifetime-access-density-hot-threshold", cl::init(1000),
43-
cl::Hidden,
44-
cl::desc("The minimum TotalLifetimeAccessDensity / AllocCount for an "
45-
"allocation to be considered hot"));
46-
47-
LLVM_ABI cl::opt<bool>
48-
MemProfUseHotHints("memprof-use-hot-hints", cl::init(false), cl::Hidden,
49-
cl::desc("Enable use of hot hints (only supported for "
50-
"unambigously hot allocations)"));
51-
5224
cl::opt<bool> MemProfReportHintedSizes(
5325
"memprof-report-hinted-sizes", cl::init(false), cl::Hidden,
5426
cl::desc("Report total allocation sizes of hinted allocations"));
@@ -73,28 +45,6 @@ cl::opt<unsigned> MinCallsiteColdBytePercent(
7345
cl::desc("Min percent of cold bytes at a callsite to discard non-cold "
7446
"contexts"));
7547

76-
AllocationType llvm::memprof::getAllocType(uint64_t TotalLifetimeAccessDensity,
77-
uint64_t AllocCount,
78-
uint64_t TotalLifetime) {
79-
// The access densities are multiplied by 100 to hold 2 decimal places of
80-
// precision, so need to divide by 100.
81-
if (((float)TotalLifetimeAccessDensity) / AllocCount / 100 <
82-
MemProfLifetimeAccessDensityColdThreshold
83-
// Lifetime is expected to be in ms, so convert the threshold to ms.
84-
&& ((float)TotalLifetime) / AllocCount >=
85-
MemProfAveLifetimeColdThreshold * 1000)
86-
return AllocationType::Cold;
87-
88-
// The access densities are multiplied by 100 to hold 2 decimal places of
89-
// precision, so need to divide by 100.
90-
if (MemProfUseHotHints &&
91-
((float)TotalLifetimeAccessDensity) / AllocCount / 100 >
92-
MemProfMinAveLifetimeAccessDensityHotThreshold)
93-
return AllocationType::Hot;
94-
95-
return AllocationType::NotCold;
96-
}
97-
9848
MDNode *llvm::memprof::buildCallstackMetadata(ArrayRef<uint64_t> CallStack,
9949
LLVMContext &Ctx) {
10050
SmallVector<Metadata *, 8> StackVals;

llvm/lib/ProfileData/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ add_llvm_component_library(LLVMProfileData
1010
MemProf.cpp
1111
MemProfReader.cpp
1212
MemProfRadixTree.cpp
13+
MemProfSummary.cpp
14+
MemProfSummaryBuilder.cpp
1315
PGOCtxProfReader.cpp
1416
PGOCtxProfWriter.cpp
1517
ProfileSummaryBuilder.cpp

llvm/lib/ProfileData/IndexedMemProfData.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/ProfileData/InstrProfReader.h"
1616
#include "llvm/ProfileData/MemProf.h"
1717
#include "llvm/ProfileData/MemProfRadixTree.h"
18+
#include "llvm/ProfileData/MemProfSummary.h"
1819
#include "llvm/Support/FormatVariadic.h"
1920
#include "llvm/Support/OnDiskHashTable.h"
2021

@@ -220,7 +221,8 @@ static Error writeMemProfRadixTreeBased(
220221
ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
221222
memprof::IndexedVersion Version, bool MemProfFullSchema,
222223
std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData =
223-
nullptr) {
224+
nullptr,
225+
memprof::MemProfSummary *MemProfSum = nullptr) {
224226
assert((Version == memprof::Version3 || Version == memprof::Version4) &&
225227
"Unsupported version for radix tree format");
226228

@@ -229,9 +231,12 @@ static Error writeMemProfRadixTreeBased(
229231
OS.write(0ULL); // Reserve space for the memprof call stack payload offset.
230232
OS.write(0ULL); // Reserve space for the memprof record payload offset.
231233
OS.write(0ULL); // Reserve space for the memprof record table offset.
232-
if (Version >= memprof::Version4)
234+
if (Version >= memprof::Version4) {
233235
OS.write(0ULL); // Reserve space for the data access profile offset.
234236

237+
MemProfSum->write(OS);
238+
}
239+
235240
auto Schema = memprof::getHotColdSchema();
236241
if (MemProfFullSchema)
237242
Schema = memprof::getFullSchema();
@@ -297,25 +302,27 @@ static Error writeMemProfV3(ProfOStream &OS,
297302
static Error writeMemProfV4(
298303
ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
299304
bool MemProfFullSchema,
300-
std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData) {
301-
return writeMemProfRadixTreeBased(OS, MemProfData, memprof::Version4,
302-
MemProfFullSchema,
303-
std::move(DataAccessProfileData));
305+
std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,
306+
memprof::MemProfSummary *MemProfSum) {
307+
return writeMemProfRadixTreeBased(
308+
OS, MemProfData, memprof::Version4, MemProfFullSchema,
309+
std::move(DataAccessProfileData), MemProfSum);
304310
}
305311

306312
// Write out the MemProf data in a requested version.
307313
Error writeMemProf(
308314
ProfOStream &OS, memprof::IndexedMemProfData &MemProfData,
309315
memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema,
310-
std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData) {
316+
std::unique_ptr<memprof::DataAccessProfData> DataAccessProfileData,
317+
memprof::MemProfSummary *MemProfSum) {
311318
switch (MemProfVersionRequested) {
312319
case memprof::Version2:
313320
return writeMemProfV2(OS, MemProfData, MemProfFullSchema);
314321
case memprof::Version3:
315322
return writeMemProfV3(OS, MemProfData, MemProfFullSchema);
316323
case memprof::Version4:
317324
return writeMemProfV4(OS, MemProfData, MemProfFullSchema,
318-
std::move(DataAccessProfileData));
325+
std::move(DataAccessProfileData), MemProfSum);
319326
}
320327

321328
return make_error<InstrProfError>(
@@ -395,9 +402,11 @@ Error IndexedMemProfReader::deserializeRadixTreeBased(
395402
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
396403

397404
uint64_t DataAccessProfOffset = 0;
398-
if (Version == memprof::Version4)
405+
if (Version >= memprof::Version4) {
399406
DataAccessProfOffset =
400407
support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
408+
MemProfSum = memprof::MemProfSummary::deserialize(Ptr);
409+
}
401410

402411
// Read the schema.
403412
auto SchemaOr = memprof::readMemProfSchema(Ptr);

0 commit comments

Comments
 (0)