Reland "[wasm] Perform NativeModule tier down in parallel."

This is a reland of faccc95b77

Since 1c9bb77de5, async jobs use existing
entry in native module cache and skip recompilation so we need to fix
the test.

Original change's description:
> Reland "[wasm] Perform NativeModule tier down in parallel."
>
> This is a reland of 3352fcc900
>
> Disable stress-opt for test and check recompilation before clearing
> callbacks.
>
> Original change's description:
> > [wasm] Perform NativeModule tier down in parallel.
> >
> > Reuse logic in {CompileNativeModule} function in module-compiler.cc:
> > initialize parallel compile jobs, then wait for them to finish while
> > taking part in this compilation.
> >
> > Bug: v8:9654
> > Change-Id: I9974d9f8b516e9faec716a592c7c0ee9c7077d8e
> > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1977041
> > Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
> > Reviewed-by: Clemens Backes <clemensb@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#65763}
>
> Bug: v8:9654
> Change-Id: I8e8830f05e189596207365b7332a2cc25e493e47
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2002945
> Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
> Reviewed-by: Clemens Backes <clemensb@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#65901}

Bug: v8:9654
Change-Id: Ia63b86d4275088d93202046bc9823e6202b7991a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2012986
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#65929}
This commit is contained in:
Z Nguyen-Huu 2020-01-22 10:01:34 -08:00 committed by Commit Bot
parent c93be468ff
commit 30f94ea4c9
7 changed files with 162 additions and 25 deletions

View File

@ -92,7 +92,8 @@ class WireBytesStorage {
enum class CompilationEvent : uint8_t {
kFinishedBaselineCompilation,
kFinishedTopTierCompilation,
kFailedCompilation
kFailedCompilation,
kFinishedRecompilation
};
// The implementation of {CompilationState} lives in module-compiler.cc.
@ -117,6 +118,7 @@ class CompilationState {
bool failed() const;
V8_EXPORT_PRIVATE bool baseline_compilation_finished() const;
V8_EXPORT_PRIVATE bool top_tier_compilation_finished() const;
V8_EXPORT_PRIVATE bool recompilation_finished() const;
// Override {operator delete} to avoid implicit instantiation of {operator
// delete} with {size_t} argument. The {size_t} argument would be incorrect.

View File

@ -381,6 +381,9 @@ class CompilationStateImpl {
// is invoked which triggers background compilation.
void InitializeCompilationProgress(bool lazy_module, int num_wrappers);
// Initialize compilation progress for recompilation of the whole module.
void InitializeRecompilationProgress(ExecutionTier tier);
// Add the callback function to be called on compilation events. Needs to be
// set before {AddCompilationUnits} is run to ensure that it receives all
// events. The callback object must support being deleted from any thread.
@ -404,7 +407,8 @@ class CompilationStateImpl {
void OnFinishedUnits(Vector<WasmCode*>);
void OnFinishedJSToWasmWrapperUnits(int num);
void TriggerCallbacks(bool completes_baseline_compilation,
bool completes_top_tier_compilation);
bool completes_top_tier_compilation,
bool completes_recompilation = false);
void OnBackgroundTaskStopped(int task_id, const WasmFeatures& detected);
void UpdateDetectedFeatures(const WasmFeatures& detected);
@ -427,6 +431,11 @@ class CompilationStateImpl {
return outstanding_top_tier_functions_ == 0;
}
bool recompilation_finished() const {
base::MutexGuard guard(&callbacks_mutex_);
return outstanding_recompilation_functions_ == 0;
}
CompileMode compile_mode() const { return compile_mode_; }
Counters* counters() const { return async_counters_.get(); }
WasmFeatures* detected_features() { return &detected_features_; }
@ -536,6 +545,7 @@ class CompilationStateImpl {
int outstanding_top_tier_functions_ = 0;
std::vector<uint8_t> compilation_progress_;
int outstanding_recompilation_functions_ = 0;
// End of fields protected by {callbacks_mutex_}.
//////////////////////////////////////////////////////////////////////////////
@ -597,6 +607,10 @@ bool CompilationState::top_tier_compilation_finished() const {
return Impl(this)->top_tier_compilation_finished();
}
bool CompilationState::recompilation_finished() const {
return Impl(this)->recompilation_finished();
}
// static
std::unique_ptr<CompilationState> CompilationState::New(
const std::shared_ptr<NativeModule>& native_module,
@ -747,6 +761,10 @@ class CompilationUnitBuilder {
tiering_units_.emplace_back(func_index, tiers.top_tier);
}
void AddBaselineUnit(int func_index) {
baseline_units_.emplace_back(func_index, ExecutionTier::kLiftoff);
}
bool Commit() {
if (baseline_units_.empty() && tiering_units_.empty() &&
js_to_wasm_wrapper_units_.empty()) {
@ -1195,6 +1213,19 @@ void InitializeCompilationUnits(Isolate* isolate, NativeModule* native_module) {
builder.Commit();
}
void AddBaselineCompilationUnits(NativeModule* native_module) {
CompilationUnitBuilder builder(native_module);
auto* module = native_module->module();
uint32_t start = module->num_imported_functions;
uint32_t end = start + module->num_declared_functions;
for (uint32_t func_index = start; func_index < end; func_index++) {
builder.AddBaselineUnit(func_index);
}
builder.Commit();
}
bool MayCompriseLazyFunctions(const WasmModule* module,
const WasmFeatures& enabled_features,
bool lazy_module) {
@ -1388,6 +1419,40 @@ std::shared_ptr<NativeModule> CompileToNativeModule(
return native_module;
}
void RecompileNativeModule(Isolate* isolate, NativeModule* native_module,
ExecutionTier tier) {
// 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());
DCHECK_EQ(tier, ExecutionTier::kLiftoff);
// The callback captures a shared ptr to the semaphore.
compilation_state->AddCallback(
[recompilation_finished_semaphore](CompilationEvent event) {
if (event == CompilationEvent::kFinishedRecompilation) {
recompilation_finished_semaphore->Signal();
}
});
// Initialize the compilation units and kick off background compile tasks.
compilation_state->InitializeRecompilationProgress(tier);
AddBaselineCompilationUnits(native_module);
// The main thread contributes to the compilation, except if we need
// deterministic compilation; in that case, the single background task will
// execute all compilation.
if (!NeedsDeterministicCompile()) {
while (ExecuteCompilationUnits(
compilation_state->background_compile_token(), isolate->counters(),
kMainThreadTaskId, kBaselineOnly)) {
// Continue executing compilation units.
}
}
// Now wait until baseline recompilation finished.
recompilation_finished_semaphore->Wait();
DCHECK(!compilation_state->failed());
}
AsyncCompileJob::AsyncCompileJob(
Isolate* isolate, const WasmFeatures& enabled,
std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
@ -1630,7 +1695,7 @@ class AsyncCompileJob::CompilationStateCallback {
// At this point, the job will already be gone, thus do not access it
// here.
break;
case CompilationEvent::kFailedCompilation: {
case CompilationEvent::kFailedCompilation:
DCHECK(!last_event_.has_value());
if (job_->DecrementAndCheckFinisherCount()) {
// TODO(v8:6847): Also share streaming compilation result.
@ -1641,7 +1706,8 @@ class AsyncCompileJob::CompilationStateCallback {
job_->DoSync<CompileFailed>();
}
break;
}
case CompilationEvent::kFinishedRecompilation:
break;
default:
UNREACHABLE();
}
@ -1960,6 +2026,7 @@ class SampleTopTierCodeSizeCallback {
void operator()(CompilationEvent event) {
// This callback is registered after baseline compilation finished, so the
// only possible event to follow is {kFinishedTopTierCompilation}.
if (event == CompilationEvent::kFinishedRecompilation) return;
DCHECK_EQ(CompilationEvent::kFinishedTopTierCompilation, event);
if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
native_module->engine()->SampleTopTierCodeSizeInAllIsolates(
@ -2352,6 +2419,37 @@ void CompilationStateImpl::InitializeCompilationProgress(bool lazy_module,
}
}
void CompilationStateImpl::InitializeRecompilationProgress(ExecutionTier tier) {
DCHECK(!failed());
auto* module = native_module_->module();
base::MutexGuard guard(&callbacks_mutex_);
// Ensure that we don't trigger recompilation if another recompilation is
// already happening.
DCHECK_EQ(0, outstanding_recompilation_functions_);
int start = module->num_imported_functions;
int end = start + module->num_declared_functions;
for (int function_index = start; function_index < end; function_index++) {
int slot_index = function_index - start;
DCHECK_LT(slot_index, compilation_progress_.size());
ExecutionTier reached_tier =
ReachedTierField::decode(compilation_progress_[slot_index]);
if (reached_tier != tier) {
outstanding_recompilation_functions_++;
}
}
DCHECK_LE(0, outstanding_recompilation_functions_);
DCHECK_LE(outstanding_recompilation_functions_,
module->num_declared_functions);
// Trigger callbacks if module needs no recompilation.
if (outstanding_recompilation_functions_ == 0) {
for (auto& callback : callbacks_) {
callback(CompilationEvent::kFinishedRecompilation);
}
}
}
void CompilationStateImpl::AddCallback(CompilationState::callback_t callback) {
base::MutexGuard callbacks_guard(&callbacks_mutex_);
callbacks_.emplace_back(std::move(callback));
@ -2423,7 +2521,8 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
// This is especially important for lazy modules that were deserialized.
// Compilation progress was not set up in these cases.
if (outstanding_baseline_units_ == 0 &&
outstanding_top_tier_functions_ == 0) {
outstanding_top_tier_functions_ == 0 &&
outstanding_recompilation_functions_ == 0) {
return;
}
@ -2439,6 +2538,7 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
bool completes_baseline_compilation = false;
bool completes_top_tier_compilation = false;
bool completes_recompilation = false;
for (WasmCode* code : code_vector) {
DCHECK_NOT_NULL(code);
@ -2486,17 +2586,34 @@ void CompilationStateImpl::OnFinishedUnits(Vector<WasmCode*> code_vector) {
}
}
// Update function's compilation progress.
if (code->tier() > reached_tier) {
compilation_progress_[slot_index] = ReachedTierField::update(
compilation_progress_[slot_index], code->tier());
// If there is recompilation in progress, we would only count the
// functions which are not Liftoff already, and would only decrement the
// counter once a function reaches Liftoff.
if (outstanding_recompilation_functions_ > 0) {
// TODO(duongn): extend this logic for tier up.
if (code->tier() == ExecutionTier::kLiftoff &&
reached_tier != ExecutionTier::kLiftoff) {
outstanding_recompilation_functions_--;
// Update function's compilation progress.
compilation_progress_[slot_index] = ReachedTierField::update(
compilation_progress_[slot_index], code->tier());
if (outstanding_recompilation_functions_ == 0) {
completes_recompilation = true;
}
}
} else {
// Update function's compilation progress.
if (code->tier() > reached_tier) {
compilation_progress_[slot_index] = ReachedTierField::update(
compilation_progress_[slot_index], code->tier());
}
}
DCHECK_LE(0, outstanding_baseline_units_);
}
}
TriggerCallbacks(completes_baseline_compilation,
completes_top_tier_compilation);
completes_top_tier_compilation, completes_recompilation);
}
void CompilationStateImpl::OnFinishedJSToWasmWrapperUnits(int num) {
@ -2507,8 +2624,14 @@ void CompilationStateImpl::OnFinishedJSToWasmWrapperUnits(int num) {
TriggerCallbacks(completes_baseline_compilation, false);
}
void CompilationStateImpl::TriggerCallbacks(
bool completes_baseline_compilation, bool completes_top_tier_compilation) {
void CompilationStateImpl::TriggerCallbacks(bool completes_baseline_compilation,
bool completes_top_tier_compilation,
bool completes_recompilation) {
if (completes_recompilation) {
for (auto& callback : callbacks_) {
callback(CompilationEvent::kFinishedRecompilation);
}
}
if (completes_baseline_compilation) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), "BaselineFinished");
for (auto& callback : callbacks_) {
@ -2523,6 +2646,10 @@ void CompilationStateImpl::TriggerCallbacks(
for (auto& callback : callbacks_) {
callback(CompilationEvent::kFinishedTopTierCompilation);
}
}
if (outstanding_baseline_units_ == 0 &&
outstanding_top_tier_functions_ == 0 &&
outstanding_recompilation_functions_ == 0) {
// Clear the callbacks because no more events will be delivered.
callbacks_.clear();
}
@ -2589,7 +2716,7 @@ void CompilationStateImpl::RestartBackgroundTasks() {
}
}
if (baseline_compilation_finished()) {
if (baseline_compilation_finished() && recompilation_finished()) {
for (auto& task : new_tasks) {
V8::GetCurrentPlatform()->CallLowPriorityTaskOnWorkerThread(
std::move(task));

View File

@ -44,6 +44,9 @@ std::shared_ptr<NativeModule> CompileToNativeModule(
std::shared_ptr<const WasmModule> module, const ModuleWireBytes& wire_bytes,
Handle<FixedArray>* export_wrappers_out);
void RecompileNativeModule(Isolate* isolate, NativeModule* native_module,
ExecutionTier tier);
V8_EXPORT_PRIVATE
void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
Handle<FixedArray>* export_wrappers_out);

View File

@ -1818,13 +1818,8 @@ void NativeModule::TierDown(Isolate* isolate) {
tier_down_ = true;
}
// Tier down all functions.
// TODO(duongn): parallelize this eventually.
for (uint32_t index = module_->num_imported_functions;
index < num_functions(); index++) {
isolate->wasm_engine()->CompileFunction(isolate, this, index,
ExecutionTier::kLiftoff);
DCHECK(!compilation_state()->failed());
}
isolate->wasm_engine()->RecompileAllFunctions(isolate, this,
ExecutionTier::kLiftoff);
}
void NativeModule::TierUp(Isolate* isolate) {

View File

@ -488,6 +488,12 @@ void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
&native_module->module()->functions[function_index], tier);
}
void WasmEngine::RecompileAllFunctions(Isolate* isolate,
NativeModule* native_module,
ExecutionTier tier) {
RecompileNativeModule(isolate, native_module, tier);
}
std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
Handle<WasmModuleObject> module_object) {
return module_object->shared_native_module();

View File

@ -148,6 +148,10 @@ class V8_EXPORT_PRIVATE WasmEngine {
void CompileFunction(Isolate* isolate, NativeModule* native_module,
uint32_t function_index, ExecutionTier tier);
// Recompiles all functions at a specific compilation tier.
void RecompileAllFunctions(Isolate* isolate, NativeModule* native_module,
ExecutionTier tier);
// Exports the sharable parts of the given module object so that they can be
// transferred to a different Context/Isolate using the same engine.
std::shared_ptr<NativeModule> ExportNativeModule(

View File

@ -2,17 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --wasm-tier-up
// Flags: --allow-natives-syntax --liftoff --wasm-tier-up --no-stress-opt
load('test/mjsunit/wasm/wasm-module-builder.js');
const num_functions = 2;
function create_builder() {
function create_builder(delta = 0) {
const builder = new WasmModuleBuilder();
for (let i = 0; i < num_functions; ++i) {
builder.addFunction('f' + i, kSig_i_v)
.addBody(wasmI32Const(i))
.addBody(wasmI32Const(i + delta))
.exportFunc();
}
return builder;
@ -41,10 +41,10 @@ function check(instance) {
check(instance);
})();
// Use slightly different module for this test to avoid sharing native module.
async function testTierDownToLiftoffAsync() {
print(arguments.callee.name);
const instance = await create_builder().asyncInstantiate();
const instance = await create_builder(num_functions).asyncInstantiate();
check(instance);
}