Updates for the handling of Profile data in the JIT
authorBrian Sullivan <briansul@microsoft.com>
Sat, 18 Mar 2017 01:42:50 +0000 (18:42 -0700)
committerBrian Sullivan <briansul@microsoft.com>
Sat, 18 Mar 2017 01:54:46 +0000 (18:54 -0700)
Use the type weight_t instead of unsigned when dealing with BasicBlock weights.
Added new method getCalledCount to return the number of times that the method was called.
Added new method fgProfileRunsCount to return the number of scenario runs for the profile data.
Made the method fgIsUsingProfileWeight public instead of protected
Renamed fgCalledWeight to fgCalledCount
Updated the logic in getBBWeight and added comments explaining how it works.

Commit migrated from https://github.com/dotnet/coreclr/commit/756214424d736aa3f7f57af013b89aac51d6ba78

src/coreclr/src/jit/block.h
src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/emit.cpp
src/coreclr/src/jit/flowgraph.cpp
src/coreclr/src/jit/lclvars.cpp

index 2f84b9b..2d6cdf5 100644 (file)
@@ -518,8 +518,11 @@ struct BasicBlock : private LIR::Range
 
     weight_t bbWeight; // The dynamic execution weight of this block
 
+    // getCalledCount -- get the value used to normalize weights for this method
+    weight_t getCalledCount(Compiler* comp);
+
     // getBBWeight -- get the normalized weight of this block
-    unsigned getBBWeight(Compiler* comp);
+    weight_t getBBWeight(Compiler* comp);
 
     // hasProfileWeight -- Returns true if this block's weight came from profile data
     bool hasProfileWeight() const
@@ -530,7 +533,7 @@ struct BasicBlock : private LIR::Range
     // setBBWeight -- if the block weight is not derived from a profile,
     // then set the weight to the input weight, making sure to not overflow BB_MAX_WEIGHT
     // Note to set the weight from profile data, instead use setBBProfileWeight
-    void setBBWeight(unsigned weight)
+    void setBBWeight(weight_t weight)
     {
         if (!hasProfileWeight())
         {
@@ -548,7 +551,7 @@ struct BasicBlock : private LIR::Range
 
     // modifyBBWeight -- same as setBBWeight, but also make sure that if the block is rarely run, it stays that
     // way, and if it's not rarely run then its weight never drops below 1.
-    void modifyBBWeight(unsigned weight)
+    void modifyBBWeight(weight_t weight)
     {
         if (this->bbWeight != BB_ZERO_WEIGHT)
         {
index 7baba5d..7d2e66e 100644 (file)
@@ -3552,7 +3552,7 @@ public:
     bool                 fgSlopUsedInEdgeWeights;  // true if their was some slop used when computing the edge weights
     bool                 fgRangeUsedInEdgeWeights; // true if some of the edgeWeight are expressed in Min..Max form
     bool                 fgNeedsUpdateFlowGraph;   // true if we need to run fgUpdateFlowGraph
-    BasicBlock::weight_t fgCalledWeight;           // count of the number of times this method was called
+    BasicBlock::weight_t fgCalledCount;            // count of the number of times this method was called
                                                    // This is derived from the profile data
                                                    // or is BB_UNITY_WEIGHT when we don't have profile data
 
@@ -4528,12 +4528,22 @@ protected:
 
     bool fgHaveProfileData();
     bool fgGetProfileWeightForBasicBlock(IL_OFFSET offset, unsigned* weight);
+    void fgInstrumentMethod();
 
+public:
+    // fgIsUsingProfileWeights - returns true if we have real profile data for this method
+    //                           or if we have some fake profile data for the stress mode
     bool fgIsUsingProfileWeights()
     {
         return (fgHaveProfileData() || fgStressBBProf());
     }
-    void fgInstrumentMethod();
+
+    // fgProfileRunsCount - returns total number of scenario runs for the profile data
+    //                      or BB_UNITY_WEIGHT when we aren't using profile data.
+    unsigned fgProfileRunsCount()
+    {
+        return fgIsUsingProfileWeights() ? fgNumProfileRuns : BB_UNITY_WEIGHT;
+    }
 
 //-------- Insert a statement at the start or end of a basic block --------
 
index 6d08b52..e9ba4e5 100644 (file)
@@ -4502,7 +4502,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
     //
     if (emitComp->fgHaveProfileData())
     {
-        if (emitComp->fgCalledWeight > (BB_VERY_HOT_WEIGHT * emitComp->fgNumProfileRuns))
+        if (emitComp->fgCalledCount > (BB_VERY_HOT_WEIGHT * emitComp->fgProfileRunsCount()))
         {
             allocMemFlag = CORJIT_ALLOCMEM_FLG_16BYTE_ALIGN;
         }
index dd99411..d9463a8 100644 (file)
@@ -44,7 +44,7 @@ void Compiler::fgInit()
     fgSlopUsedInEdgeWeights  = false;
     fgRangeUsedInEdgeWeights = true;
     fgNeedsUpdateFlowGraph   = false;
-    fgCalledWeight           = BB_ZERO_WEIGHT;
+    fgCalledCount            = BB_ZERO_WEIGHT;
 
     /* We haven't yet computed the dominator sets */
     fgDomsComputed = false;
@@ -12554,7 +12554,7 @@ void Compiler::fgComputeEdgeWeights()
         }
 #endif // DEBUG
         fgHaveValidEdgeWeights = false;
-        fgCalledWeight         = BB_UNITY_WEIGHT;
+        fgCalledCount          = BB_UNITY_WEIGHT;
     }
 
 #if DEBUG
@@ -12692,12 +12692,12 @@ void Compiler::fgComputeEdgeWeights()
     }
 #endif
 
-    // When we are not using profile data we have already setup fgCalledWeight
+    // When we are not using profile data we have already setup fgCalledCount
     // only set it here if we are using profile data
     //
     if (fgIsUsingProfileWeights())
     {
-        // If the first block has one ref then it's weight is the fgCalledWeight
+        // If the first block has one ref then it's weight is the fgCalledCount
         // otherwise we have backedge's into the first block so instead
         // we use the sum of the return block weights.
         // If the profile data has a 0 for the returnWeoght
@@ -12705,11 +12705,11 @@ void Compiler::fgComputeEdgeWeights()
         //
         if ((fgFirstBB->countOfInEdges() == 1) || (returnWeight == 0))
         {
-            fgCalledWeight = fgFirstBB->bbWeight;
+            fgCalledCount = fgFirstBB->bbWeight;
         }
         else
         {
-            fgCalledWeight = returnWeight;
+            fgCalledCount = returnWeight;
         }
     }
 
@@ -12723,7 +12723,7 @@ void Compiler::fgComputeEdgeWeights()
         //
         if (bDst == fgFirstBB)
         {
-            bDstWeight -= fgCalledWeight;
+            bDstWeight -= fgCalledCount;
         }
 
         for (edge = bDst->bbPreds; edge != nullptr; edge = edge->flNext)
@@ -12888,7 +12888,7 @@ void Compiler::fgComputeEdgeWeights()
                 //
                 if (bDst == fgFirstBB)
                 {
-                    bDstWeight -= fgCalledWeight;
+                    bDstWeight -= fgCalledCount;
                 }
 
                 UINT64 minEdgeWeightSum = 0;
@@ -19132,7 +19132,7 @@ bool Compiler::fgDumpFlowGraph(Phases phase)
         return false;
     }
     bool        validWeights  = fgHaveValidEdgeWeights;
-    unsigned    calledCount   = max(fgCalledWeight, BB_UNITY_WEIGHT) / BB_UNITY_WEIGHT;
+    unsigned    calledCount   = max(fgCalledCount, BB_UNITY_WEIGHT) / BB_UNITY_WEIGHT;
     double      weightDivisor = (double)(calledCount * BB_UNITY_WEIGHT);
     const char* escapedString;
     const char* regionString = "NONE";
index e0d7d51..319a64d 100644 (file)
@@ -2394,8 +2394,41 @@ unsigned Compiler::lvaLclExactSize(unsigned varNum)
     return genTypeSize(varType);
 }
 
+// getCalledCount -- get the value used to normalized weights for this method
+//  if we don't have profile data then getCalledCount will return BB_UNITY_WEIGHT (100)
+//  otherwise it returns the number of times that profile data says the method was called.
+//
+BasicBlock::weight_t BasicBlock::getCalledCount(Compiler* comp)
+{
+    // when we don't have profile data then fgCalledCount will be BB_UNITY_WEIGHT (100)
+    BasicBlock::weight_t calledCount = comp->fgCalledCount;
+
+    // If we haven't yet reach the place where we setup fgCalledCount it could still be zero
+    // so return a reasonable value to use until we set it.
+    //
+    if (calledCount == 0)
+    {
+        if (comp->fgIsUsingProfileWeights())
+        {
+            // When we use profile data block counts we have exact counts,
+            // not multiples of BB_UNITY_WEIGHT (100)
+            calledCount = 1;
+        }
+        else
+        {
+            calledCount = comp->fgFirstBB->bbWeight;
+
+            if (calledCount == 0)
+            {
+                calledCount = BB_UNITY_WEIGHT;
+            }
+        }
+    }
+    return calledCount;
+}
+
 // getBBWeight -- get the normalized weight of this block
-unsigned BasicBlock::getBBWeight(Compiler* comp)
+BasicBlock::weight_t BasicBlock::getBBWeight(Compiler* comp)
 {
     if (this->bbWeight == 0)
     {
@@ -2403,22 +2436,50 @@ unsigned BasicBlock::getBBWeight(Compiler* comp)
     }
     else
     {
-        unsigned calledWeight = comp->fgCalledWeight;
-        if (calledWeight == 0)
-        {
-            calledWeight = comp->fgFirstBB->bbWeight;
-            if (calledWeight == 0)
-            {
-                calledWeight = BB_UNITY_WEIGHT;
-            }
-        }
+        weight_t calledCount = getCalledCount(comp);
+
+        // Normalize the bbWeights by multiplying by BB_UNITY_WEIGHT and dividing by the calledCount.
+        //
+        // 1. For methods that do not have IBC data the called weight will always be 100 (BB_UNITY_WEIGHT)
+        //     and the entry point bbWeight value is almost always 100 (BB_UNITY_WEIGHT)
+        // 2.  For methods that do have IBC data the called weight is the actual number of calls
+        //     from the IBC data and the entry point bbWeight value is almost always the actual
+        //     number of calls from the IBC data.
+        //
+        // "almost always" - except for the rare case where a loop backedge jumps to BB01
+        //
+        // We also perform a rounding operation by adding half of the 'calledCount' before performing
+        // the division.
+        //
+        // Thus for both cases we will return 100 (BB_UNITY_WEIGHT) for the entry point BasicBlock
+        //
+        // Note that with a 100 (BB_UNITY_WEIGHT) values between 1 and 99 represent decimal fractions.
+        // (i.e. 33 represents 33% and 75 represents 75%, and values greater than 100 require
+        //  some kind of loop backedge)
+        //
+
         if (this->bbWeight < (BB_MAX_WEIGHT / BB_UNITY_WEIGHT))
         {
-            return max(1, (((this->bbWeight * BB_UNITY_WEIGHT) + (calledWeight / 2)) / calledWeight));
+            // Calculate the result using unsigned arithmetic
+            weight_t result = ((this->bbWeight * BB_UNITY_WEIGHT) + (calledCount / 2)) / calledCount;
+
+            // We don't allow a value of zero, as that would imply rarely run
+            return max(1, result);
         }
         else
         {
-            return (unsigned)((((double)this->bbWeight * (double)BB_UNITY_WEIGHT) / (double)calledWeight) + 0.5);
+            // Calculate the full result using floating point
+            double fullResult = ((double)this->bbWeight * (double)BB_UNITY_WEIGHT) / (double)calledCount;
+
+            if (fullResult < (double)BB_MAX_WEIGHT)
+            {
+                // Add 0.5 and truncate to unsigned
+                return (weight_t)(fullResult + 0.5);
+            }
+            else
+            {
+                return BB_MAX_WEIGHT;
+            }
         }
     }
 }