[wasm] Avoid redundant recompilation
In certain situations (especially if the profiler is disabled) it can happen that multiple isolates trigger tier-up at the same time. Each recompilation would then identify the set of functions to recompile and add compilation units for them. This creates lots of redundant compilation, which takes time and can exceed the total wasm code space limit (code would later get garbage-collected, but there is a high peak). This CL fixes that by always finishing a running recompilation before starting a new one. Since typically concurrent recompilations all recompile for the same tier, this would avoid do most compilation only once, and later recompilations can skip most functions. The only redundant compilation that can happen is via compilation units that are still executing while the next recompilation starts, but those should not create too much redundant code. R=thibaudm@chromium.org Bug: chromium:1114093 Change-Id: If8fa7d953f3ce77a9d146458bf5e17bb9bf97219 Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel_ng Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_isolates_rel_ng Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2379851 Commit-Queue: Clemens Backes <clemensb@chromium.org> Reviewed-by: Thibaud Michaud <thibaudm@chromium.org> Cr-Commit-Position: refs/heads/master@{#69588}
This commit is contained in:
parent
a52569e625
commit
945fabf52f
@ -1717,6 +1717,7 @@ void RecompileNativeModule(NativeModule* native_module,
|
||||
// Install a callback to notify us once background recompilation finished.
|
||||
auto recompilation_finished_semaphore = std::make_shared<base::Semaphore>(0);
|
||||
auto* compilation_state = Impl(native_module->compilation_state());
|
||||
|
||||
// The callback captures a shared ptr to the semaphore.
|
||||
// Initialize the compilation units and kick off background compile tasks.
|
||||
compilation_state->InitializeRecompilation(
|
||||
@ -2843,8 +2844,19 @@ void CompilationStateImpl::InitializeRecompilation(
|
||||
CompilationState::callback_t recompilation_finished_callback) {
|
||||
DCHECK(!failed());
|
||||
|
||||
// Generate necessary compilation units on the fly.
|
||||
CompilationUnitBuilder builder(native_module_);
|
||||
// Hold the mutex as long as possible, to synchronize between multiple
|
||||
// recompilations that are triggered at the same time (e.g. when the profiler
|
||||
// is disabled).
|
||||
base::Optional<base::MutexGuard> guard(&callbacks_mutex_);
|
||||
|
||||
while (NumOutstandingCompilations() > 0) {
|
||||
// Take part in compilation, after releasing the mutex.
|
||||
guard.reset();
|
||||
constexpr JobDelegate* kNoDelegate = nullptr;
|
||||
ExecuteCompilationUnits(background_compile_token_, async_counters_.get(),
|
||||
kNoDelegate, kBaselineOrTopTier);
|
||||
guard.emplace(&callbacks_mutex_);
|
||||
}
|
||||
|
||||
// Information about compilation progress is shared between this class and the
|
||||
// NativeModule. Before updating information here, consult the NativeModule to
|
||||
@ -2856,48 +2868,44 @@ void CompilationStateImpl::InitializeRecompilation(
|
||||
std::vector<int> recompile_function_indexes =
|
||||
native_module_->FindFunctionsToRecompile(new_tiering_state);
|
||||
|
||||
{
|
||||
base::MutexGuard guard(&callbacks_mutex_);
|
||||
callbacks_.emplace_back(std::move(recompilation_finished_callback));
|
||||
tiering_state_ = new_tiering_state;
|
||||
|
||||
callbacks_.emplace_back(std::move(recompilation_finished_callback));
|
||||
tiering_state_ = new_tiering_state;
|
||||
|
||||
// If compilation progress is not initialized yet, then compilation didn't
|
||||
// start yet, and new code will be kept tiered-down from the start. For
|
||||
// streaming compilation, there is a special path to tier down later, when
|
||||
// the module is complete. In any case, we don't need to recompile here.
|
||||
if (compilation_progress_.size() > 0) {
|
||||
const WasmModule* module = native_module_->module();
|
||||
DCHECK_EQ(module->num_declared_functions, compilation_progress_.size());
|
||||
DCHECK_GE(module->num_declared_functions,
|
||||
recompile_function_indexes.size());
|
||||
outstanding_recompilation_functions_ =
|
||||
static_cast<int>(recompile_function_indexes.size());
|
||||
// Restart recompilation if another recompilation is already happening.
|
||||
for (auto& progress : compilation_progress_) {
|
||||
progress = MissingRecompilationField::update(progress, false);
|
||||
}
|
||||
auto new_tier = new_tiering_state == kTieredDown
|
||||
? ExecutionTier::kLiftoff
|
||||
: ExecutionTier::kTurbofan;
|
||||
int imported = module->num_imported_functions;
|
||||
for (int function_index : recompile_function_indexes) {
|
||||
DCHECK_LE(imported, function_index);
|
||||
int slot_index = function_index - imported;
|
||||
auto& progress = compilation_progress_[slot_index];
|
||||
progress = MissingRecompilationField::update(progress, true);
|
||||
builder.AddRecompilationUnit(function_index, new_tier);
|
||||
}
|
||||
// If compilation progress is not initialized yet, then compilation didn't
|
||||
// start yet, and new code will be kept tiered-down from the start. For
|
||||
// streaming compilation, there is a special path to tier down later, when
|
||||
// the module is complete. In any case, we don't need to recompile here.
|
||||
if (compilation_progress_.size() > 0) {
|
||||
const WasmModule* module = native_module_->module();
|
||||
DCHECK_EQ(module->num_declared_functions, compilation_progress_.size());
|
||||
DCHECK_GE(module->num_declared_functions,
|
||||
recompile_function_indexes.size());
|
||||
outstanding_recompilation_functions_ =
|
||||
static_cast<int>(recompile_function_indexes.size());
|
||||
// Restart recompilation if another recompilation is already happening.
|
||||
for (auto& progress : compilation_progress_) {
|
||||
progress = MissingRecompilationField::update(progress, false);
|
||||
}
|
||||
|
||||
// Trigger callback if module needs no recompilation.
|
||||
if (outstanding_recompilation_functions_ == 0) {
|
||||
TriggerCallbacks(base::EnumSet<CompilationEvent>(
|
||||
{CompilationEvent::kFinishedRecompilation}));
|
||||
auto new_tier = new_tiering_state == kTieredDown ? ExecutionTier::kLiftoff
|
||||
: ExecutionTier::kTurbofan;
|
||||
int imported = module->num_imported_functions;
|
||||
// Generate necessary compilation units on the fly.
|
||||
CompilationUnitBuilder builder(native_module_);
|
||||
for (int function_index : recompile_function_indexes) {
|
||||
DCHECK_LE(imported, function_index);
|
||||
int slot_index = function_index - imported;
|
||||
auto& progress = compilation_progress_[slot_index];
|
||||
progress = MissingRecompilationField::update(progress, true);
|
||||
builder.AddRecompilationUnit(function_index, new_tier);
|
||||
}
|
||||
builder.Commit();
|
||||
}
|
||||
|
||||
builder.Commit();
|
||||
// Trigger callback if module needs no recompilation.
|
||||
if (outstanding_recompilation_functions_ == 0) {
|
||||
TriggerCallbacks(base::EnumSet<CompilationEvent>(
|
||||
{CompilationEvent::kFinishedRecompilation}));
|
||||
}
|
||||
}
|
||||
|
||||
void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) {
|
||||
|
Loading…
Reference in New Issue
Block a user