From 2bd4d3bc5e1859b13af93156a5d6e67c516b88b8 Mon Sep 17 00:00:00 2001 From: Teresa Johnson Date: Mon, 28 Oct 2024 21:10:44 -0700 Subject: [PATCH 1/2] [MemProf] Support for random hotness when writing profile Add support for generating random hotness in the memprof profile writer, to be used for testing. The random seed is printed to stderr, and an additional option enables providing a specific seed in order to reproduce a particular random profile. --- llvm/include/llvm/ProfileData/MemProf.h | 9 ++++ llvm/lib/ProfileData/InstrProfWriter.cpp | 48 ++++++++++++++++++++-- llvm/test/Transforms/PGOProfile/memprof.ll | 19 +++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/ProfileData/MemProf.h b/llvm/include/llvm/ProfileData/MemProf.h index f8121d3573251..da2cc80737009 100644 --- a/llvm/include/llvm/ProfileData/MemProf.h +++ b/llvm/include/llvm/ProfileData/MemProf.h @@ -147,6 +147,15 @@ struct PortableMemInfoBlock { return Name; \ } #include "llvm/ProfileData/MIBEntryDef.inc" +#undef MIBEntryDef + + // Define setters for each type which can be called by the writer. +#define MIBEntryDef(NameTag, Name, Type) \ + void set##Name(Type NewVal) { \ + assert(Schema[llvm::to_underlying(Meta::Name)]); \ + Name = NewVal; \ + } +#include "llvm/ProfileData/MIBEntryDef.inc" #undef MIBEntryDef void clear() { *this = PortableMemInfoBlock(); } diff --git a/llvm/lib/ProfileData/InstrProfWriter.cpp b/llvm/lib/ProfileData/InstrProfWriter.cpp index 1a3721bf10350..f72e27a9a18ee 100644 --- a/llvm/lib/ProfileData/InstrProfWriter.cpp +++ b/llvm/lib/ProfileData/InstrProfWriter.cpp @@ -19,6 +19,7 @@ #include "llvm/ProfileData/InstrProf.h" #include "llvm/ProfileData/MemProf.h" #include "llvm/ProfileData/ProfileCommon.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" @@ -36,6 +37,14 @@ using namespace llvm; +static cl::opt + MemprofGenerateRandomHotness("memprof-random-hotness", cl::init(false), + cl::Hidden, + cl::desc("Generate random hotness values")); +static cl::opt MemprofGenerateRandomHotnessSeed( + "memprof-random-hotness-seed", cl::init(0), cl::Hidden, + cl::desc("Random hotness seed to use (0 to generate new seed)")); + // A struct to define how the data stream should be patched. For Indexed // profiling, only uint64_t data type is needed. struct PatchItem { @@ -190,7 +199,16 @@ InstrProfWriter::InstrProfWriter( InfoObj(new InstrProfRecordWriterTrait()), WritePrevVersion(WritePrevVersion), MemProfVersionRequested(MemProfVersionRequested), - MemProfFullSchema(MemProfFullSchema) {} + MemProfFullSchema(MemProfFullSchema) { + // Set up the random number seed if requested. + if (MemprofGenerateRandomHotness) { + unsigned seed = MemprofGenerateRandomHotnessSeed + ? MemprofGenerateRandomHotnessSeed + : std::time(nullptr); + errs() << "random hotness seed = " << seed << "\n"; + std::srand(seed); + } +} InstrProfWriter::~InstrProfWriter() { delete InfoObj; } @@ -273,13 +291,37 @@ void InstrProfWriter::addRecord(StringRef Name, uint64_t Hash, void InstrProfWriter::addMemProfRecord( const Function::GUID Id, const memprof::IndexedMemProfRecord &Record) { - auto [Iter, Inserted] = MemProfData.Records.insert({Id, Record}); + auto NewRecord = Record; + // Provoke random hotness values if requested. We specify the lifetime access + // density and lifetime length that will result in a cold or not cold hotness. + // See the logic in getAllocType() in Analysis/MemoryProfileInfo.cpp. + if (MemprofGenerateRandomHotness) { + for (auto &Alloc : NewRecord.AllocSites) { + uint64_t NewTLAD = 0; + uint64_t NewTL = 0; + bool IsCold = std::rand() % 2; + if (IsCold) { + // To get a cold context, set the lifetime access density to 0 and the + // lifetime to the maximum value. + NewTLAD = 0; + NewTL = std::numeric_limits::max(); + } else { + // To get a not cold context, set the lifetime access density to the + // maximum value and the lifetime to 0. + NewTLAD = std::numeric_limits::max(); + NewTL = 0; + } + Alloc.Info.setTotalLifetimeAccessDensity(NewTLAD); + Alloc.Info.setTotalLifetime(NewTL); + } + } + auto [Iter, Inserted] = MemProfData.Records.insert({Id, NewRecord}); // If we inserted a new record then we are done. if (Inserted) { return; } memprof::IndexedMemProfRecord &Existing = Iter->second; - Existing.merge(Record); + Existing.merge(NewRecord); } bool InstrProfWriter::addMemProfFrame(const memprof::FrameId Id, diff --git a/llvm/test/Transforms/PGOProfile/memprof.ll b/llvm/test/Transforms/PGOProfile/memprof.ll index e1457ca7251ed..205eeb8878989 100644 --- a/llvm/test/Transforms/PGOProfile/memprof.ll +++ b/llvm/test/Transforms/PGOProfile/memprof.ll @@ -66,6 +66,18 @@ ;; Check that the total sizes are reported if requested. ; RUN: opt < %s -passes='memprof-use' -pgo-warn-missing-function -S -memprof-report-hinted-sizes 2>&1 | FileCheck %s --check-prefixes=TOTALSIZES +;; Make sure we emit a random hotness seed if requested. +; RUN: llvm-profdata merge -memprof-random-hotness %S/Inputs/memprof.memprofraw --profiled-binary %S/Inputs/memprof.exe -o %t.memprofdatarand 2>&1 | FileCheck %s --check-prefix=RAND +; RAND: random hotness seed = +;; Can't check the exact values, but make sure applying the random profile +;; succeeds with the same stats +; RUN: opt < %s -passes='memprof-use' -pgo-warn-missing-function -S -stats 2>&1 | FileCheck %s --check-prefixes=ALL,MEMPROFONLY,MEMPROFSTATS + +;; Make sure we use a specific random hotness seed if requested. +; RUN: llvm-profdata merge -memprof-random-hotness -memprof-random-hotness-seed=1730170724 %S/Inputs/memprof.memprofraw --profiled-binary %S/Inputs/memprof.exe -o %t.memprofdatarand2 2>&1 | FileCheck %s --check-prefix=RAND2 +; RAND2: random hotness seed = 1730170724 +; RUN: opt < %s -passes='memprof-use' -pgo-warn-missing-function -S -stats 2>&1 | FileCheck %s --check-prefixes=MEMPROFRAND2,ALL,MEMPROFONLY,MEMPROFSTATS + ; MEMPROFMATCHINFO: MemProf notcold context with id 1093248920606587996 has total profiled size 10 is matched ; MEMPROFMATCHINFO: MemProf notcold context with id 5725971306423925017 has total profiled size 10 is matched ; MEMPROFMATCHINFO: MemProf notcold context with id 6792096022461663180 has total profiled size 10 is matched @@ -372,6 +384,13 @@ for.end: ; preds = %for.cond ; MEMPROFNOCOLINFO: ![[C10]] = !{i64 -4535090212904553409} ; MEMPROFNOCOLINFO: ![[C11]] = !{i64 3577763375057267810} +;; For the specific random seed, this is the expected order of hotness +; MEMPROFRAND2: !"cold" +; MEMPROFRAND2: !"cold" +; MEMPROFRAND2: !"cold" +; MEMPROFRAND2: !"hot" +; MEMPROFRAND2: !"hot" + ; MEMPROFSTATS: 8 memprof - Number of alloc contexts in memory profile. ; MEMPROFSTATS: 10 memprof - Number of callsites in memory profile. ; MEMPROFSTATS: 6 memprof - Number of functions having valid memory profile. From 52c9e0222234cb0301618ef5bb965ad631aba9fa Mon Sep 17 00:00:00 2001 From: Teresa Johnson Date: Tue, 29 Oct 2024 21:43:46 -0700 Subject: [PATCH 2/2] Address comments --- .../llvm/ProfileData/InstrProfWriter.h | 10 +++++++- llvm/lib/ProfileData/InstrProfWriter.cpp | 24 +++++++------------ llvm/tools/llvm-profdata/llvm-profdata.cpp | 12 +++++++++- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/llvm/include/llvm/ProfileData/InstrProfWriter.h b/llvm/include/llvm/ProfileData/InstrProfWriter.h index b8b6c684717b0..559549b0a22cc 100644 --- a/llvm/include/llvm/ProfileData/InstrProfWriter.h +++ b/llvm/include/llvm/ProfileData/InstrProfWriter.h @@ -78,12 +78,20 @@ class InstrProfWriter { // Whether to serialize the full schema. bool MemProfFullSchema; + // Whether to generated random memprof hotness for testing. + bool MemprofGenerateRandomHotness; + public: + // For memprof testing, random hotness can be assigned to the contexts if + // MemprofGenerateRandomHotness is enabled. The random seed can be either + // provided by MemprofGenerateRandomHotnessSeed, or if that is 0, one will be + // generated in the writer using the current time. InstrProfWriter( bool Sparse = false, uint64_t TemporalProfTraceReservoirSize = 0, uint64_t MaxTemporalProfTraceLength = 0, bool WritePrevVersion = false, memprof::IndexedVersion MemProfVersionRequested = memprof::Version0, - bool MemProfFullSchema = false); + bool MemProfFullSchema = false, bool MemprofGenerateRandomHotness = false, + unsigned MemprofGenerateRandomHotnessSeed = 0); ~InstrProfWriter(); StringMap &getProfileData() { return FunctionData; } diff --git a/llvm/lib/ProfileData/InstrProfWriter.cpp b/llvm/lib/ProfileData/InstrProfWriter.cpp index f72e27a9a18ee..f09241681b92a 100644 --- a/llvm/lib/ProfileData/InstrProfWriter.cpp +++ b/llvm/lib/ProfileData/InstrProfWriter.cpp @@ -37,14 +37,6 @@ using namespace llvm; -static cl::opt - MemprofGenerateRandomHotness("memprof-random-hotness", cl::init(false), - cl::Hidden, - cl::desc("Generate random hotness values")); -static cl::opt MemprofGenerateRandomHotnessSeed( - "memprof-random-hotness-seed", cl::init(0), cl::Hidden, - cl::desc("Random hotness seed to use (0 to generate new seed)")); - // A struct to define how the data stream should be patched. For Indexed // profiling, only uint64_t data type is needed. struct PatchItem { @@ -193,13 +185,16 @@ class InstrProfRecordWriterTrait { InstrProfWriter::InstrProfWriter( bool Sparse, uint64_t TemporalProfTraceReservoirSize, uint64_t MaxTemporalProfTraceLength, bool WritePrevVersion, - memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema) + memprof::IndexedVersion MemProfVersionRequested, bool MemProfFullSchema, + bool MemprofGenerateRandomHotness, + unsigned MemprofGenerateRandomHotnessSeed) : Sparse(Sparse), MaxTemporalProfTraceLength(MaxTemporalProfTraceLength), TemporalProfTraceReservoirSize(TemporalProfTraceReservoirSize), InfoObj(new InstrProfRecordWriterTrait()), WritePrevVersion(WritePrevVersion), MemProfVersionRequested(MemProfVersionRequested), - MemProfFullSchema(MemProfFullSchema) { + MemProfFullSchema(MemProfFullSchema), + MemprofGenerateRandomHotness(MemprofGenerateRandomHotness) { // Set up the random number seed if requested. if (MemprofGenerateRandomHotness) { unsigned seed = MemprofGenerateRandomHotnessSeed @@ -297,7 +292,9 @@ void InstrProfWriter::addMemProfRecord( // See the logic in getAllocType() in Analysis/MemoryProfileInfo.cpp. if (MemprofGenerateRandomHotness) { for (auto &Alloc : NewRecord.AllocSites) { - uint64_t NewTLAD = 0; + // To get a not cold context, set the lifetime access density to the + // maximum value and the lifetime to 0. + uint64_t NewTLAD = std::numeric_limits::max(); uint64_t NewTL = 0; bool IsCold = std::rand() % 2; if (IsCold) { @@ -305,11 +302,6 @@ void InstrProfWriter::addMemProfRecord( // lifetime to the maximum value. NewTLAD = 0; NewTL = std::numeric_limits::max(); - } else { - // To get a not cold context, set the lifetime access density to the - // maximum value and the lifetime to 0. - NewTLAD = std::numeric_limits::max(); - NewTL = 0; } Alloc.Info.setTotalLifetimeAccessDensity(NewTLAD); Alloc.Info.setTotalLifetime(NewTL); diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index 59f0f1f1fae89..f7023aa966adf 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -342,6 +342,15 @@ cl::opt MemProfFullSchema( "memprof-full-schema", cl::Hidden, cl::sub(MergeSubcommand), cl::desc("Use the full schema for serialization"), cl::init(false)); +static cl::opt + MemprofGenerateRandomHotness("memprof-random-hotness", cl::init(false), + cl::Hidden, cl::sub(MergeSubcommand), + cl::desc("Generate random hotness values")); +static cl::opt MemprofGenerateRandomHotnessSeed( + "memprof-random-hotness-seed", cl::init(0), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("Random hotness seed to use (0 to generate new seed)")); + // Options specific to overlap subcommand. cl::opt BaseFilename(cl::Positional, cl::Required, cl::desc(""), @@ -641,7 +650,8 @@ struct WriterContext { SmallSet &WriterErrorCodes, uint64_t ReservoirSize = 0, uint64_t MaxTraceLength = 0) : Writer(IsSparse, ReservoirSize, MaxTraceLength, DoWritePrevVersion, - MemProfVersionRequested, MemProfFullSchema), + MemProfVersionRequested, MemProfFullSchema, + MemprofGenerateRandomHotness, MemprofGenerateRandomHotnessSeed), ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {} };