Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SampleFDO] Stale profile call-graph matching #95135

Merged
merged 25 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
622d78f
[SampleFDO] Stale profile renaming matching
wlei-llvm May 4, 2024
c7ada8b
addressing comments
wlei-llvm May 20, 2024
183f6ae
fix test & udpate non-inline call targets & addressing comments
wlei-llvm May 23, 2024
6beddf2
addressing comments
wlei-llvm May 29, 2024
7f38b7e
change ProfCallee to ProfFunc
wlei-llvm May 30, 2024
ae436dd
early break for non-inline callees match when NewIRCallees is empty
wlei-llvm May 30, 2024
7cb0cd7
refactoring findOrMatchFunction
wlei-llvm May 30, 2024
beaa601
addressing comments
wlei-llvm Jun 3, 2024
c61ee03
fix comment
wlei-llvm Jun 4, 2024
81811c3
run matching in top-down order and along with CFG matching
wlei-llvm Jun 9, 2024
4bdda81
add stats
wlei-llvm Jun 10, 2024
df77394
FunctionProfileNameMap to FuncToProfileNameMap and fix lint
wlei-llvm Jun 10, 2024
d001406
fix varibale name
wlei-llvm Jun 11, 2024
8fc8f54
fix typo and incorrect comments
wlei-llvm Jun 12, 2024
ecc4000
addressing comment: build its own top-down function for matcher
wlei-llvm Jun 14, 2024
da8b752
addressing comment
wlei-llvm Jun 17, 2024
93d70fa
addressing feedback
wlei-llvm Jun 21, 2024
8c36fcc
add test for recursive case
wlei-llvm Jun 22, 2024
dc4d4f9
renaming and fix comments
wlei-llvm Jun 24, 2024
c7ae1a9
Merge branch 'main' into call-graph-matching
wlei-llvm Jun 24, 2024
7b458b4
rename to FunctionsWithoutProfile
wlei-llvm Jun 24, 2024
15e8d0c
refactor functionHasProfile
wlei-llvm Jun 25, 2024
47c1816
fix use-after-free
wlei-llvm Jul 8, 2024
82cf2a6
Merge branch 'main' into call-graph-matching
wlei-llvm Jul 8, 2024
845ebe3
Merge branch 'llvm:main' into call-graph-matching
wlei-llvm Jul 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 84 additions & 14 deletions llvm/include/llvm/Transforms/IPO/SampleProfileMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,33 @@ class SampleProfileMatcher {
StringMap<std::unordered_map<LineLocation, MatchState, LineLocationHash>>
FuncCallsiteMatchStates;

struct FuncToProfileNameMapHash {
uint64_t
operator()(const std::pair<const Function *, FunctionId> &P) const {
return hash_combine(P.first, P.second);
}
};
// A map from a pair of function and profile name to a boolean value
// indicating whether they are matched. This is used as a cache for the
// matching result.
std::unordered_map<std::pair<const Function *, FunctionId>, bool,
FuncToProfileNameMapHash>
FuncToProfileNameMap;
// The new functions found by the call graph matching. The map's key is the
// old profile name and value is the new(renamed) function.
HashKeyMap<std::unordered_map, FunctionId, Function *> ProfileNameToFuncMap;

// A map pointer to the SymbolMap in the SampleProfileLoader, which stores all
// the original matched symbols before the matching. this is to determine if
// the profile is unused(to be matched) or not.
HashKeyMap<std::unordered_map, FunctionId, Function *> *SymbolMap;

// The new functions from IR.
HashKeyMap<std::unordered_map, FunctionId, Function *> NewIRFunctions;

// Pointer to the Profile Symbol List in the reader.
std::shared_ptr<ProfileSymbolList> PSL;

// Profile mismatch statstics:
uint64_t TotalProfiledFunc = 0;
// Num of checksum-mismatched function.
Expand All @@ -72,34 +99,54 @@ class SampleProfileMatcher {
uint64_t MismatchedCallsiteSamples = 0;
uint64_t RecoveredCallsiteSamples = 0;

// Profile call-graph matching statstics:
uint64_t NumRecoveredUnusedSamples = 0;
uint64_t NumRecoveredUnusedFunc = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the names above, how about:

NumCallGraphRecoveredProfiledFunc
NumCallGraphRecoveredFuncSamples


// A dummy name for unknown indirect callee, used to differentiate from a
// non-call instruction that also has an empty callee name.
static constexpr const char *UnknownIndirectCallee =
"unknown.indirect.callee";

public:
SampleProfileMatcher(Module &M, SampleProfileReader &Reader,
const PseudoProbeManager *ProbeManager,
ThinOrFullLTOPhase LTOPhase)
: M(M), Reader(Reader), ProbeManager(ProbeManager), LTOPhase(LTOPhase){};
void runOnModule();
SampleProfileMatcher(
Module &M, SampleProfileReader &Reader,
const PseudoProbeManager *ProbeManager, ThinOrFullLTOPhase LTOPhase,
HashKeyMap<std::unordered_map, FunctionId, Function *> &SymMap,
std::shared_ptr<ProfileSymbolList> PSL)
: M(M), Reader(Reader), ProbeManager(ProbeManager), LTOPhase(LTOPhase),
SymbolMap(&SymMap), PSL(PSL) {};
void runOnModule(std::vector<Function *> &OrderedFuncList);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably better not to have runOnModule not depend on a passed in ordered list. Given a module, the top-down order is deterministic. Depending on passed in order can be error prone as it's not guaranteed to be top-down.

We can have SampleProfileMatcher ctor take CG, and build top down order from within runOnModule.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, done.(that needs some refactoring to reuse the code from SampleLoader)

void clearMatchingData() {
// Do not clear FuncMappings, it stores IRLoc to ProfLoc remappings which
// will be used for sample loader.
FuncCallsiteMatchStates.clear();
FlattenedProfiles.clear();

NewIRFunctions.clear();
FuncToProfileNameMap.clear();
ProfileNameToFuncMap.clear();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clear doesn't free memory held by the container itself: https://en.cppreference.com/w/cpp/container/vector/clear

To free memory, a common trick is to swap with empty container.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, changing to use swap. thanks for the suggestion!

}

private:
FunctionSamples *getFlattenedSamplesFor(const Function &F) {
StringRef CanonFName = FunctionSamples::getCanonicalFnName(F);
auto It = FlattenedProfiles.find(FunctionId(CanonFName));
FunctionSamples *getFlattenedSamplesFor(const FunctionId &Fname) {
auto It = FlattenedProfiles.find(Fname);
if (It != FlattenedProfiles.end())
return &It->second;
return nullptr;
}
FunctionSamples *getFlattenedSamplesFor(const Function &F) {
StringRef CanonFName = FunctionSamples::getCanonicalFnName(F);
return getFlattenedSamplesFor(FunctionId(CanonFName));
}
void getFilteredAnchorList(const AnchorMap &IRAnchors,
const AnchorMap &ProfileAnchors,
AnchorList &FilteredIRAnchorsList,
AnchorList &FilteredProfileAnchorList);
void runOnFunction(Function &F);
void findIRAnchors(const Function &F, AnchorMap &IRAnchors);
void findProfileAnchors(const FunctionSamples &FS, AnchorMap &ProfileAnchors);
void findIRAnchors(const Function &F, AnchorMap &IRAnchors) const;
void findProfileAnchors(const FunctionSamples &FS,
AnchorMap &ProfileAnchors) const;
// Record the callsite match states for profile staleness report, the result
// is saved in FuncCallsiteMatchStates.
void recordCallsiteMatchStates(const Function &F, const AnchorMap &IRAnchors,
Expand Down Expand Up @@ -141,6 +188,12 @@ class SampleProfileMatcher {
}
void distributeIRToProfileLocationMap();
void distributeIRToProfileLocationMap(FunctionSamples &FS);
// Check if the two functions are equal. If FindMatchedProfileOnly is set,
// only search the existing matched function. Otherwise, if the two functions
// are both new, try to match the two functions.
bool isFunctionEqual(const FunctionId &IRFuncName,
const FunctionId &ProfileFuncName,
bool FindMatchedProfileOnly);
// This function implements the Myers diff algorithm used for stale profile
// matching. The algorithm provides a simple and efficient way to find the
// Longest Common Subsequence(LCS) or the Shortest Edit Script(SES) of two
Expand All @@ -151,15 +204,32 @@ class SampleProfileMatcher {
// parts from the resulting SES are used to remap the IR locations to the
// profile locations. As the number of function callsite is usually not big,
// we currently just implements the basic greedy version(page 6 of the paper).
LocToLocMap
longestCommonSequence(const AnchorList &IRCallsiteAnchors,
const AnchorList &ProfileCallsiteAnchors) const;
LocToLocMap longestCommonSequence(const AnchorList &IRCallsiteAnchors,
const AnchorList &ProfileCallsiteAnchors,
bool MatchUnusedFunction);
void matchNonCallsiteLocs(const LocToLocMap &AnchorMatchings,
const AnchorMap &IRAnchors,
LocToLocMap &IRToProfileLocationMap);
void runStaleProfileMatching(const Function &F, const AnchorMap &IRAnchors,
const AnchorMap &ProfileAnchors,
LocToLocMap &IRToProfileLocationMap);
LocToLocMap &IRToProfileLocationMap,
bool RunCFGMatching, bool RunCGMatching);
bool functionMatchesProfileHelper(const Function &IRFunc,
const FunctionId &ProfFunc);
// Determine if the function matches profile by computing a similarity ratio
// between two callsite anchors extracted from function and profile. If it's
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: two callsite anchors -> two sequences of callsite anchors

// above the threshold, the function matches the profile.
bool functionMatchesProfile(Function &IRFunc, const FunctionId &ProfFunc,
bool FindMatchedProfileOnly);
void matchProfileForNewFunctions(const StringMap<Function *> &NewIRFunctions,
FunctionSamples &FS);
// Find functions that don't show in the profile or profile symbol list,
// which are supposed to be new functions. We use them as the targets for
// renaming matching.
void findNewIRFunctions();
void updateProfillesAndSymbolMap();
wlei-llvm marked this conversation as resolved.
Show resolved Hide resolved
void updateProfileWithNewName(FunctionSamples &FuncProfile);
void runCallGraphMatching();
void reportOrPersistProfileStats();
};
} // end namespace llvm
Expand Down
10 changes: 6 additions & 4 deletions llvm/lib/Transforms/IPO/SampleProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ class SampleProfileLoader final : public SampleProfileLoaderBaseImpl<Function> {

/// Profle Symbol list tells whether a function name appears in the binary
/// used to generate the current profile.
std::unique_ptr<ProfileSymbolList> PSL;
std::shared_ptr<ProfileSymbolList> PSL;

/// Total number of samples collected in this profile.
///
Expand Down Expand Up @@ -2077,7 +2077,7 @@ bool SampleProfileLoader::doInitialization(Module &M,
if (ReportProfileStaleness || PersistProfileStaleness ||
SalvageStaleProfile) {
MatchingManager = std::make_unique<SampleProfileMatcher>(
M, *Reader, ProbeManager.get(), LTOPhase);
M, *Reader, ProbeManager.get(), LTOPhase, SymbolMap, PSL);
}

return true;
Expand Down Expand Up @@ -2196,14 +2196,16 @@ bool SampleProfileLoader::runOnModule(Module &M, ModuleAnalysisManager *AM,
assert(SymbolMap.count(FunctionId()) == 0 &&
"No empty StringRef should be added in SymbolMap");

std::vector<Function *> OrderedFuncList = buildFunctionOrder(M, CG);

if (ReportProfileStaleness || PersistProfileStaleness ||
SalvageStaleProfile) {
MatchingManager->runOnModule();
MatchingManager->runOnModule(OrderedFuncList);
MatchingManager->clearMatchingData();
}

bool retval = false;
for (auto *F : buildFunctionOrder(M, CG)) {
for (auto *F : OrderedFuncList) {
assert(!F->isDeclaration());
clearFunctionData();
retval |= runOnFunction(*F, AM);
Expand Down
Loading
Loading