Skip to content

Commit

Permalink
Configure number of speculative target functions used in callProfiled…
Browse files Browse the repository at this point in the history
…Func

Summary: Generalizing the number of times we speculate the callee is the given profiled function at each call site.  This is mostly to simplify the code and to make it easier to optimize

Reviewed By: ricklavoie

Differential Revision: D65442714

fbshipit-source-id: 27ba7c13f725f165ad18b8b3f86e42b8c3707506
  • Loading branch information
Jae Wie authored and facebook-github-bot committed Nov 8, 2024
1 parent 6406f2c commit 0520b44
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 66 deletions.
3 changes: 2 additions & 1 deletion hphp/doc/configs.specification
Original file line number Diff line number Diff line change
Expand Up @@ -1622,7 +1622,8 @@ The format can be found in hphp/tools/configs/generate_configs.rs
Minimum probability of an arc to include it in HotCFG. [0,1]

- uint32_t Jit.PGORelaxPercent = 100, UNKNOWN
- double Jit.PGOCalledFuncCheckThreshold = 25, UNKNOWN
- uint32_t Jit.PGOCalledFuncCheckNumSpeculations = 8, UNKNOWN
- double Jit.PGOCalledFuncCheckThreshold = 10, UNKNOWN
- double Jit.PGOCalledFuncExitThreshold = 99.9, UNKNOWN
- bool Jit.PGODumpCallGraph = false, UNKNOWN
- bool Jit.PGOOptCodeCallGraph = true, UNKNOWN
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/vm/jit/call-target-profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct CallTargetProfile {

void init();

static const size_t kMaxEntries = 6;
static const size_t kMaxEntries = 8;

Entry m_entries[kMaxEntries];
uint32_t m_untracked{0};
Expand Down
110 changes: 46 additions & 64 deletions hphp/runtime/vm/jit/irgen-call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,50 @@ void handleCallReturn(IRGS& env, const Func* callee, SSATmp* retVal,

//////////////////////////////////////////////////////////////////////

template<class TKnown, class TUnknown>
void speculateTargetFunction(IRGS& env, SSATmp* callee,
TKnown callKnown, TUnknown callUnknown,
const jit::vector<CallTargetProfile::Choice> &choices,
size_t attempts, const size_t maxAttempts) {
auto indirectCall = [&] {
auto const unlikely = !choices.empty() && choices[0].probability * 100 >=
Cfg::Jit::PGOCalledFuncExitThreshold;
if (unlikely) {
hint(env, Block::Hint::Unlikely);
IRUnit::Hinter h(env.irb->unit(), Block::Hint::Unlikely);
callUnknown(true);
} else {
callUnknown(false);
}
};

if (attempts >= choices.size() || attempts == maxAttempts) {
return indirectCall();
}
const Func* profiledFunc = choices[attempts].func;
double probability = choices[attempts].probability;
// Don't emit the check if the probability of it succeeding is below the
// threshold.
if (probability * 100 < Cfg::Jit::PGOCalledFuncCheckThreshold) {
return indirectCall();
}

ifThenElse(
env,
[&] (Block* taken2) {
auto const equal = gen(env, EqFunc, callee,
cns(env, profiledFunc));
gen(env, JmpZero, taken2, equal);
},
[&] {
callKnown(profiledFunc);
},
[&] {
speculateTargetFunction(env, callee, callKnown, callUnknown, choices,
attempts + 1, maxAttempts);
}
);
}
/*
* In PGO mode, we use profiling to try to determine the most likely target
* function at each call site. In profiling translations, this function profiles
Expand All @@ -401,14 +445,6 @@ void callProfiledFunc(IRGS& env, SSATmp* callee,
if (!profile.optimizing()) return callUnknown(false);

auto const data = profile.data();
auto const choices = data.choose();
const Func* profiledFunc = nullptr;
double probability = 0;
if (choices.size() > 0) {
profiledFunc = choices[0].func;
probability = choices[0].probability;
}

// Dump annotations if requested.
if (Cfg::Eval::DumpCallTargets) {
auto const fnName = curFunc(env)->fullName()->data();
Expand All @@ -418,62 +454,8 @@ void callProfiledFunc(IRGS& env, SSATmp* callee,
);
}

// Don't emit the check if the probability of it succeeding is below the
// threshold.
if (profiledFunc == nullptr ||
probability * 100 < Cfg::Jit::PGOCalledFuncCheckThreshold) {
return callUnknown(false);
}

ifThenElse(
env,
[&] (Block* taken) {
auto const equal = gen(env, EqFunc, callee, cns(env, profiledFunc));
gen(env, JmpZero, taken, equal);
},
[&] {
callKnown(profiledFunc);
},
[&] {
auto indirectCall = [&] {
auto const unlikely = probability * 100 >=
Cfg::Jit::PGOCalledFuncExitThreshold;
if (unlikely) {
hint(env, Block::Hint::Unlikely);
IRUnit::Hinter h(env.irb->unit(), Block::Hint::Unlikely);
callUnknown(true);
} else {
callUnknown(false);
}
};
// If we have a 2nd hottest call target, consider adding a check + direct
// call to it too.
if (choices.size() > 1) {
profiledFunc = choices[1].func;
auto const remainingProb = 1 - choices[0].probability;
always_assert(remainingProb > 0);
probability = choices[1].probability / remainingProb;
if (probability * 100 >= Cfg::Jit::PGOCalledFuncCheckThreshold) {
ifThenElse(
env,
[&] (Block* taken2) {
auto const equal = gen(env, EqFunc, callee,
cns(env, profiledFunc));
gen(env, JmpZero, taken2, equal);
},
[&] {
callKnown(profiledFunc);
},
[&] {
indirectCall();
}
);
return;
}
}
indirectCall();
}
);
speculateTargetFunction(env, callee, callKnown, callUnknown, data.choose(),
0, Cfg::Jit::PGOCalledFuncCheckNumSpeculations);
}

//////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit 0520b44

Please sign in to comment.