Skip to content

Commit 4fe91e0

Browse files
committed
[llvm-profdata] ProfileReader cleanup - preparation for MD5 refactoring - 3
Cleanup profile reader classes to prepare for complex refactoring as propsed in D147740, continuing D148872 This is patch 3/n. This patch changes the behavior of function offset table. Previously when reading ExtBinary profile, the funcOffsetTable (map) is always populated, and in addition if the profile is CS, the orderedFuncOffsets (list) is also populated. However when reading the function samples, only one of the container is being used, never both, so it's a huge waste of time to populate both. Added logic to select which one to use, and completely skip reading function offset table if we are in tool mode (all function samples are to be read sequentially regardless) Reviewed By: davidxl, wenlei Differential Revision: https://reviews.llvm.org/D149124
1 parent be5747e commit 4fe91e0

File tree

2 files changed

+97
-55
lines changed

2 files changed

+97
-55
lines changed

llvm/include/llvm/ProfileData/SampleProfReader.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -639,9 +639,6 @@ class SampleProfileReaderBinary : public SampleProfileReader {
639639
/// Read the string index and check whether it overflows the table.
640640
template <typename T> inline ErrorOr<size_t> readStringIndex(T &Table);
641641

642-
/// Return true if we've reached the end of file.
643-
bool at_eof() const { return Data >= End; }
644-
645642
/// Read the next function profile instance.
646643
std::error_code readFuncProfile(const uint8_t *Start);
647644

@@ -759,15 +756,20 @@ class SampleProfileReaderExtBinaryBase : public SampleProfileReaderBinary {
759756
// placeholder for subclasses to dispatch their own section readers.
760757
virtual std::error_code readCustomSection(const SecHdrTableEntry &Entry) = 0;
761758

759+
/// Determine which container readFuncOffsetTable() should populate, the list
760+
/// FuncOffsetList or the map FuncOffsetTable.
761+
bool useFuncOffsetList() const;
762+
762763
std::unique_ptr<ProfileSymbolList> ProfSymList;
763764

764765
/// The table mapping from function context to the offset of its
765766
/// FunctionSample towards file start.
767+
/// At most one of FuncOffsetTable and FuncOffsetList is populated.
766768
DenseMap<SampleContext, uint64_t> FuncOffsetTable;
767769

768-
/// Function offset mapping ordered by contexts.
769-
std::unique_ptr<std::vector<std::pair<SampleContext, uint64_t>>>
770-
OrderedFuncOffsets;
770+
/// The list version of FuncOffsetTable. This is used if every entry is
771+
/// being accessed.
772+
std::vector<std::pair<SampleContext, uint64_t>> FuncOffsetList;
771773

772774
/// The set containing the functions to use when compiling a module.
773775
DenseSet<StringRef> FuncsToUse;
@@ -776,8 +778,6 @@ class SampleProfileReaderExtBinaryBase : public SampleProfileReaderBinary {
776778
/// SecFlagFlat flag.
777779
bool SkipFlatProf = false;
778780

779-
bool FuncOffsetsOrdered = false;
780-
781781
public:
782782
SampleProfileReaderExtBinaryBase(std::unique_ptr<MemoryBuffer> B,
783783
LLVMContext &C, SampleProfileFormat Format)

llvm/lib/ProfileData/SampleProfReader.cpp

Lines changed: 89 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ SampleProfileReaderBinary::readFuncProfile(const uint8_t *Start) {
679679
std::error_code SampleProfileReaderBinary::readImpl() {
680680
ProfileIsFS = ProfileIsFSDisciminator;
681681
FunctionSamples::ProfileIsFS = ProfileIsFS;
682-
while (!at_eof()) {
682+
while (Data < End) {
683683
if (std::error_code EC = readFuncProfile(Data))
684684
return EC;
685685
}
@@ -727,9 +727,17 @@ std::error_code SampleProfileReaderExtBinaryBase::readOneSection(
727727
return EC;
728728
break;
729729
case SecFuncOffsetTable:
730-
FuncOffsetsOrdered = hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagOrdered);
731-
if (std::error_code EC = readFuncOffsetTable())
732-
return EC;
730+
// If module is absent, we are using LLVM tools, and need to read all
731+
// profiles, so skip reading the function offset table.
732+
if (!M) {
733+
Data = End;
734+
} else {
735+
assert((!ProfileIsCS ||
736+
hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagOrdered)) &&
737+
"func offset table should always be sorted in CS profile");
738+
if (std::error_code EC = readFuncOffsetTable())
739+
return EC;
740+
}
733741
break;
734742
case SecFuncMetadata: {
735743
ProfileIsProbeBased =
@@ -753,6 +761,35 @@ std::error_code SampleProfileReaderExtBinaryBase::readOneSection(
753761
return sampleprof_error::success;
754762
}
755763

764+
bool SampleProfileReaderExtBinaryBase::useFuncOffsetList() const {
765+
// If profile is CS, the function offset section is expected to consist of
766+
// sequences of contexts in pre-order layout
767+
// (e.g. [A, A:1 @ B, A:1 @ B:2.3 @ C] [D, D:1 @ E]), so that when a matched
768+
// context in the module is found, the profiles of all its callees are
769+
// recursively loaded. A list is needed since the order of profiles matters.
770+
if (ProfileIsCS)
771+
return true;
772+
773+
// If the profile is MD5, use the map container to lookup functions in
774+
// the module. A remapper has no use on MD5 names.
775+
if (useMD5())
776+
return false;
777+
778+
// Profile is not MD5 and if a remapper is present, the remapped name of
779+
// every function needed to be matched against the module, so use the list
780+
// container since each entry is accessed.
781+
if (Remapper)
782+
return true;
783+
784+
// Otherwise use the map container for faster lookup.
785+
// TODO: If the cardinality of the function offset section is much smaller
786+
// than the number of functions in the module, using the list container can
787+
// be always faster, but we need to figure out the constant factor to
788+
// determine the cutoff.
789+
return false;
790+
}
791+
792+
756793
bool SampleProfileReaderExtBinaryBase::collectFuncsFromModule() {
757794
if (!M)
758795
return false;
@@ -763,22 +800,20 @@ bool SampleProfileReaderExtBinaryBase::collectFuncsFromModule() {
763800
}
764801

765802
std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() {
766-
// If there are more than one FuncOffsetTable, the profile read associated
767-
// with previous FuncOffsetTable has to be done before next FuncOffsetTable
768-
// is read.
803+
// If there are more than one function offset section, the profile associated
804+
// with the previous section has to be done reading before next one is read.
769805
FuncOffsetTable.clear();
806+
FuncOffsetList.clear();
770807

771808
auto Size = readNumber<uint64_t>();
772809
if (std::error_code EC = Size.getError())
773810
return EC;
774811

775-
FuncOffsetTable.reserve(*Size);
776-
777-
if (FuncOffsetsOrdered) {
778-
OrderedFuncOffsets =
779-
std::make_unique<std::vector<std::pair<SampleContext, uint64_t>>>();
780-
OrderedFuncOffsets->reserve(*Size);
781-
}
812+
bool UseFuncOffsetList = useFuncOffsetList();
813+
if (UseFuncOffsetList)
814+
FuncOffsetList.reserve(*Size);
815+
else
816+
FuncOffsetTable.reserve(*Size);
782817

783818
for (uint64_t I = 0; I < *Size; ++I) {
784819
auto FContext(readSampleContextFromTable());
@@ -789,12 +824,13 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() {
789824
if (std::error_code EC = Offset.getError())
790825
return EC;
791826

792-
FuncOffsetTable[*FContext] = *Offset;
793-
if (FuncOffsetsOrdered)
794-
OrderedFuncOffsets->emplace_back(*FContext, *Offset);
795-
}
827+
if (UseFuncOffsetList)
828+
FuncOffsetList.emplace_back(*FContext, *Offset);
829+
else
830+
FuncOffsetTable[*FContext] = *Offset;
831+
}
796832

797-
return sampleprof_error::success;
833+
return sampleprof_error::success;
798834
}
799835

800836
std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
@@ -806,7 +842,8 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
806842
// NameTable section is read.
807843
bool LoadFuncsToBeUsed = collectFuncsFromModule();
808844

809-
// When LoadFuncsToBeUsed is false, load all the function profiles.
845+
// When LoadFuncsToBeUsed is false, we are using LLVM tool, need to read all
846+
// profiles.
810847
const uint8_t *Start = Data;
811848
if (!LoadFuncsToBeUsed) {
812849
while (Data < End) {
@@ -823,6 +860,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
823860
}
824861

825862
if (ProfileIsCS) {
863+
assert(useFuncOffsetList());
826864
DenseSet<uint64_t> FuncGuidsToUse;
827865
if (useMD5()) {
828866
for (auto Name : FuncsToUse)
@@ -836,10 +874,8 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
836874
// as if they were walked in preorder of a context trie. While
837875
// traversing the trie, a link to the highest common ancestor node is
838876
// kept so that all of its decendants will be loaded.
839-
assert(OrderedFuncOffsets.get() &&
840-
"func offset table should always be sorted in CS profile");
841877
const SampleContext *CommonContext = nullptr;
842-
for (const auto &NameOffset : *OrderedFuncOffsets) {
878+
for (const auto &NameOffset : FuncOffsetList) {
843879
const auto &FContext = NameOffset.first;
844880
auto FName = FContext.getName();
845881
// For function in the current module, keep its farthest ancestor
@@ -857,35 +893,41 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
857893
// Load profile for the current context which originated from
858894
// the common ancestor.
859895
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
860-
assert(FuncProfileAddr < End && "out of LBRProfile section");
861896
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
862897
return EC;
863898
}
864899
}
900+
} else if (useMD5()) {
901+
assert(!useFuncOffsetList());
902+
for (auto Name : FuncsToUse) {
903+
auto GUID = std::to_string(MD5Hash(Name));
904+
auto iter = FuncOffsetTable.find(StringRef(GUID));
905+
if (iter == FuncOffsetTable.end())
906+
continue;
907+
const uint8_t *FuncProfileAddr = Start + iter->second;
908+
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
909+
return EC;
910+
}
911+
} else if (Remapper) {
912+
assert(useFuncOffsetList());
913+
for (auto NameOffset : FuncOffsetList) {
914+
SampleContext FContext(NameOffset.first);
915+
auto FuncName = FContext.getName();
916+
if (!FuncsToUse.count(FuncName) && !Remapper->exist(FuncName))
917+
continue;
918+
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
919+
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
920+
return EC;
921+
}
865922
} else {
866-
if (useMD5()) {
867-
for (auto Name : FuncsToUse) {
868-
auto GUID = std::to_string(MD5Hash(Name));
869-
auto iter = FuncOffsetTable.find(StringRef(GUID));
870-
if (iter == FuncOffsetTable.end())
871-
continue;
872-
const uint8_t *FuncProfileAddr = Start + iter->second;
873-
assert(FuncProfileAddr < End && "out of LBRProfile section");
874-
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
875-
return EC;
876-
}
877-
} else {
878-
for (auto NameOffset : FuncOffsetTable) {
879-
SampleContext FContext(NameOffset.first);
880-
auto FuncName = FContext.getName();
881-
if (!FuncsToUse.count(FuncName) &&
882-
(!Remapper || !Remapper->exist(FuncName)))
883-
continue;
884-
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
885-
assert(FuncProfileAddr < End && "out of LBRProfile section");
886-
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
887-
return EC;
888-
}
923+
assert(!useFuncOffsetList());
924+
for (auto Name : FuncsToUse) {
925+
auto iter = FuncOffsetTable.find(Name);
926+
if (iter == FuncOffsetTable.end())
927+
continue;
928+
const uint8_t *FuncProfileAddr = Start + iter->second;
929+
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
930+
return EC;
889931
}
890932
}
891933
Data = End;

0 commit comments

Comments
 (0)