Skip to content

Commit 3182dfb

Browse files
authored
JIT: fix exit edge likelihood computation for capped loops (#84817)
The initial version of this only really worked for very simple cases. Generalize to handle loops with multiple exits better. The rough idea is to compute how much additional profile flow needs to come out of the loop to match the capped probability, and then try and find one exit block where we can adjust likelhoods to cause exactly this amount of extra flow to leave the loop. Closes #84789.
1 parent 87ba6ea commit 3182dfb

1 file changed

Lines changed: 88 additions & 32 deletions

File tree

src/coreclr/jit/fgprofilesynthesis.cpp

Lines changed: 88 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,12 +1140,16 @@ void ProfileSynthesis::ComputeCyclicProbabilities(SimpleLoop* loop)
11401140
//
11411141
if (cyclicWeight > cappedLikelihood)
11421142
{
1143+
JITDUMP("Cyclic weight " FMT_WT " > " FMT_WT "(cap) -- will reduce to cap\n", cyclicWeight, cappedLikelihood);
11431144
capped = true;
11441145
cyclicWeight = cappedLikelihood;
11451146
m_cappedCyclicProbabilities++;
11461147
}
11471148

1148-
weight_t cyclicProbability = 1.0 / (1.0 - cyclicWeight);
1149+
// Note this value is not actually a probability; it is the expected
1150+
// iteration count of the loop.
1151+
//
1152+
weight_t const cyclicProbability = 1.0 / (1.0 - cyclicWeight);
11491153

11501154
JITDUMP("For loop at " FMT_BB " cyclic weight is " FMT_WT " cyclic probability is " FMT_WT "%s\n",
11511155
loop->m_head->bbNum, cyclicWeight, cyclicProbability, capped ? " [capped]" : "");
@@ -1154,50 +1158,102 @@ void ProfileSynthesis::ComputeCyclicProbabilities(SimpleLoop* loop)
11541158

11551159
// Try and adjust loop exit likelihood to reflect capping.
11561160
// If there are multiple exits we just adjust the first one we can. This is somewhat arbitrary.
1161+
// If there are no exits, there's nothing we can do.
11571162
//
1158-
if (capped)
1163+
if (capped && (loop->m_exitEdges.size() > 0))
11591164
{
1160-
bool adjustedExit = false;
1165+
// Figure out how much flow exits the loop with the capped probablility
1166+
// and current block frequencies and exit likelihoods.
1167+
//
1168+
weight_t cappedExitWeight = 0.0;
11611169

11621170
for (FlowEdge* const exitEdge : loop->m_exitEdges)
11631171
{
1164-
BasicBlock* const exitBlock = exitEdge->getSourceBlock();
1172+
BasicBlock* const exitBlock = exitEdge->getSourceBlock();
1173+
weight_t const exitBlockFrequency = exitBlock->bbWeight;
1174+
weight_t const exitBlockWeight = exitBlockFrequency * cyclicProbability;
1175+
weight_t const exitWeight = exitEdge->getLikelihood() * exitBlockWeight;
1176+
cappedExitWeight += exitWeight;
1177+
JITDUMP("Exit from " FMT_BB " has weight " FMT_WT "\n", exitBlock->bbNum, exitWeight);
1178+
}
1179+
1180+
JITDUMP("Total exit weight " FMT_WT "\n", cappedExitWeight);
1181+
1182+
// We should end up with a value less than one since we input one unit of flow into the
1183+
// loop and are artificially capping the iteration count of the loop, so less weight is
1184+
// now flowing out than in. However because of rounding we might end up near or a bit over 1.0.
1185+
//
1186+
if ((cappedExitWeight + epsilon) < 1.0)
1187+
{
1188+
// We want to increase the exit likelihood of one exit block to create
1189+
// additional flow out of the loop. Figure out how much we need.
1190+
//
1191+
weight_t const missingExitWeight = 1.0 - cappedExitWeight;
1192+
JITDUMP("Loop exit flow deficit from capping is " FMT_WT "\n", missingExitWeight);
11651193

1166-
if ((exitBlock->bbJumpKind == BBJ_COND) &&
1167-
!Compiler::fgProfileWeightsEqual(exitBlock->bbWeight, 0.0, epsilon))
1194+
bool adjustedExit = false;
1195+
1196+
for (FlowEdge* const exitEdge : loop->m_exitEdges)
11681197
{
1169-
JITDUMP("Will adjust likelihood of the exit edge from loop exit block " FMT_BB
1170-
" to reflect capping; current likelihood is " FMT_WT "\n",
1171-
exitBlock->bbNum, exitEdge->getLikelihood());
1172-
1173-
BasicBlock* const jump = exitBlock->bbJumpDest;
1174-
BasicBlock* const next = exitBlock->bbNext;
1175-
FlowEdge* const jumpEdge = m_comp->fgGetPredForBlock(jump, exitBlock);
1176-
FlowEdge* const nextEdge = m_comp->fgGetPredForBlock(next, exitBlock);
1177-
weight_t const continueLikelihood = cappedLikelihood / exitBlock->bbWeight;
1178-
weight_t const breakLikelihood = 1 - continueLikelihood;
1179-
1180-
if (jumpEdge == exitEdge)
1181-
{
1182-
jumpEdge->setLikelihood(breakLikelihood);
1183-
nextEdge->setLikelihood(continueLikelihood);
1184-
}
1185-
else
1198+
// Does this block have enough weight that it can supply all the missing weight?
1199+
//
1200+
BasicBlock* const exitBlock = exitEdge->getSourceBlock();
1201+
weight_t const exitBlockFrequency = exitBlock->bbWeight;
1202+
weight_t const exitBlockWeight = exitBlockFrequency * cyclicProbability;
1203+
weight_t const currentExitWeight = exitEdge->getLikelihood() * exitBlockWeight;
1204+
1205+
// TODO: we might also want to exclude edges that are exiting from child loops here,
1206+
// or think harder about what might be appropriate in those cases. Seems like we ought
1207+
// to adjust an edge's likelihoods at most once.
1208+
//
1209+
// Currently we don't know which edges do this.
1210+
//
1211+
if ((exitBlock->bbJumpKind == BBJ_COND) && (exitBlockWeight > (missingExitWeight + currentExitWeight)))
11861212
{
1187-
assert(nextEdge == exitEdge);
1188-
jumpEdge->setLikelihood(continueLikelihood);
1189-
nextEdge->setLikelihood(breakLikelihood);
1213+
JITDUMP("Will adjust likelihood of the exit edge from loop exit block " FMT_BB
1214+
" to reflect capping; current likelihood is " FMT_WT "\n",
1215+
exitBlock->bbNum, exitEdge->getLikelihood());
1216+
1217+
BasicBlock* const jump = exitBlock->bbJumpDest;
1218+
BasicBlock* const next = exitBlock->bbNext;
1219+
FlowEdge* const jumpEdge = m_comp->fgGetPredForBlock(jump, exitBlock);
1220+
FlowEdge* const nextEdge = m_comp->fgGetPredForBlock(next, exitBlock);
1221+
weight_t const exitLikelihood = (missingExitWeight + currentExitWeight) / exitBlockWeight;
1222+
weight_t const continueLikelihood = 1.0 - exitLikelihood;
1223+
1224+
// We are making it more likely that the loop exits, so the new exit likelihood
1225+
// should be greater than the old.
1226+
//
1227+
assert(exitLikelihood > exitEdge->getLikelihood());
1228+
1229+
if (jumpEdge == exitEdge)
1230+
{
1231+
jumpEdge->setLikelihood(exitLikelihood);
1232+
nextEdge->setLikelihood(continueLikelihood);
1233+
}
1234+
else
1235+
{
1236+
assert(nextEdge == exitEdge);
1237+
jumpEdge->setLikelihood(continueLikelihood);
1238+
nextEdge->setLikelihood(exitLikelihood);
1239+
}
1240+
adjustedExit = true;
1241+
1242+
JITDUMP("New likelihood is " FMT_WT "\n", exitEdge->getLikelihood());
1243+
break;
11901244
}
1191-
adjustedExit = true;
1245+
}
11921246

1193-
JITDUMP("New likelihood is " FMT_WT "\n", exitEdge->getLikelihood());
1194-
break;
1247+
if (!adjustedExit)
1248+
{
1249+
// Possibly we could have fixed things up by adjusting more than one exit?
1250+
//
1251+
JITDUMP("Unable to find suitable exit to carry off capped flow\n");
11951252
}
11961253
}
1197-
1198-
if (!adjustedExit)
1254+
else
11991255
{
1200-
JITDUMP("Unable to find suitable exit to carry off capped flow\n");
1256+
JITDUMP("Exit weight comparable or above 1.0, leaving as is\n");
12011257
}
12021258
}
12031259
}

0 commit comments

Comments
 (0)