[llvm] [SampleFDO] Stale profile call-graph matching (PR #95135)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 16 12:09:50 PDT 2024


================
@@ -590,14 +688,237 @@ void SampleProfileMatcher::computeAndReportProfileStaleness() {
   }
 }
 
+void SampleProfileMatcher::findNewIRFunctions() {
+  // TODO: Support MD5 profile.
+  if (FunctionSamples::UseMD5)
+    return;
+  StringSet<> NamesInProfile;
+  if (auto NameTable = Reader.getNameTable()) {
+    for (auto Name : *NameTable)
+      NamesInProfile.insert(Name.stringRef());
+  }
+
+  for (auto &F : M) {
+    // Skip declarations, as even if the function can be matched, we have
+    // nothing to do with it.
+    if (F.isDeclaration())
+      continue;
+
+    StringRef CanonFName = FunctionSamples::getCanonicalFnName(F.getName());
+    const auto *FS = getFlattenedSamplesFor(F);
+    if (FS)
+      continue;
+
+    // For extended binary, functions are fully inlined may not be loaded in the
+    // top-level profile, so check the NameTable which has the all symbol names
+    // in profile.
+    if (NamesInProfile.count(CanonFName))
+      continue;
+
+    // For extended binary, non-profiled function symbols are in the profile
+    // symbol list table.
+    if (PSL && PSL->contains(CanonFName))
+      continue;
+
+    LLVM_DEBUG(dbgs() << "Function " << CanonFName
+                      << " is not in profile or symbol list table.\n");
+    NewIRFunctions[FunctionId(CanonFName)] = &F;
+  }
+}
+
+bool SampleProfileMatcher::functionMatchesProfileHelper(
+    const Function &IRFunc, const FunctionId &ProfFunc) {
+  // The value is in the range [0, 1]. The bigger the value is, the more similar
+  // two sequences are.
+  float Similarity = 0.0;
+
+  const auto *FSFlattened = getFlattenedSamplesFor(ProfFunc);
+  assert(FSFlattened && "Flattened profile sample is null");
+  // Similarity check may not be reliable if the function is tiny, we use the
+  // number of basic block as a proxy for the function complexity and skip the
+  // matching if it's too small.
+  if (IRFunc.size() < MinFuncCountForCGMatching ||
+      FSFlattened->getBodySamples().size() < MinFuncCountForCGMatching)
+    return false;
+
+  // For probe-based function, we first trust the checksum info. If the checksum
+  // doesn't match, we continue checking for similarity.
+  if (FunctionSamples::ProfileIsProbeBased) {
+    const auto *FuncDesc = ProbeManager->getDesc(IRFunc);
+    if (FuncDesc &&
+        !ProbeManager->profileIsHashMismatched(*FuncDesc, *FSFlattened)) {
+      LLVM_DEBUG(dbgs() << "The checksums for " << IRFunc.getName()
+                        << "(IR) and " << ProfFunc << "(Profile) match.\n");
+
+      return true;
+    }
+  }
+
+  AnchorMap IRAnchors;
+  findIRAnchors(IRFunc, IRAnchors);
+  AnchorMap ProfileAnchors;
+  findProfileAnchors(*FSFlattened, ProfileAnchors);
+
+  AnchorList FilteredIRAnchorsList;
+  AnchorList FilteredProfileAnchorList;
+  getFilteredAnchorList(IRAnchors, ProfileAnchors, FilteredIRAnchorsList,
+                        FilteredProfileAnchorList);
+
+  // Similarly skip the matching if the num of anchors is not enough.
+  if (FilteredIRAnchorsList.size() < MinCallCountForCGMatching ||
+      FilteredProfileAnchorList.size() < MinCallCountForCGMatching)
+    return false;
+
+  // Use the diff algorithm to find the LCS between IR and profile.
+
+  // Don't recursively match the callee function to avoid infinite matching,
+  // callee functions will be handled later since it's processed in top-down
+  // order .
+  LocToLocMap MatchedAnchors = longestCommonSequence(
+      FilteredIRAnchorsList, FilteredProfileAnchorList, false);
+
+  Similarity =
+      static_cast<float>(MatchedAnchors.size()) * 2 /
+      (FilteredIRAnchorsList.size() + FilteredProfileAnchorList.size());
+
+  LLVM_DEBUG(dbgs() << "The similarity between " << IRFunc.getName()
+                    << "(IR) and " << ProfFunc << "(profile) is "
+                    << format("%.2f", Similarity) << "\n");
+  assert((Similarity >= 0 && Similarity <= 1.0) &&
+         "Similarity value should be in [0, 1]");
+  return Similarity * 100 > FuncProfileSimilarityThreshold;
+}
+
+// If FindMatchedProfileOnly is set to true, only use the processed function
+// results. This is used for skipping the repeated recursive matching.
+bool SampleProfileMatcher::functionMatchesProfile(Function &IRFunc,
+                                                  const FunctionId &ProfFunc,
+                                                  bool FindMatchedProfileOnly) {
+  auto R = FuncToProfileNameMap.find({&IRFunc, ProfFunc});
+  if (R != FuncToProfileNameMap.end())
+    return R->second;
+
+  if (FindMatchedProfileOnly)
+    return false;
+
+  bool Matched = functionMatchesProfileHelper(IRFunc, ProfFunc);
+  FuncToProfileNameMap[{&IRFunc, ProfFunc}] = Matched;
+  if (Matched) {
+    ProfileNameToFuncMap[ProfFunc] = &IRFunc;
+    LLVM_DEBUG(dbgs() << "Function:" << IRFunc.getName()
+                      << " matches profile:" << ProfFunc << "\n");
+  }
+
+  return Matched;
+}
+
+void SampleProfileMatcher::updateProfileWithNewName(
+    FunctionSamples &FuncProfile) {
+  auto FindNewMatch =
+      [&](const FunctionId &ProfileName,
+          std::vector<std::pair<FunctionId, FunctionId>> &MatchResult,
+          [[maybe_unused]] const FunctionId &CallerName) {
+        auto P = ProfileNameToFuncMap.find(ProfileName);
+        if (P != ProfileNameToFuncMap.end()) {
+          FunctionId IRCallee(P->second->getName());
+          assert(IRCallee != ProfileName &&
+                 "New callee symbol is not a new function");
+          LLVM_DEBUG(dbgs()
+                     << "Profile name is updated from " << ProfileName << " to "
+                     << IRCallee << " under caller: " << CallerName << "\n");
+          MatchResult.emplace_back(IRCallee, ProfileName);
+        }
+      };
+
+  // Update non-inline callees.
+  for (auto &BS : const_cast<BodySampleMap &>(FuncProfile.getBodySamples())) {
+    // New function to old function pairs used to update the CallTargetMap.
+    std::vector<std::pair<FunctionId, FunctionId>> MatchResult;
+    SampleRecord::CallTargetMap &CTM =
+        const_cast<SampleRecord::CallTargetMap &>(BS.second.getCallTargets());
+    for (const auto &TS : CTM)
+      FindNewMatch(TS.first, MatchResult, FuncProfile.getFunction());
+    // Update the CallTargetMap.
+    for (const auto &P : MatchResult) {
+      uint64_t Samples = CTM[P.second];
+      if (ReportProfileStaleness || PersistProfileStaleness)
+        NumRecoveredUnusedSamples += Samples;
+      CTM[P.first] = Samples;
+      CTM.erase(P.second);
+    }
+  }
+
+  // Update inline callees recursively.
+  for (auto &CM :
+       const_cast<CallsiteSampleMap &>(FuncProfile.getCallsiteSamples())) {
+    auto &CalleeMap = CM.second;
+    // New function to old function pairs used to update the CallsiteSampleMap.
+    std::vector<std::pair<FunctionId, FunctionId>> MatchResult;
+    for (auto &CS : CalleeMap) {
+      FindNewMatch(CS.second.getFunction(), MatchResult,
+                   FuncProfile.getFunction());
+      updateProfileWithNewName(CS.second);
+    }
+    // Update the CalleeMap using the new name and remove the old entry.
+    for (auto &P : MatchResult) {
+      assert(P.first != P.second &&
+             "Renamed function name should be different from the old map key");
+      FunctionSamples &FS = CalleeMap[P.second];
+      if (ReportProfileStaleness || PersistProfileStaleness)
+        NumRecoveredUnusedSamples += FS.getTotalSamples();
+      FS.setFunction(P.first);
+      CalleeMap[P.first] = FS;
+      CalleeMap.erase(P.second);
+    }
+  }
+}
+
+void SampleProfileMatcher::updateProfilesAndSymbolMap() {
+  if (ProfileNameToFuncMap.empty())
+    return;
+  for (auto &P : Reader.getProfiles())
+    updateProfileWithNewName(P.second);
----------------
WenleiHe wrote:

What if we found a profile name - function name match based on callsite names, but the profile exists but wasn't loaded because with extbinary format we only load profile for functions in the current module by names.  

https://github.com/llvm/llvm-project/pull/95135


More information about the llvm-commits mailing list