[llvm] r251568 - SamplePGO - Add flag to check sampling coverage.

Diego Novillo via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 28 15:30:26 PDT 2015


Author: dnovillo
Date: Wed Oct 28 17:30:25 2015
New Revision: 251568

URL: http://llvm.org/viewvc/llvm-project?rev=251568&view=rev
Log:
SamplePGO - Add flag to check sampling coverage.

This adds the flag -mllvm -sample-profile-check-coverage=N to the
SampleProfile pass. N is the percent of input sample records that the
user expects to apply.  If the pass does not use N% (or more) of the
sample records in the input, it emits a warning.

This is useful to detect some forms of stale profiles. If the code has
drifted enough from the original profile, there will be records that do
not match the IR anymore.

This will not detect cases where a sample profile record for line L is
referring to some other instructions that also used to be at line L.

Added:
    llvm/trunk/test/Transforms/SampleProfile/Inputs/coverage-warning.prof
    llvm/trunk/test/Transforms/SampleProfile/coverage-warning.ll
Modified:
    llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp

Modified: llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp?rev=251568&r1=251567&r2=251568&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp (original)
+++ llvm/trunk/lib/Transforms/IPO/SampleProfile.cpp Wed Oct 28 17:30:25 2015
@@ -63,6 +63,10 @@ static cl::opt<unsigned> SampleProfileMa
     "sample-profile-max-propagate-iterations", cl::init(100),
     cl::desc("Maximum number of iterations to go through when propagating "
              "sample block/edge weights through the CFG."));
+static cl::opt<unsigned> SampleProfileCoverage(
+    "sample-profile-check-coverage", cl::init(0), cl::value_desc("N"),
+    cl::desc("Emit a warning if less than N% of samples in the input profile "
+             "are matched to the IR."));
 
 namespace {
 typedef DenseMap<const BasicBlock *, uint64_t> BlockWeightMap;
@@ -174,6 +178,64 @@ protected:
   /// \brief Flag indicating whether the profile input loaded successfully.
   bool ProfileIsValid;
 };
+
+class SampleCoverageTracker {
+public:
+  SampleCoverageTracker() : SampleCoverage() {}
+
+  void markSamplesUsed(const FunctionSamples *Samples, uint32_t LineOffset,
+                       uint32_t Discriminator);
+  unsigned computeCoverage(const FunctionSamples *Samples) const;
+  unsigned getNumUsedSamples(const FunctionSamples *Samples) const;
+
+private:
+  typedef DenseMap<LineLocation, unsigned> BodySampleCoverageMap;
+  typedef DenseMap<const FunctionSamples *, BodySampleCoverageMap>
+      FunctionSamplesCoverageMap;
+
+  /// Coverage map for sampling records.
+  ///
+  /// This map keeps a record of sampling records that have been matched to
+  /// an IR instruction. This is used to detect some form of staleness in
+  /// profiles (see flag -sample-profile-check-coverage).
+  ///
+  /// Each entry in the map corresponds to a FunctionSamples instance.  This is
+  /// another map that counts how many times the sample record at the
+  /// given location has been used.
+  FunctionSamplesCoverageMap SampleCoverage;
+};
+
+SampleCoverageTracker CoverageTracker;
+}
+
+/// Mark as used the sample record for the given function samples at
+/// (LineOffset, Discriminator).
+void SampleCoverageTracker::markSamplesUsed(const FunctionSamples *Samples,
+                                            uint32_t LineOffset,
+                                            uint32_t Discriminator) {
+  BodySampleCoverageMap &Coverage = SampleCoverage[Samples];
+  Coverage[LineLocation(LineOffset, Discriminator)]++;
+}
+
+/// Return the number of sample records that were applied from this profile.
+unsigned
+SampleCoverageTracker::getNumUsedSamples(const FunctionSamples *Samples) const {
+  auto I = SampleCoverage.find(Samples);
+  return (I != SampleCoverage.end()) ? I->second.size() : 0;
+}
+
+/// Return the fraction of sample records used in this profile.
+///
+/// The returned value is an unsigned integer in the range 0-100 indicating
+/// the percentage of sample records that were used while applying this
+/// profile to the associated function.
+unsigned
+SampleCoverageTracker::computeCoverage(const FunctionSamples *Samples) const {
+  uint32_t NumTotalRecords = Samples->getBodySamples().size();
+  uint32_t NumUsedRecords = getNumUsedSamples(Samples);
+  assert(NumUsedRecords <= NumTotalRecords &&
+         "number of used records cannot exceed the total number of records");
+  return NumTotalRecords > 0 ? NumUsedRecords * 100 / NumTotalRecords : 100;
 }
 
 /// Clear all the per-function data used to load samples and propagate weights.
@@ -257,13 +319,17 @@ SampleProfileLoader::getInstWeight(const
   unsigned Lineno = DLoc.getLine();
   unsigned HeaderLineno = DIL->getScope()->getSubprogram()->getLine();
 
-  ErrorOr<uint64_t> R = FS->findSamplesAt(getOffset(Lineno, HeaderLineno),
-                                          DIL->getDiscriminator());
-  if (R)
+  uint32_t LineOffset = getOffset(Lineno, HeaderLineno);
+  uint32_t Discriminator = DIL->getDiscriminator();
+  ErrorOr<uint64_t> R = FS->findSamplesAt(LineOffset, Discriminator);
+  if (R) {
+    if (SampleProfileCoverage)
+      CoverageTracker.markSamplesUsed(FS, LineOffset, Discriminator);
     DEBUG(dbgs() << "    " << Lineno << "." << DIL->getDiscriminator() << ":"
                  << Inst << " (line offset: " << Lineno - HeaderLineno << "."
                  << DIL->getDiscriminator() << " - weight: " << R.get()
                  << ")\n");
+  }
   return R;
 }
 
@@ -311,6 +377,20 @@ bool SampleProfileLoader::computeBlockWe
     DEBUG(printBlockWeight(dbgs(), &BB));
   }
 
+  if (SampleProfileCoverage) {
+    unsigned Coverage = CoverageTracker.computeCoverage(Samples);
+    if (Coverage < SampleProfileCoverage) {
+      const char *Filename = getDISubprogram(&F)->getFilename().str().c_str();
+      F.getContext().diagnose(DiagnosticInfoSampleProfile(
+          Filename, getFunctionLoc(F),
+          Twine(CoverageTracker.getNumUsedSamples(Samples)) + " of " +
+              Twine(Samples->getBodySamples().size()) +
+              " available profile records (" + Twine(Coverage) +
+              "%) were applied",
+          DS_Warning));
+    }
+  }
+
   return Changed;
 }
 

Added: llvm/trunk/test/Transforms/SampleProfile/Inputs/coverage-warning.prof
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/SampleProfile/Inputs/coverage-warning.prof?rev=251568&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/SampleProfile/Inputs/coverage-warning.prof (added)
+++ llvm/trunk/test/Transforms/SampleProfile/Inputs/coverage-warning.prof Wed Oct 28 17:30:25 2015
@@ -0,0 +1,5 @@
+foo:30000:100
+ 2: 28000
+ 3: 1000
+# This profile is stale. Function foo() does not have a line 8 anymore.
+ 8: 1700

Added: llvm/trunk/test/Transforms/SampleProfile/coverage-warning.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/SampleProfile/coverage-warning.ll?rev=251568&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/SampleProfile/coverage-warning.ll (added)
+++ llvm/trunk/test/Transforms/SampleProfile/coverage-warning.ll Wed Oct 28 17:30:25 2015
@@ -0,0 +1,45 @@
+; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/coverage-warning.prof -sample-profile-check-coverage=90 2>& 1 | FileCheck %s
+define i32 @foo(i32 %i) {
+; The profile has samples for line locations that are no longer present.
+; Coverage does not reach 90%, so we should get this warning:
+;
+; CHECK: warning: coverage-warning.c:1: 2 of 3 available profile records (66%) were applied
+entry:
+  %retval = alloca i32, align 4
+  %i.addr = alloca i32, align 4
+  store i32 %i, i32* %i.addr, align 4
+  %0 = load i32, i32* %i.addr, align 4, !dbg !9
+  %cmp = icmp sgt i32 %0, 1000, !dbg !10
+  br i1 %cmp, label %if.then, label %if.end, !dbg !9
+
+if.then:                                          ; preds = %entry
+  store i32 30, i32* %retval, align 4, !dbg !11
+  br label %return, !dbg !11
+
+if.end:                                           ; preds = %entry
+  store i32 3, i32* %retval, align 4, !dbg !12
+  br label %return, !dbg !12
+
+return:                                           ; preds = %if.end, %if.then
+  %1 = load i32, i32* %retval, align 4, !dbg !13
+  ret i32 %1, !dbg !13
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!6, !7}
+!llvm.ident = !{!8}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 251524) (llvm/trunk 251531)", isOptimized: false, runtimeVersion: 0, emissionKind: 2, enums: !2, subprograms: !3)
+!1 = !DIFile(filename: "coverage-warning.c", directory: ".")
+!2 = !{}
+!3 = !{!4}
+!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, function: i32 (i32)* @foo, variables: !2)
+!5 = !DISubroutineType(types: !2)
+!6 = !{i32 2, !"Dwarf Version", i32 4}
+!7 = !{i32 2, !"Debug Info Version", i32 3}
+!8 = !{!"clang version 3.8.0 (trunk 251524) (llvm/trunk 251531)"}
+!9 = !DILocation(line: 2, column: 7, scope: !4)
+!10 = !DILocation(line: 2, column: 9, scope: !4)
+!11 = !DILocation(line: 3, column: 5, scope: !4)
+!12 = !DILocation(line: 4, column: 3, scope: !4)
+!13 = !DILocation(line: 5, column: 1, scope: !4)




More information about the llvm-commits mailing list