Commit f3682984 authored by Clemens Backes's avatar Clemens Backes Committed by Commit Bot

[wasm] Initialize compile job early

Since the compile job can always be reused after creation (even if it
runs out of work), we do not need the logic to (re-)initialize it. In
fact, it will always only be initialized once already.
This allows us to initialize it once during construction of the
compilation state (or right after the initialization), and then access
it without locks later.

In addition, this CL
1) renames "current_compile_job_" to "compile_job_", since there will
   always only be one now;
2) removes the {ScheduleCompileJobForNewUnits} method, and just does a
   {compile_job_->NotifyConcurrencyIncrease()} instead;
3) removes the {has_priority_} field and just directly does a
   {compile_job_->UpdatePriority} call.

The streaming test platform needed to be fixed to avoid calling {Join}
on the job handle, which would invalidate the handle afterwards.
Instead, we just run all tasks as long as there are any.

R=thibaudm@chromium.org
CC=etiennep@chromium.org

Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel_ng
Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_isolates_rel_ng
Change-Id: I7094231e86d5f54cfca5e971b96fd81e994c874a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2584946
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarThibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71757}
parent d7de8fa4
...@@ -24,6 +24,7 @@ namespace wasm { ...@@ -24,6 +24,7 @@ namespace wasm {
class NativeModule; class NativeModule;
class WasmCode; class WasmCode;
class WasmEngine;
class WasmError; class WasmError;
enum RuntimeExceptionSupport : bool { enum RuntimeExceptionSupport : bool {
...@@ -116,6 +117,8 @@ class V8_EXPORT_PRIVATE CompilationState { ...@@ -116,6 +117,8 @@ class V8_EXPORT_PRIVATE CompilationState {
~CompilationState(); ~CompilationState();
void InitCompileJob(WasmEngine*);
void CancelCompilation(); void CancelCompilation();
void SetError(); void SetError();
......
...@@ -531,13 +531,14 @@ class CompilationStateImpl { ...@@ -531,13 +531,14 @@ class CompilationStateImpl {
CompilationStateImpl(const std::shared_ptr<NativeModule>& native_module, CompilationStateImpl(const std::shared_ptr<NativeModule>& native_module,
std::shared_ptr<Counters> async_counters); std::shared_ptr<Counters> async_counters);
~CompilationStateImpl() { ~CompilationStateImpl() {
// It is safe to access current_compile_job_ without a lock since this is DCHECK(compile_job_->IsValid());
// the last reference. compile_job_->CancelAndDetach();
if (current_compile_job_ && current_compile_job_->IsValid()) {
current_compile_job_->CancelAndDetach();
}
} }
// Call right after the constructor, after the {compilation_state_} field in
// the {NativeModule} has been initialized.
void InitCompileJob(WasmEngine*);
// Cancel all background compilation, without waiting for compile tasks to // Cancel all background compilation, without waiting for compile tasks to
// finish. // finish.
void CancelCompilation(); void CancelCompilation();
...@@ -592,9 +593,6 @@ class CompilationStateImpl { ...@@ -592,9 +593,6 @@ class CompilationStateImpl {
void PublishDetectedFeatures(Isolate*); void PublishDetectedFeatures(Isolate*);
void SchedulePublishCompilationResults( void SchedulePublishCompilationResults(
std::vector<std::unique_ptr<WasmCode>> unpublished_code); std::vector<std::unique_ptr<WasmCode>> unpublished_code);
// Ensure that a compilation job is running, and increase its concurrency if
// needed.
void ScheduleCompileJobForNewUnits();
size_t NumOutstandingCompilations() const; size_t NumOutstandingCompilations() const;
...@@ -603,8 +601,8 @@ class CompilationStateImpl { ...@@ -603,8 +601,8 @@ class CompilationStateImpl {
void WaitForCompilationEvent(CompilationEvent event); void WaitForCompilationEvent(CompilationEvent event);
void SetHighPriority() { void SetHighPriority() {
base::MutexGuard guard(&mutex_); // TODO(wasm): Keep a lower priority for TurboFan-only jobs.
has_priority_ = true; compile_job_->UpdatePriority(TaskPriority::kUserBlocking);
} }
bool failed() const { bool failed() const {
...@@ -668,8 +666,9 @@ class CompilationStateImpl { ...@@ -668,8 +666,9 @@ class CompilationStateImpl {
CompilationUnitQueues compilation_unit_queues_; CompilationUnitQueues compilation_unit_queues_;
// Index of the next wrapper to compile in {js_to_wasm_wrapper_units_}. // Number of wrappers to be compiled. Initialized once, counted down in
std::atomic<int> js_to_wasm_wrapper_id_{0}; // {GetNextJSToWasmWrapperCompilationUnit}.
std::atomic<size_t> outstanding_js_to_wasm_wrappers_{0};
// Wrapper compilation units are stored in shared_ptrs so that they are kept // Wrapper compilation units are stored in shared_ptrs so that they are kept
// alive by the tasks even if the NativeModule dies. // alive by the tasks even if the NativeModule dies.
std::vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>> std::vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>>
...@@ -679,13 +678,13 @@ class CompilationStateImpl { ...@@ -679,13 +678,13 @@ class CompilationStateImpl {
// being accessed concurrently. // being accessed concurrently.
mutable base::Mutex mutex_; mutable base::Mutex mutex_;
// The compile job handle, initialized right after construction of
// {CompilationStateImpl}.
std::unique_ptr<JobHandle> compile_job_;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Protected by {mutex_}: // Protected by {mutex_}:
bool has_priority_ = false;
std::unique_ptr<JobHandle> current_compile_job_;
// Features detected to be used in this module. Features can be detected // Features detected to be used in this module. Features can be detected
// as a module is being compiled. // as a module is being compiled.
WasmFeatures detected_features_ = WasmFeatures::None(); WasmFeatures detected_features_ = WasmFeatures::None();
...@@ -776,6 +775,10 @@ constexpr uint32_t CompilationEnv::kMaxMemoryPagesAtRuntime; ...@@ -776,6 +775,10 @@ constexpr uint32_t CompilationEnv::kMaxMemoryPagesAtRuntime;
CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); } CompilationState::~CompilationState() { Impl(this)->~CompilationStateImpl(); }
void CompilationState::InitCompileJob(WasmEngine* engine) {
Impl(this)->InitCompileJob(engine);
}
void CompilationState::CancelCompilation() { Impl(this)->CancelCompilation(); } void CompilationState::CancelCompilation() { Impl(this)->CancelCompilation(); }
void CompilationState::SetError() { Impl(this)->SetError(); } void CompilationState::SetError() { Impl(this)->SetError(); }
...@@ -2750,6 +2753,14 @@ CompilationStateImpl::CompilationStateImpl( ...@@ -2750,6 +2753,14 @@ CompilationStateImpl::CompilationStateImpl(
async_counters_(std::move(async_counters)), async_counters_(std::move(async_counters)),
compilation_unit_queues_(native_module->num_functions()) {} compilation_unit_queues_(native_module->num_functions()) {}
void CompilationStateImpl::InitCompileJob(WasmEngine* engine) {
DCHECK_NULL(compile_job_);
compile_job_ = V8::GetCurrentPlatform()->PostJob(
TaskPriority::kUserVisible,
std::make_unique<BackgroundCompileJob>(native_module_weak_, engine,
async_counters_));
}
void CompilationStateImpl::CancelCompilation() { void CompilationStateImpl::CancelCompilation() {
// std::memory_order_relaxed is sufficient because no other state is // std::memory_order_relaxed is sufficient because no other state is
// synchronized with |compile_cancelled_|. // synchronized with |compile_cancelled_|.
...@@ -2951,19 +2962,22 @@ void CompilationStateImpl::AddCompilationUnits( ...@@ -2951,19 +2962,22 @@ void CompilationStateImpl::AddCompilationUnits(
Vector<WasmCompilationUnit> top_tier_units, Vector<WasmCompilationUnit> top_tier_units,
Vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>> Vector<std::shared_ptr<JSToWasmWrapperCompilationUnit>>
js_to_wasm_wrapper_units) { js_to_wasm_wrapper_units) {
if (!baseline_units.empty() || !top_tier_units.empty()) {
compilation_unit_queues_.AddUnits(baseline_units, top_tier_units,
native_module_->module());
}
if (!js_to_wasm_wrapper_units.empty()) { if (!js_to_wasm_wrapper_units.empty()) {
// |js_to_wasm_wrapper_units_| can only be modified before background // |js_to_wasm_wrapper_units_| will only be initialized once.
// compilation started. DCHECK_EQ(0, outstanding_js_to_wasm_wrappers_.load());
DCHECK(!current_compile_job_);
js_to_wasm_wrapper_units_.insert(js_to_wasm_wrapper_units_.end(), js_to_wasm_wrapper_units_.insert(js_to_wasm_wrapper_units_.end(),
js_to_wasm_wrapper_units.begin(), js_to_wasm_wrapper_units.begin(),
js_to_wasm_wrapper_units.end()); js_to_wasm_wrapper_units.end());
// Use release semantics such that updates to {js_to_wasm_wrapper_units_}
// are available to other threads doing an acquire load.
outstanding_js_to_wasm_wrappers_.store(js_to_wasm_wrapper_units.size(),
std::memory_order_release);
} }
ScheduleCompileJobForNewUnits(); if (!baseline_units.empty() || !top_tier_units.empty()) {
compilation_unit_queues_.AddUnits(baseline_units, top_tier_units,
native_module_->module());
}
compile_job_->NotifyConcurrencyIncrease();
} }
void CompilationStateImpl::AddTopTierCompilationUnit(WasmCompilationUnit unit) { void CompilationStateImpl::AddTopTierCompilationUnit(WasmCompilationUnit unit) {
...@@ -2973,17 +2987,23 @@ void CompilationStateImpl::AddTopTierCompilationUnit(WasmCompilationUnit unit) { ...@@ -2973,17 +2987,23 @@ void CompilationStateImpl::AddTopTierCompilationUnit(WasmCompilationUnit unit) {
void CompilationStateImpl::AddTopTierPriorityCompilationUnit( void CompilationStateImpl::AddTopTierPriorityCompilationUnit(
WasmCompilationUnit unit, size_t priority) { WasmCompilationUnit unit, size_t priority) {
compilation_unit_queues_.AddTopTierPriorityUnit(unit, priority); compilation_unit_queues_.AddTopTierPriorityUnit(unit, priority);
ScheduleCompileJobForNewUnits(); compile_job_->NotifyConcurrencyIncrease();
} }
std::shared_ptr<JSToWasmWrapperCompilationUnit> std::shared_ptr<JSToWasmWrapperCompilationUnit>
CompilationStateImpl::GetNextJSToWasmWrapperCompilationUnit() { CompilationStateImpl::GetNextJSToWasmWrapperCompilationUnit() {
int wrapper_id = size_t outstanding_units =
js_to_wasm_wrapper_id_.fetch_add(1, std::memory_order_relaxed); outstanding_js_to_wasm_wrappers_.load(std::memory_order_relaxed);
if (wrapper_id < static_cast<int>(js_to_wasm_wrapper_units_.size())) { // Use acquire semantics such that initialization of
return js_to_wasm_wrapper_units_[wrapper_id]; // {js_to_wasm_wrapper_units_} is available.
while (outstanding_units &&
!outstanding_js_to_wasm_wrappers_.compare_exchange_weak(
outstanding_units, outstanding_units - 1,
std::memory_order_acquire)) {
// Retry with updated {outstanding_units}.
} }
return nullptr; if (outstanding_units == 0) return nullptr;
return js_to_wasm_wrapper_units_[outstanding_units - 1];
} }
void CompilationStateImpl::FinalizeJSToWasmWrappers( void CompilationStateImpl::FinalizeJSToWasmWrappers(
...@@ -3257,39 +3277,9 @@ void CompilationStateImpl::SchedulePublishCompilationResults( ...@@ -3257,39 +3277,9 @@ void CompilationStateImpl::SchedulePublishCompilationResults(
} }
} }
void CompilationStateImpl::ScheduleCompileJobForNewUnits() {
if (failed()) return;
{
base::MutexGuard guard(&mutex_);
if (!current_compile_job_ || !current_compile_job_->IsValid()) {
WasmEngine* engine = native_module_->engine();
std::unique_ptr<JobTask> new_compile_job =
std::make_unique<BackgroundCompileJob>(native_module_weak_, engine,
async_counters_);
// TODO(wasm): Lower priority for TurboFan-only jobs.
current_compile_job_ = V8::GetCurrentPlatform()->PostJob(
has_priority_ ? TaskPriority::kUserBlocking
: TaskPriority::kUserVisible,
std::move(new_compile_job));
// Reset the priority. Later uses of the compilation state, e.g. for
// debugging, should compile with the default priority again.
has_priority_ = false;
return;
}
}
// Once initialized, |current_compile_job_| is never cleared (except in tests,
// where it's done synchronously).
current_compile_job_->NotifyConcurrencyIncrease();
}
size_t CompilationStateImpl::NumOutstandingCompilations() const { size_t CompilationStateImpl::NumOutstandingCompilations() const {
size_t next_wrapper = js_to_wasm_wrapper_id_.load(std::memory_order_relaxed);
size_t outstanding_wrappers = size_t outstanding_wrappers =
next_wrapper >= js_to_wasm_wrapper_units_.size() outstanding_js_to_wasm_wrappers_.load(std::memory_order_relaxed);
? 0
: js_to_wasm_wrapper_units_.size() - next_wrapper;
size_t outstanding_functions = compilation_unit_queues_.GetTotalSize(); size_t outstanding_functions = compilation_unit_queues_.GetTotalSize();
return outstanding_wrappers + outstanding_functions; return outstanding_wrappers + outstanding_functions;
} }
......
...@@ -819,6 +819,7 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled, ...@@ -819,6 +819,7 @@ NativeModule::NativeModule(WasmEngine* engine, const WasmFeatures& enabled,
shared_this->reset(this); shared_this->reset(this);
compilation_state_ = compilation_state_ =
CompilationState::New(*shared_this, std::move(async_counters)); CompilationState::New(*shared_this, std::move(async_counters));
compilation_state_->InitCompileJob(engine);
DCHECK_NOT_NULL(module_); DCHECK_NOT_NULL(module_);
if (module_->num_declared_functions > 0) { if (module_->num_declared_functions > 0) {
code_table_ = code_table_ =
......
...@@ -58,12 +58,7 @@ class MockPlatform final : public TestPlatform { ...@@ -58,12 +58,7 @@ class MockPlatform final : public TestPlatform {
bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; } bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; }
void ExecuteTasks() { void ExecuteTasks() { task_runner_->ExecuteTasks(); }
for (auto* job_handle : job_handles_) {
if (job_handle->IsValid()) job_handle->Join();
}
task_runner_->ExecuteTasks();
}
private: private:
class MockTaskRunner final : public TaskRunner { class MockTaskRunner final : public TaskRunner {
...@@ -97,16 +92,19 @@ class MockPlatform final : public TestPlatform { ...@@ -97,16 +92,19 @@ class MockPlatform final : public TestPlatform {
void ExecuteTasks() { void ExecuteTasks() {
std::queue<std::unique_ptr<v8::Task>> tasks; std::queue<std::unique_ptr<v8::Task>> tasks;
while (true) {
{ {
base::MutexGuard lock_scope(&tasks_lock_); base::MutexGuard lock_scope(&tasks_lock_);
tasks.swap(tasks_); tasks.swap(tasks_);
} }
if (tasks.empty()) break;
while (!tasks.empty()) { while (!tasks.empty()) {
std::unique_ptr<Task> task = std::move(tasks.front()); std::unique_ptr<Task> task = std::move(tasks.front());
tasks.pop(); tasks.pop();
task->Run(); task->Run();
} }
} }
}
private: private:
base::Mutex tasks_lock_; base::Mutex tasks_lock_;
......
...@@ -54,9 +54,6 @@ class MockPlatform final : public TestPlatform { ...@@ -54,9 +54,6 @@ class MockPlatform final : public TestPlatform {
bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; } bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; }
void ExecuteTasks() { void ExecuteTasks() {
for (auto* job_handle : job_handles_) {
if (job_handle->IsValid()) job_handle->Join();
}
task_runner_->ExecuteTasks(); task_runner_->ExecuteTasks();
} }
...@@ -92,16 +89,19 @@ class MockPlatform final : public TestPlatform { ...@@ -92,16 +89,19 @@ class MockPlatform final : public TestPlatform {
void ExecuteTasks() { void ExecuteTasks() {
std::queue<std::unique_ptr<v8::Task>> tasks; std::queue<std::unique_ptr<v8::Task>> tasks;
while (true) {
{ {
base::MutexGuard lock_scope(&tasks_lock_); base::MutexGuard lock_scope(&tasks_lock_);
tasks.swap(tasks_); tasks.swap(tasks_);
} }
if (tasks.empty()) break;
while (!tasks.empty()) { while (!tasks.empty()) {
std::unique_ptr<Task> task = std::move(tasks.front()); std::unique_ptr<Task> task = std::move(tasks.front());
tasks.pop(); tasks.pop();
task->Run(); task->Run();
} }
} }
}
private: private:
base::Mutex tasks_lock_; base::Mutex tasks_lock_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment