Bug 1965195 - wasm: make lazy tiering more aggressive for small modules. r=rhunt.

Consideration of lazy tiering for the limit cases "module size tends to
infinity" and "module size tends to zero" shows that lazy tiering is relatively
at a disadvantage to eager tiering as modules get smaller.  We see the results
in the regression detailed in bug 1964966.

This patch changes LazyTieringHeuristics::estimateIonCompilationCost to take
into account module size, using that to reduce the resulting tier-up threshold
for small modules.

Differential Revision: https://phabricator.services.mozilla.com/D248597
This commit is contained in:
Julian Seward
2025-05-09 17:30:31 +00:00
committed by jseward@mozilla.com
parent 1e4f169fa4
commit 00242a9d28
3 changed files with 24 additions and 5 deletions

View File

@@ -38,6 +38,7 @@ namespace wasm {
class LazyTieringHeuristics {
static constexpr uint32_t MIN_LEVEL = 1;
static constexpr uint32_t MAX_LEVEL = 9;
static constexpr uint32_t SMALL_MODULE_THRESH = 150000;
// A scaling table for levels 2 .. 8. Levels 1 and 9 are special-cased. In
// this table, each value differs from its neighbour by a factor of 3, giving
@@ -64,8 +65,20 @@ class LazyTieringHeuristics {
// goes negative it requests tier-up. See "[SMDOC] WebAssembly baseline
// compiler -- Lazy Tier-Up mechanism" in WasmBaselineCompile.cpp.
static int32_t estimateIonCompilationCost(uint32_t bodyLength) {
static int32_t estimateIonCompilationCost(uint32_t bodyLength,
size_t codeSectionSize) {
uint32_t level = rawLevel();
// Increase the aggressiveness of tiering for small modules, since they
// don't generate much optimised-tier compilation work, so we might as well
// try to get them into optimized code sooner. But don't overdo it, since
// we don't want to lose indirect-target resolution as a result. See bug
// 1965195.
MOZ_ASSERT(codeSectionSize > 0);
if (codeSectionSize <= SMALL_MODULE_THRESH && level < MAX_LEVEL) {
level += 1;
}
if (MOZ_LIKELY(MIN_LEVEL < level && level < MAX_LEVEL)) {
// The estimated cost, in X86_64 insns, for Ion compilation:
// 30k up-front cost + 4k per bytecode byte.

View File

@@ -2343,10 +2343,12 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports,
// Initialize the hotness counters, if relevant.
if (code().mode() == CompileMode::LazyTiering) {
// Computing the initial hotness counters requires the code section size.
const size_t codeSectionSize = codeMeta().codeSectionSize();
for (uint32_t funcIndex = codeMeta().numFuncImports;
funcIndex < codeMeta().numFuncs(); funcIndex++) {
funcDefInstanceData(funcIndex)->hotnessCounter =
computeInitialHotnessCounter(funcIndex);
computeInitialHotnessCounter(funcIndex, codeSectionSize);
}
}
@@ -2736,10 +2738,13 @@ void Instance::resetTemporaryStackLimit(JSContext* cx) {
onSuspendableStack_ = false;
}
int32_t Instance::computeInitialHotnessCounter(uint32_t funcIndex) {
int32_t Instance::computeInitialHotnessCounter(uint32_t funcIndex,
size_t codeSectionSize) {
MOZ_ASSERT(code().mode() == CompileMode::LazyTiering);
MOZ_ASSERT(codeSectionSize > 0);
uint32_t bodyLength = codeTailMeta().funcDefRange(funcIndex).size;
return LazyTieringHeuristics::estimateIonCompilationCost(bodyLength);
return LazyTieringHeuristics::estimateIonCompilationCost(bodyLength,
codeSectionSize);
}
void Instance::resetHotnessCounter(uint32_t funcIndex) {

View File

@@ -400,7 +400,8 @@ class alignas(16) Instance {
void setTemporaryStackLimit(JS::NativeStackLimit limit);
void resetTemporaryStackLimit(JSContext* cx);
int32_t computeInitialHotnessCounter(uint32_t funcIndex);
int32_t computeInitialHotnessCounter(uint32_t funcIndex,
size_t codeSectionSize);
void resetHotnessCounter(uint32_t funcIndex);
int32_t readHotnessCounter(uint32_t funcIndex) const;
void submitCallRefHints(uint32_t funcIndex);