Skip to content

[Deprecated][SampleFDO] Stale profile call-graph matching #92151

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

Closed
wants to merge 12 commits into from
Closed
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 FuncProfNameMapHash {
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,
FuncProfNameMapHash>
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,55 @@ class SampleProfileMatcher {
uint64_t MismatchedCallsiteSamples = 0;
uint64_t RecoveredCallsiteSamples = 0;

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

// 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);
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();
}

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 runCFGMatching(Function &F);
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 +189,11 @@ class SampleProfileMatcher {
}
void distributeIRToProfileLocationMap();
void distributeIRToProfileLocationMap(FunctionSamples &FS);
// Check if the two functions are equal. If MatchUnusedFunction is set and the
// two functions are both new, try to match the two functions.
bool isFunctionEqual(const FunctionId &IRFuncName,
const FunctionId &ProfileFuncName,
bool MatchUnusedFunction);
// 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
// above the threshold, the function matches the profile.
bool functionMatchesProfile(Function &IRFunc, const FunctionId &ProfFunc,
bool FindOnly);
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();
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