[llvm] [CSSPGO] Compute and report profile matching recovered callsites and samples (PR #79090)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 31 22:51:58 PST 2024
================
@@ -2443,53 +2372,222 @@ void SampleProfileMatcher::runOnFunction(const Function &F) {
std::map<LineLocation, std::unordered_set<FunctionId>> ProfileAnchors;
findProfileAnchors(*FSFlattened, ProfileAnchors);
- // Detect profile mismatch for profile staleness metrics report.
- // Skip reporting the metrics for imported functions.
- if (!GlobalValue::isAvailableExternallyLinkage(F.getLinkage()) &&
- (ReportProfileStaleness || PersistProfileStaleness)) {
- // Use top-level nested FS for counting profile mismatch metrics since
- // currently once a callsite is mismatched, all its children profiles are
- // dropped.
- if (const auto *FS = Reader.getSamplesFor(F))
- countProfileMismatches(F, *FS, IRAnchors, ProfileAnchors);
- }
+ // Compute the callsite match states for profile staleness report.
+ if (ReportProfileStaleness || PersistProfileStaleness)
+ computeCallsiteMatchStates(F, IRAnchors, ProfileAnchors, LocToLocMap());
// Run profile matching for checksum mismatched profile, currently only
// support for pseudo-probe.
if (SalvageStaleProfile && FunctionSamples::ProfileIsProbeBased &&
!ProbeManager->profileIsValid(F, *FSFlattened)) {
// The matching result will be saved to IRToProfileLocationMap, create a new
// map for each function.
+ auto &IRToProfileLocationMap = getIRToProfileLocationMap(F);
runStaleProfileMatching(F, IRAnchors, ProfileAnchors,
- getIRToProfileLocationMap(F));
+ IRToProfileLocationMap);
+ // Find and update callsite match states after matching.
+ if ((ReportProfileStaleness || PersistProfileStaleness) &&
+ !IRToProfileLocationMap.empty())
+ computeCallsiteMatchStates(F, IRAnchors, ProfileAnchors,
+ IRToProfileLocationMap);
}
}
-void SampleProfileMatcher::runOnModule() {
- ProfileConverter::flattenProfile(Reader.getProfiles(), FlattenedProfiles,
- FunctionSamples::ProfileIsCS);
- for (auto &F : M) {
- if (F.isDeclaration() || !F.hasFnAttribute("use-sample-profile"))
+void SampleProfileMatcher::computeCallsiteMatchStates(
+ const Function &F, const std::map<LineLocation, StringRef> &IRAnchors,
+ const std::map<LineLocation, std::unordered_set<FunctionId>>
+ &ProfileAnchors,
+ const LocToLocMap &IRToProfileLocationMap) {
+ // Use the matching result to determine if it's in post-match phrase.
+ bool IsPostMatch = !IRToProfileLocationMap.empty();
+ auto &MismatchedCallsites =
+ FuncCallsiteMatchStates[FunctionSamples::getCanonicalFnName(F.getName())];
+
+ auto MapIRLocToProfileLoc = [&](const LineLocation &IRLoc) {
+ const auto &ProfileLoc = IRToProfileLocationMap.find(IRLoc);
+ if (ProfileLoc != IRToProfileLocationMap.end())
+ return ProfileLoc->second;
+ else
+ return IRLoc;
+ };
+
+ std::set<LineLocation> MatchedCallsites;
+ for (const auto &I : IRAnchors) {
+ // In post-match, use the matching result to remap the current IR callsite.
+ const auto &Loc = MapIRLocToProfileLoc(I.first);
+ const auto &IRCalleeName = I.second;
+ const auto &It = ProfileAnchors.find(Loc);
+ if (It == ProfileAnchors.end())
continue;
- runOnFunction(F);
+ const auto &Callees = It->second;
+
+ // Since indirect call does not have CalleeName, check conservatively if
+ // callsite in the profile is a callsite location. This is to reduce num of
+ // false positive since otherwise all the indirect call samples will be
+ // reported as mismatching.
+ if (IRCalleeName == SampleProfileMatcher::UnknownIndirectCallee)
+ MatchedCallsites.insert(Loc);
+ // TODO : Ideally, we should ensure it's a direct callsite location(Callees
+ // size is 1). However, there may be a bug for profile merge(like ODR
+ // violation) that causes the callees size to be more than 1. After we fix
+ // the bug, we can remove this check.
+ else if (Callees.count(getRepInFormat(IRCalleeName)))
+ MatchedCallsites.insert(Loc);
+ }
+
+ // Check if there are any callsites in the profile that does not match to any
+ // IR callsites, those callsite samples will be discarded.
+ for (const auto &I : ProfileAnchors) {
+ const auto &Loc = I.first;
+ [[maybe_unused]] const auto &Callees = I.second;
+ assert(!Callees.empty() && "Callees should not be empty");
+ if (IsPostMatch) {
+ if (MatchedCallsites.count(Loc)) {
----------------
WenleiHe wrote:
Looking at how `MatchedCallsites` is being used, I'm thinking about whether we can hold everything directly in `MismatchedCallsites` and skip `MatchedCallsites`.
Specifically, how about we record the entries directly in `MismatchedCallsites` (with Recovered or Matched flag set depending on whether it's post-match) from the first loop. And then in this 2nd loop, we add in more new entries and mark these new entries Mismatched.
Additionally, I'm not sure `MismatchedCallsites` is a good name because it contains both matched and mismatched call sites.
https://github.com/llvm/llvm-project/pull/79090
More information about the llvm-commits
mailing list