Skip to content

Commit 23144e8

Browse files
authored
[SampleFDO][NFC] Refactoring sample reader to support on-demand read profiles for given functions (#104654)
Currently in extended binary format, sample reader only read the profiles when the function are in the current module at initialization time, this extends the support to read the arbitrary profiles for given input functions in later stage. It's used for #101053.
1 parent 511500e commit 23144e8

File tree

2 files changed

+183
-86
lines changed

2 files changed

+183
-86
lines changed

llvm/include/llvm/ProfileData/SampleProfReader.h

+43
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,17 @@ class SampleProfileReader {
380380
return sampleprof_error::success;
381381
}
382382

383+
/// Read sample profiles for the given functions.
384+
std::error_code read(const DenseSet<StringRef> &FuncsToUse) {
385+
DenseSet<StringRef> S;
386+
for (StringRef F : FuncsToUse)
387+
if (Profiles.find(FunctionId(F)) == Profiles.end())
388+
S.insert(F);
389+
if (std::error_code EC = read(S, Profiles))
390+
return EC;
391+
return sampleprof_error::success;
392+
}
393+
383394
/// The implementaion to read sample profiles from the associated file.
384395
virtual std::error_code readImpl() = 0;
385396

@@ -520,8 +531,26 @@ class SampleProfileReader {
520531
/// Compute summary for this profile.
521532
void computeSummary();
522533

534+
/// Read sample profiles for the given functions and write them to the given
535+
/// profile map. Currently it's only used for extended binary format to load
536+
/// the profiles on-demand.
537+
virtual std::error_code read(const DenseSet<StringRef> &FuncsToUse,
538+
SampleProfileMap &Profiles) {
539+
return sampleprof_error::not_implemented;
540+
}
541+
523542
std::unique_ptr<SampleProfileReaderItaniumRemapper> Remapper;
524543

544+
// A map from a function's context hash to its meta data section range, used
545+
// for on-demand read function profile metadata.
546+
std::unordered_map<uint64_t, std::pair<const uint8_t *, const uint8_t *>>
547+
FuncMetadataIndex;
548+
549+
std::pair<const uint8_t *, const uint8_t *> ProfileSecRange;
550+
551+
/// Whether the profile has attribute metadata.
552+
bool ProfileHasAttribute = false;
553+
525554
/// \brief Whether samples are collected based on pseudo probes.
526555
bool ProfileIsProbeBased = false;
527556

@@ -621,6 +650,8 @@ class SampleProfileReaderBinary : public SampleProfileReader {
621650

622651
/// Read the next function profile instance.
623652
std::error_code readFuncProfile(const uint8_t *Start);
653+
std::error_code readFuncProfile(const uint8_t *Start,
654+
SampleProfileMap &Profiles);
624655

625656
/// Read the contents of the given profile instance.
626657
std::error_code readProfile(FunctionSamples &FProfile);
@@ -720,11 +751,15 @@ class SampleProfileReaderExtBinaryBase : public SampleProfileReaderBinary {
720751
std::error_code readSecHdrTableEntry(uint64_t Idx);
721752
std::error_code readSecHdrTable();
722753

754+
std::error_code readFuncMetadata(bool ProfileHasAttribute,
755+
SampleProfileMap &Profiles);
723756
std::error_code readFuncMetadata(bool ProfileHasAttribute);
724757
std::error_code readFuncMetadata(bool ProfileHasAttribute,
725758
FunctionSamples *FProfile);
726759
std::error_code readFuncOffsetTable();
727760
std::error_code readFuncProfiles();
761+
std::error_code readFuncProfiles(const DenseSet<StringRef> &FuncsToUse,
762+
SampleProfileMap &Profiles);
728763
std::error_code readNameTableSec(bool IsMD5, bool FixedLengthMD5);
729764
std::error_code readCSNameTableSec();
730765
std::error_code readProfileSymbolList();
@@ -781,6 +816,14 @@ class SampleProfileReaderExtBinaryBase : public SampleProfileReaderBinary {
781816
};
782817

783818
void setSkipFlatProf(bool Skip) override { SkipFlatProf = Skip; }
819+
820+
private:
821+
/// Read the profiles on-demand for the given functions. This is used after
822+
/// stale call graph matching finds new functions whose profiles aren't loaded
823+
/// at the beginning and we need to loaded the profiles explicitly for
824+
/// potential matching.
825+
std::error_code read(const DenseSet<StringRef> &FuncsToUse,
826+
SampleProfileMap &Profiles) override;
784827
};
785828

786829
class SampleProfileReaderExtBinary : public SampleProfileReaderExtBinaryBase {

llvm/lib/ProfileData/SampleProfReader.cpp

+140-86
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,8 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) {
653653
}
654654

655655
std::error_code
656-
SampleProfileReaderBinary::readFuncProfile(const uint8_t *Start) {
656+
SampleProfileReaderBinary::readFuncProfile(const uint8_t *Start,
657+
SampleProfileMap &Profiles) {
657658
Data = Start;
658659
auto NumHeadSamples = readNumber<uint64_t>();
659660
if (std::error_code EC = NumHeadSamples.getError())
@@ -678,6 +679,11 @@ SampleProfileReaderBinary::readFuncProfile(const uint8_t *Start) {
678679
return sampleprof_error::success;
679680
}
680681

682+
std::error_code
683+
SampleProfileReaderBinary::readFuncProfile(const uint8_t *Start) {
684+
return readFuncProfile(Start, Profiles);
685+
}
686+
681687
std::error_code SampleProfileReaderBinary::readImpl() {
682688
ProfileIsFS = ProfileIsFSDisciminator;
683689
FunctionSamples::ProfileIsFS = ProfileIsFS;
@@ -725,6 +731,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readOneSection(
725731
break;
726732
}
727733
case SecLBRProfile:
734+
ProfileSecRange = std::make_pair(Data, End);
728735
if (std::error_code EC = readFuncProfiles())
729736
return EC;
730737
break;
@@ -745,9 +752,9 @@ std::error_code SampleProfileReaderExtBinaryBase::readOneSection(
745752
ProfileIsProbeBased =
746753
hasSecFlag(Entry, SecFuncMetadataFlags::SecFlagIsProbeBased);
747754
FunctionSamples::ProfileIsProbeBased = ProfileIsProbeBased;
748-
bool HasAttribute =
755+
ProfileHasAttribute =
749756
hasSecFlag(Entry, SecFuncMetadataFlags::SecFlagHasAttribute);
750-
if (std::error_code EC = readFuncMetadata(HasAttribute))
757+
if (std::error_code EC = readFuncMetadata(ProfileHasAttribute))
751758
return EC;
752759
break;
753760
}
@@ -791,6 +798,19 @@ bool SampleProfileReaderExtBinaryBase::useFuncOffsetList() const {
791798
return false;
792799
}
793800

801+
std::error_code
802+
SampleProfileReaderExtBinaryBase::read(const DenseSet<StringRef> &FuncsToUse,
803+
SampleProfileMap &Profiles) {
804+
Data = ProfileSecRange.first;
805+
End = ProfileSecRange.second;
806+
if (std::error_code EC = readFuncProfiles(FuncsToUse, Profiles))
807+
return EC;
808+
End = Data;
809+
810+
if (std::error_code EC = readFuncMetadata(ProfileHasAttribute, Profiles))
811+
return EC;
812+
return sampleprof_error::success;
813+
}
794814

795815
bool SampleProfileReaderExtBinaryBase::collectFuncsFromModule() {
796816
if (!M)
@@ -838,6 +858,97 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() {
838858
return sampleprof_error::success;
839859
}
840860

861+
std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles(
862+
const DenseSet<StringRef> &FuncsToUse, SampleProfileMap &Profiles) {
863+
const uint8_t *Start = Data;
864+
865+
if (Remapper) {
866+
for (auto Name : FuncsToUse) {
867+
Remapper->insert(Name);
868+
}
869+
}
870+
871+
if (ProfileIsCS) {
872+
assert(useFuncOffsetList());
873+
DenseSet<uint64_t> FuncGuidsToUse;
874+
if (useMD5()) {
875+
for (auto Name : FuncsToUse)
876+
FuncGuidsToUse.insert(Function::getGUID(Name));
877+
}
878+
879+
// For each function in current module, load all context profiles for
880+
// the function as well as their callee contexts which can help profile
881+
// guided importing for ThinLTO. This can be achieved by walking
882+
// through an ordered context container, where contexts are laid out
883+
// as if they were walked in preorder of a context trie. While
884+
// traversing the trie, a link to the highest common ancestor node is
885+
// kept so that all of its decendants will be loaded.
886+
const SampleContext *CommonContext = nullptr;
887+
for (const auto &NameOffset : FuncOffsetList) {
888+
const auto &FContext = NameOffset.first;
889+
FunctionId FName = FContext.getFunction();
890+
StringRef FNameString;
891+
if (!useMD5())
892+
FNameString = FName.stringRef();
893+
894+
// For function in the current module, keep its farthest ancestor
895+
// context. This can be used to load itself and its child and
896+
// sibling contexts.
897+
if ((useMD5() && FuncGuidsToUse.count(FName.getHashCode())) ||
898+
(!useMD5() && (FuncsToUse.count(FNameString) ||
899+
(Remapper && Remapper->exist(FNameString))))) {
900+
if (!CommonContext || !CommonContext->isPrefixOf(FContext))
901+
CommonContext = &FContext;
902+
}
903+
904+
if (CommonContext == &FContext ||
905+
(CommonContext && CommonContext->isPrefixOf(FContext))) {
906+
// Load profile for the current context which originated from
907+
// the common ancestor.
908+
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
909+
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
910+
return EC;
911+
}
912+
}
913+
} else if (useMD5()) {
914+
assert(!useFuncOffsetList());
915+
for (auto Name : FuncsToUse) {
916+
auto GUID = MD5Hash(Name);
917+
auto iter = FuncOffsetTable.find(GUID);
918+
if (iter == FuncOffsetTable.end())
919+
continue;
920+
const uint8_t *FuncProfileAddr = Start + iter->second;
921+
if (std::error_code EC = readFuncProfile(FuncProfileAddr, Profiles))
922+
return EC;
923+
}
924+
} else if (Remapper) {
925+
assert(useFuncOffsetList());
926+
for (auto NameOffset : FuncOffsetList) {
927+
SampleContext FContext(NameOffset.first);
928+
auto FuncName = FContext.getFunction();
929+
StringRef FuncNameStr = FuncName.stringRef();
930+
if (!FuncsToUse.count(FuncNameStr) && !Remapper->exist(FuncNameStr))
931+
continue;
932+
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
933+
if (std::error_code EC = readFuncProfile(FuncProfileAddr, Profiles))
934+
return EC;
935+
}
936+
} else {
937+
assert(!useFuncOffsetList());
938+
for (auto Name : FuncsToUse) {
939+
940+
auto iter = FuncOffsetTable.find(MD5Hash(Name));
941+
if (iter == FuncOffsetTable.end())
942+
continue;
943+
const uint8_t *FuncProfileAddr = Start + iter->second;
944+
if (std::error_code EC = readFuncProfile(FuncProfileAddr, Profiles))
945+
return EC;
946+
}
947+
}
948+
949+
return sampleprof_error::success;
950+
}
951+
841952
std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
842953
// Collect functions used by current module if the Reader has been
843954
// given a module.
@@ -849,7 +960,6 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
849960

850961
// When LoadFuncsToBeUsed is false, we are using LLVM tool, need to read all
851962
// profiles.
852-
const uint8_t *Start = Data;
853963
if (!LoadFuncsToBeUsed) {
854964
while (Data < End) {
855965
if (std::error_code EC = readFuncProfile(Data))
@@ -858,88 +968,8 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
858968
assert(Data == End && "More data is read than expected");
859969
} else {
860970
// Load function profiles on demand.
861-
if (Remapper) {
862-
for (auto Name : FuncsToUse) {
863-
Remapper->insert(Name);
864-
}
865-
}
866-
867-
if (ProfileIsCS) {
868-
assert(useFuncOffsetList());
869-
DenseSet<uint64_t> FuncGuidsToUse;
870-
if (useMD5()) {
871-
for (auto Name : FuncsToUse)
872-
FuncGuidsToUse.insert(Function::getGUID(Name));
873-
}
874-
875-
// For each function in current module, load all context profiles for
876-
// the function as well as their callee contexts which can help profile
877-
// guided importing for ThinLTO. This can be achieved by walking
878-
// through an ordered context container, where contexts are laid out
879-
// as if they were walked in preorder of a context trie. While
880-
// traversing the trie, a link to the highest common ancestor node is
881-
// kept so that all of its decendants will be loaded.
882-
const SampleContext *CommonContext = nullptr;
883-
for (const auto &NameOffset : FuncOffsetList) {
884-
const auto &FContext = NameOffset.first;
885-
FunctionId FName = FContext.getFunction();
886-
StringRef FNameString;
887-
if (!useMD5())
888-
FNameString = FName.stringRef();
889-
890-
// For function in the current module, keep its farthest ancestor
891-
// context. This can be used to load itself and its child and
892-
// sibling contexts.
893-
if ((useMD5() && FuncGuidsToUse.count(FName.getHashCode())) ||
894-
(!useMD5() && (FuncsToUse.count(FNameString) ||
895-
(Remapper && Remapper->exist(FNameString))))) {
896-
if (!CommonContext || !CommonContext->isPrefixOf(FContext))
897-
CommonContext = &FContext;
898-
}
899-
900-
if (CommonContext == &FContext ||
901-
(CommonContext && CommonContext->isPrefixOf(FContext))) {
902-
// Load profile for the current context which originated from
903-
// the common ancestor.
904-
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
905-
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
906-
return EC;
907-
}
908-
}
909-
} else if (useMD5()) {
910-
assert(!useFuncOffsetList());
911-
for (auto Name : FuncsToUse) {
912-
auto GUID = MD5Hash(Name);
913-
auto iter = FuncOffsetTable.find(GUID);
914-
if (iter == FuncOffsetTable.end())
915-
continue;
916-
const uint8_t *FuncProfileAddr = Start + iter->second;
917-
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
918-
return EC;
919-
}
920-
} else if (Remapper) {
921-
assert(useFuncOffsetList());
922-
for (auto NameOffset : FuncOffsetList) {
923-
SampleContext FContext(NameOffset.first);
924-
auto FuncName = FContext.getFunction();
925-
StringRef FuncNameStr = FuncName.stringRef();
926-
if (!FuncsToUse.count(FuncNameStr) && !Remapper->exist(FuncNameStr))
927-
continue;
928-
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
929-
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
930-
return EC;
931-
}
932-
} else {
933-
assert(!useFuncOffsetList());
934-
for (auto Name : FuncsToUse) {
935-
auto iter = FuncOffsetTable.find(MD5Hash(Name));
936-
if (iter == FuncOffsetTable.end())
937-
continue;
938-
const uint8_t *FuncProfileAddr = Start + iter->second;
939-
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
940-
return EC;
941-
}
942-
}
971+
if (std::error_code EC = readFuncProfiles(FuncsToUse, Profiles))
972+
return EC;
943973
Data = End;
944974
}
945975
assert((CSProfileCount == 0 || CSProfileCount == Profiles.size()) &&
@@ -1245,6 +1275,27 @@ SampleProfileReaderExtBinaryBase::readFuncMetadata(bool ProfileHasAttribute,
12451275
return sampleprof_error::success;
12461276
}
12471277

1278+
std::error_code
1279+
SampleProfileReaderExtBinaryBase::readFuncMetadata(bool ProfileHasAttribute,
1280+
SampleProfileMap &Profiles) {
1281+
if (FuncMetadataIndex.empty())
1282+
return sampleprof_error::success;
1283+
1284+
for (auto &I : Profiles) {
1285+
FunctionSamples *FProfile = &I.second;
1286+
auto R = FuncMetadataIndex.find(FProfile->getContext().getHashCode());
1287+
if (R == FuncMetadataIndex.end())
1288+
continue;
1289+
1290+
Data = R->second.first;
1291+
End = R->second.second;
1292+
if (std::error_code EC = readFuncMetadata(ProfileHasAttribute, FProfile))
1293+
return EC;
1294+
assert(Data == End && "More data is read than expected");
1295+
}
1296+
return sampleprof_error::success;
1297+
}
1298+
12481299
std::error_code
12491300
SampleProfileReaderExtBinaryBase::readFuncMetadata(bool ProfileHasAttribute) {
12501301
while (Data < End) {
@@ -1257,8 +1308,11 @@ SampleProfileReaderExtBinaryBase::readFuncMetadata(bool ProfileHasAttribute) {
12571308
if (It != Profiles.end())
12581309
FProfile = &It->second;
12591310

1311+
const uint8_t *Start = Data;
12601312
if (std::error_code EC = readFuncMetadata(ProfileHasAttribute, FProfile))
12611313
return EC;
1314+
1315+
FuncMetadataIndex[FContext.getHashCode()] = {Start, Data};
12621316
}
12631317

12641318
assert(Data == End && "More data is read than expected");

0 commit comments

Comments
 (0)