Index: src/wasm/wasm-module.cc |
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
index 30320ee37f1723ab171d9283bedcc728a65aeec5..c830365493eba236b1b9ad63c5b9cec1cf09892e 100644 |
--- a/src/wasm/wasm-module.cc |
+++ b/src/wasm/wasm-module.cc |
@@ -7,6 +7,7 @@ |
#include "src/asmjs/asm-js.h" |
#include "src/assembler-inl.h" |
#include "src/base/atomic-utils.h" |
+#include "src/base/utils/random-number-generator.h" |
#include "src/code-stubs.h" |
#include "src/compiler/wasm-compiler.h" |
#include "src/debug/interface-types.h" |
@@ -298,20 +299,91 @@ class CompilationHelper { |
// reclaimed by explicitely releasing the {module_} field. |
CompilationHelper(Isolate* isolate, std::unique_ptr<WasmModule> module, |
bool is_sync) |
- : isolate_(isolate), module_(std::move(module)), is_sync_(is_sync) {} |
+ : isolate_(isolate), |
+ module_(std::move(module)), |
+ is_sync_(is_sync), |
+ executed_units_( |
+ isolate->random_number_generator(), |
+ (isolate->heap()->memory_allocator()->code_range()->valid() |
+ ? isolate->heap()->memory_allocator()->code_range()->size() |
+ : isolate->heap()->code_space()->Capacity()) / |
+ 2), |
+ num_background_tasks_(Min( |
+ static_cast<size_t>(FLAG_wasm_num_compilation_tasks), |
+ V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads())), |
+ stopped_compilation_tasks_(num_background_tasks_) {} |
+ |
+ bool GetNextUncompiledFunctionId(size_t* index) { |
+ DCHECK_NOT_NULL(index); |
+ // - 1 because AtomicIncrement returns the value after the atomic increment. |
+ *index = next_unit_.Increment(1) - 1; |
+ return *index < compilation_units_.size(); |
+ } |
// The actual runnable task that performs compilations in the background. |
class CompilationTask : public CancelableTask { |
public: |
CompilationHelper* helper_; |
explicit CompilationTask(CompilationHelper* helper) |
- : CancelableTask(helper->isolate_), helper_(helper) {} |
+ : CancelableTask(helper->isolate_, &helper->background_task_manager_), |
+ helper_(helper) {} |
void RunInternal() override { |
- while (helper_->FetchAndExecuteCompilationUnit()) { |
+ size_t index = 0; |
+ while (helper_->executed_units_.CanAcceptWork() && |
+ helper_->GetNextUncompiledFunctionId(&index)) { |
+ helper_->CompileAndSchedule(index); |
} |
- helper_->module_->pending_tasks.get()->Signal(); |
+ helper_->OnBackgroundTaskStopped(); |
+ } |
+ }; |
+ |
+ void OnBackgroundTaskStopped() { |
+ base::LockGuard<base::Mutex> guard(&tasks_mutex_); |
+ ++stopped_compilation_tasks_; |
+ DCHECK_LE(stopped_compilation_tasks_, num_background_tasks_); |
+ } |
+ |
+ void CompileAndSchedule(size_t index) { |
+ DisallowHeapAllocation no_allocation; |
+ DisallowHandleAllocation no_handles; |
+ DisallowHandleDereference no_deref; |
+ DisallowCodeDependencyChange no_dependency_change; |
+ DCHECK_LT(index, compilation_units_.size()); |
+ |
+ std::unique_ptr<compiler::WasmCompilationUnit> unit = |
+ std::move(compilation_units_.at(index)); |
+ unit->ExecuteCompilation(); |
+ { |
+ base::LockGuard<base::Mutex> guard(&result_mutex_); |
+ executed_units_.Schedule(std::move(unit)); |
} |
+ } |
+ |
+ class CodeGenerationSchedule { |
+ public: |
+ explicit CodeGenerationSchedule( |
+ base::RandomNumberGenerator* random_number_generator, |
+ size_t max_memory = 0); |
+ |
+ void Schedule(std::unique_ptr<compiler::WasmCompilationUnit>&& item); |
+ |
+ bool IsEmpty() const { return schedule_.empty(); } |
+ |
+ std::unique_ptr<compiler::WasmCompilationUnit> GetNext(); |
+ |
+ bool CanAcceptWork() const; |
+ |
+ void EnableThrottling() { throttle_ = true; } |
+ |
+ private: |
+ size_t GetRandomIndexInSchedule(); |
+ |
+ base::RandomNumberGenerator* random_number_generator_ = nullptr; |
+ std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> schedule_; |
+ const size_t max_memory_; |
+ bool throttle_ = false; |
+ base::AtomicNumber<size_t> allocated_memory_{0}; |
}; |
Isolate* isolate_; |
@@ -319,10 +391,11 @@ class CompilationHelper { |
bool is_sync_; |
std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> |
compilation_units_; |
- std::queue<std::unique_ptr<compiler::WasmCompilationUnit>> executed_units_; |
+ CodeGenerationSchedule executed_units_; |
base::Mutex result_mutex_; |
base::AtomicNumber<size_t> next_unit_; |
- size_t num_background_tasks_ = 0; |
+ const size_t num_background_tasks_ = 0; |
+ CancelableTaskManager background_task_manager_; |
// Run by each compilation task and by the main thread. |
bool FetchAndExecuteCompilationUnit() { |
@@ -341,7 +414,7 @@ class CompilationHelper { |
std::move(compilation_units_.at(index)); |
unit->ExecuteCompilation(); |
base::LockGuard<base::Mutex> guard(&result_mutex_); |
- executed_units_.push(std::move(unit)); |
+ executed_units_.Schedule(std::move(unit)); |
return true; |
} |
@@ -363,24 +436,12 @@ class CompilationHelper { |
return funcs_to_compile; |
} |
- void InitializeHandles() { |
- for (auto& unit : compilation_units_) { |
- unit->InitializeHandles(); |
- } |
- } |
- |
- uint32_t* StartCompilationTasks() { |
- num_background_tasks_ = |
- Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), |
- V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads()); |
- uint32_t* task_ids = new uint32_t[num_background_tasks_]; |
- for (size_t i = 0; i < num_background_tasks_; ++i) { |
- CompilationTask* task = new CompilationTask(this); |
- task_ids[i] = task->id(); |
+ void RestartCompilationTasks() { |
+ base::LockGuard<base::Mutex> guard(&tasks_mutex_); |
+ for (; stopped_compilation_tasks_ > 0; --stopped_compilation_tasks_) { |
V8::GetCurrentPlatform()->CallOnBackgroundThread( |
- task, v8::Platform::kShortRunningTask); |
+ new CompilationTask(this), v8::Platform::kShortRunningTask); |
} |
- return task_ids; |
} |
void WaitForCompilationTasks(uint32_t* task_ids) { |
@@ -394,23 +455,26 @@ class CompilationHelper { |
} |
} |
- void FinishCompilationUnits(std::vector<Handle<Code>>& results, |
- ErrorThrower* thrower) { |
+ size_t FinishCompilationUnits(std::vector<Handle<Code>>& results, |
+ ErrorThrower* thrower) { |
+ size_t finished = 0; |
while (true) { |
int func_index = -1; |
Handle<Code> result = FinishCompilationUnit(thrower, &func_index); |
if (func_index < 0) break; |
results[func_index] = result; |
+ ++finished; |
} |
+ RestartCompilationTasks(); |
+ return finished; |
} |
Handle<Code> FinishCompilationUnit(ErrorThrower* thrower, int* func_index) { |
std::unique_ptr<compiler::WasmCompilationUnit> unit; |
{ |
base::LockGuard<base::Mutex> guard(&result_mutex_); |
- if (executed_units_.empty()) return Handle<Code>::null(); |
- unit = std::move(executed_units_.front()); |
- executed_units_.pop(); |
+ if (executed_units_.IsEmpty()) return Handle<Code>::null(); |
+ unit = executed_units_.GetNext(); |
} |
*func_index = unit->func_index(); |
Handle<Code> result = unit->FinishCompilation(thrower); |
@@ -446,32 +510,34 @@ class CompilationHelper { |
// 1) The main thread allocates a compilation unit for each wasm function |
// and stores them in the vector {compilation_units}. |
InitializeParallelCompilation(module->functions, *module_env); |
- InitializeHandles(); |
- // Objects for the synchronization with the background threads. |
- base::AtomicNumber<size_t> next_unit( |
- static_cast<size_t>(FLAG_skip_compiling_wasm_funcs)); |
+ executed_units_.EnableThrottling(); |
// 2) The main thread spawns {CompilationTask} instances which run on |
// the background threads. |
- std::unique_ptr<uint32_t[]> task_ids(StartCompilationTasks()); |
- |
- // 3.a) The background threads and the main thread pick one compilation |
- // unit at a time and execute the parallel phase of the compilation |
- // unit. After finishing the execution of the parallel phase, the |
- // result is enqueued in {executed_units}. |
- while (FetchAndExecuteCompilationUnit()) { |
+ RestartCompilationTasks(); |
+ |
+ size_t finished_functions = 0; |
+ while (finished_functions < compilation_units_.size()) { |
+ // 3.a) The background threads and the main thread pick one compilation |
+ // unit at a time and execute the parallel phase of the compilation |
+ // unit. After finishing the execution of the parallel phase, the |
+ // result is enqueued in {executed_units}. |
+ size_t index = 0; |
+ if (GetNextUncompiledFunctionId(&index)) { |
+ CompileAndSchedule(index); |
+ } |
// 3.b) If {executed_units} contains a compilation unit, the main thread |
// dequeues it and finishes the compilation unit. Compilation units |
// are finished concurrently to the background threads to save |
// memory. |
- FinishCompilationUnits(results, thrower); |
+ finished_functions += FinishCompilationUnits(results, thrower); |
} |
// 4) After the parallel phase of all compilation units has started, the |
- // main thread waits for all {CompilationTask} instances to finish. |
- WaitForCompilationTasks(task_ids.get()); |
- // Finish the compilation of the remaining compilation units. |
- FinishCompilationUnits(results, thrower); |
+ // main thread waits for all {CompilationTask} instances to finish - |
+ // which happens once they all realize there's no next work item to |
+ // process. |
+ background_task_manager_.CancelAndWait(); |
} |
void CompileSequentially(ModuleBytesEnv* module_env, |
@@ -673,8 +739,48 @@ class CompilationHelper { |
return WasmModuleObject::New(isolate_, compiled_module); |
} |
+ size_t stopped_compilation_tasks_ = 0; |
+ base::Mutex tasks_mutex_; |
}; |
+CompilationHelper::CodeGenerationSchedule::CodeGenerationSchedule( |
+ base::RandomNumberGenerator* random_number_generator, size_t max_memory) |
+ : random_number_generator_(random_number_generator), |
+ max_memory_(max_memory) { |
+ DCHECK_NOT_NULL(random_number_generator_); |
+ DCHECK_GT(max_memory_, 0); |
+} |
+ |
+void CompilationHelper::CodeGenerationSchedule::Schedule( |
+ std::unique_ptr<compiler::WasmCompilationUnit>&& item) { |
+ size_t cost = item->memory_cost(); |
+ schedule_.push_back(std::move(item)); |
+ allocated_memory_.Increment(cost); |
+} |
+ |
+bool CompilationHelper::CodeGenerationSchedule::CanAcceptWork() const { |
+ return (!throttle_ || allocated_memory_.Value() <= max_memory_); |
+} |
+ |
+std::unique_ptr<compiler::WasmCompilationUnit> |
+CompilationHelper::CodeGenerationSchedule::GetNext() { |
+ DCHECK(!IsEmpty()); |
+ size_t index = GetRandomIndexInSchedule(); |
+ auto ret = std::move(schedule_[index]); |
+ std::swap(schedule_[schedule_.size() - 1], schedule_[index]); |
+ schedule_.pop_back(); |
+ allocated_memory_.Decrement(ret->memory_cost()); |
+ return ret; |
+} |
+ |
+size_t CompilationHelper::CodeGenerationSchedule::GetRandomIndexInSchedule() { |
+ double factor = random_number_generator_->NextDouble(); |
+ size_t index = (size_t)(factor * schedule_.size()); |
+ DCHECK_GE(index, 0); |
+ DCHECK_LT(index, schedule_.size()); |
+ return index; |
+} |
+ |
static void MemoryInstanceFinalizer(Isolate* isolate, |
WasmInstanceObject* instance) { |
DisallowHeapAllocation no_gc; |
@@ -2645,7 +2751,9 @@ class AsyncCompileJob { |
signature_tables_ = handle(*signature_tables_, isolate_); |
code_table_ = handle(*code_table_, isolate_); |
temp_instance_->ReopenHandles(isolate_); |
- helper_->InitializeHandles(); |
+ for (auto& unit : helper_->compilation_units_) { |
+ unit->ReopenCentryStub(); |
+ } |
deferred_handles_.push_back(deferred.Detach()); |
} |
@@ -3205,7 +3313,6 @@ void LazyCompilationOrchestrator::CompileFunction( |
ErrorThrower thrower(isolate, "WasmLazyCompile"); |
compiler::WasmCompilationUnit unit(isolate, &module_env, body, |
CStrVector(func_name.c_str()), func_index); |
- unit.InitializeHandles(); |
unit.ExecuteCompilation(); |
Handle<Code> code = unit.FinishCompilation(&thrower); |