Index: src/wasm/wasm-module.cc |
diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc |
index 28fdb105dc834cd7a0caf8f26657fa4c5970692d..cb979d4fddf018854e5ed5df2f83d3649ce7b1bd 100644 |
--- a/src/wasm/wasm-module.cc |
+++ b/src/wasm/wasm-module.cc |
@@ -130,11 +130,11 @@ class WasmLinker { |
// Create a placeholder code object and encode the corresponding index in |
// the {constant_pool_offset} field of the code object. |
// TODO(titzer): placeholder code objects are somewhat dangerous. |
- Handle<Code> self(nullptr, isolate_); |
byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions. |
CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr}; |
Handle<Code> code = isolate_->factory()->NewCode( |
- desc, Code::KindField::encode(Code::WASM_FUNCTION), self); |
+ desc, Code::KindField::encode(Code::WASM_FUNCTION), |
+ Handle<Object>::null()); |
code->set_constant_pool_offset(index + kPlaceholderMarker); |
placeholder_code_[index] = code; |
function_code_[index] = code; |
@@ -398,6 +398,68 @@ static MaybeHandle<JSFunction> LookupFunction( |
return Handle<JSFunction>::cast(function); |
} |
+// Fetches the compilation unit of a wasm function and executes its parallel |
+// phase. |
+bool FetchAndExecuteCompilation( |
+ Isolate* isolate, |
+ std::vector<compiler::WasmCompilationUnit*>* compilation_units, |
+ std::queue<compiler::WasmCompilationUnit*>* executed_units, |
+ base::Mutex* result_mutex, base::Atomic32* next_unit) { |
+ DisallowHeapAllocation no_allocation; |
+ DisallowHandleAllocation no_handles; |
+ DisallowHandleDereference no_deref; |
+ DisallowCodeDependencyChange no_dependency_change; |
+ |
+ // - 1 because AtomicIntrement returns the value after the atomic increment. |
+ size_t index = |
+ static_cast<size_t>(base::NoBarrier_AtomicIncrement(next_unit, 1)) - 1; |
Michael Lippautz
2016/05/10 07:36:41
nit: Don't know if you require the use of a 32bit
ahaas
2016/05/10 09:45:49
Done, thanks.
|
+ if (index >= compilation_units->size()) { |
+ return false; |
+ } |
+ |
+ compiler::WasmCompilationUnit* unit = compilation_units->at(index); |
+ if (unit != nullptr) { |
+ compiler::ExecuteCompilation(unit); |
+ { |
+ base::LockGuard<base::Mutex> guard(result_mutex); |
+ executed_units->push(unit); |
+ } |
+ } |
+ return true; |
+} |
+ |
+class WasmCompilationTask : public CancelableTask { |
+ public: |
+ WasmCompilationTask( |
+ Isolate* isolate, |
+ std::vector<compiler::WasmCompilationUnit*>* compilation_units, |
+ std::queue<compiler::WasmCompilationUnit*>* executed_units, |
+ base::Semaphore* on_finished, base::Mutex* result_mutex, |
+ base::Atomic32* next_unit) |
+ : CancelableTask(isolate), |
+ isolate_(isolate), |
+ compilation_units_(compilation_units), |
+ executed_units_(executed_units), |
+ on_finished_(on_finished), |
+ result_mutex_(result_mutex), |
+ next_unit_(next_unit) {} |
+ |
+ void RunInternal() override { |
+ while (FetchAndExecuteCompilation(isolate_, compilation_units_, |
+ executed_units_, result_mutex_, |
+ next_unit_)) { |
+ } |
+ on_finished_->Signal(); |
+ } |
+ |
+ Isolate* isolate_; |
+ std::vector<compiler::WasmCompilationUnit*>* compilation_units_; |
+ std::queue<compiler::WasmCompilationUnit*>* executed_units_; |
+ base::Semaphore* on_finished_; |
+ base::Mutex* result_mutex_; |
+ base::Atomic32* next_unit_; |
+}; |
+ |
// Instantiates a wasm module as a JSObject. |
// * allocates a backing store of {mem_size} bytes. |
// * installs a named property "memory" for that buffer if exported |
@@ -507,46 +569,109 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, |
isolate->counters()->wasm_functions_per_module()->AddSample( |
static_cast<int>(functions.size())); |
+ //----------------------------------------------------------------------- |
+ // For parallel compilation: |
+ // 1) The main thread allocates a compilation unit for each wasm function |
+ // and stores them in the vector compilation_units. |
+ // 2) The main thread spawns WasmCompilationTasks which run on the |
+ // background threads. |
+ // 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. |
+ // 3.b) If executed_units contains a compilation unit, the main thread |
+ // dequeues it and finishes the compilation. |
+ // 4) After the parallel phase of all compilation units has started, the |
+ // main thread waits for all WasmCompilationTasks to finish. |
+ // 5) The main thread finishes the compilation. |
+ |
std::vector<compiler::WasmCompilationUnit*> compilation_units( |
functions.size()); |
std::queue<compiler::WasmCompilationUnit*> executed_units; |
std::vector<Handle<Code>> results(functions.size()); |
- if (FLAG_wasm_parallel_compilation) { |
+ if (FLAG_wasm_num_compilation_tasks != 0) { |
+ CanonicalHandleScope canonical(isolate); |
+ |
// Create a placeholder code object for all functions. |
// TODO(ahaas): Maybe we could skip this for external functions. |
for (uint32_t i = 0; i < functions.size(); i++) { |
linker.GetFunctionCode(i); |
} |
+ // 1) The main thread allocates a compilation unit for each wasm function |
+ // and stores them in the vector compilation_units. |
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); |
i++) { |
if (!functions[i].external) { |
compilation_units[i] = compiler::CreateWasmCompilationUnit( |
&thrower, isolate, &module_env, &functions[i], i); |
+ } else { |
+ compilation_units[i] = nullptr; |
} |
} |
+ // 2) The main thread spawns WasmCompilationTasks which run on the |
+ // background threads. |
+ const size_t max_num_tasks = |
+ Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks), |
+ V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads()); |
+ |
+ base::SmartArrayPointer<uint32_t> task_ids(new uint32_t[max_num_tasks]); |
+ |
+ base::Semaphore pending_tasks(0); |
Michael Lippautz
2016/05/10 07:36:41
base:Semaphore and friends do not make sure the in
ahaas
2016/05/10 09:45:49
Done. I allocate the Semaphore on the heap now.
|
+ base::Mutex result_mutex; |
+ base::Atomic32 next_unit = FLAG_skip_compiling_wasm_funcs; |
+ |
+ for (size_t i = 0; i < max_num_tasks; i++) { |
+ WasmCompilationTask* task = new WasmCompilationTask( |
+ isolate, &compilation_units, &executed_units, &pending_tasks, |
+ &result_mutex, &next_unit); |
+ task_ids[i] = task->id(); |
+ V8::GetCurrentPlatform()->CallOnBackgroundThread( |
+ task, v8::Platform::kShortRunningTask); |
+ } |
+ |
index = FLAG_skip_compiling_wasm_funcs; |
- while (true) { |
- while (!executed_units.empty()) { |
- compiler::WasmCompilationUnit* unit = executed_units.front(); |
- executed_units.pop(); |
- int i = compiler::GetIndexOfWasmCompilationUnit(unit); |
- results[i] = compiler::FinishCompilation(unit); |
+ bool done = false; |
Michael Lippautz
2016/05/10 07:36:41
nit: Wouldn't it make more sense to clearly separa
ahaas
2016/05/10 09:45:48
I don't see a way to clearly separate these phases
Michael Lippautz
2016/05/10 10:35:51
Alright, then memory consumption is the reason to
|
+ while (!done) { |
+ // 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. |
+ if (!FetchAndExecuteCompilation(isolate, &compilation_units, |
+ &executed_units, &result_mutex, |
+ &next_unit)) { |
+ // 4) After the parallel phase of all compilation units has started, |
+ // the main thread waits for all WasmCompilationTasks to finish. |
+ for (size_t i = 0; i < max_num_tasks; i++) { |
+ if (!isolate->cancelable_task_manager()->TryAbort(task_ids[i])) { |
+ pending_tasks.Wait(); |
+ } |
+ } |
+ done = true; |
} |
- if (index < functions.size()) { |
- if (!functions[index].external) { |
- compiler::ExecuteCompilation(compilation_units[index]); |
- executed_units.push(compilation_units[index]); |
- index++; |
+ // 3.b) If executed_units contains a compilation unit, the main thread |
+ // dequeues it and finishes the compilation. |
+ while (!executed_units.empty()) { |
+ compiler::WasmCompilationUnit* unit = nullptr; |
+ { |
+ base::LockGuard<base::Mutex> guard(&result_mutex); |
+ if (!executed_units.empty()) { |
+ unit = executed_units.front(); |
+ executed_units.pop(); |
+ } |
+ } |
+ if (unit != nullptr) { |
+ int j = compiler::GetIndexOfWasmCompilationUnit(unit); |
+ if (!functions[j].external) { |
+ results[j] = compiler::FinishCompilation(unit); |
+ } |
} |
- } else { |
- break; |
} |
} |
} |
- |
+ // 5) The main thread finishes the compilation. |
// First pass: compile each function and initialize the code table. |
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); |
i++) { |
@@ -568,10 +693,11 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate, |
function.ToHandleChecked(), |
func.sig, str, str_null); |
} else { |
- if (FLAG_wasm_parallel_compilation) { |
+ if (FLAG_wasm_num_compilation_tasks != 0) { |
code = results[i]; |
} else { |
// Compile the function. |
+ CanonicalHandleScope canonical(isolate); |
code = compiler::CompileWasmFunction(&thrower, isolate, &module_env, |
&func); |
} |