Commit 8ec7144e authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[Compiler] Process multiple jobs in background compiler dispatcher task.

Spinning up a new background task is expensive, and many times an existing
task will finish it's work before a new task starts work on a job, so enable
the existing tasks to do more than one background job.

BUG=v8:5203

Change-Id: Ibbef317c8bb3921c36a096fed88d244716be9c42
Reviewed-on: https://chromium-review.googlesource.com/441706
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarJochen Eisinger <jochen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43593}
parent 70965025
...@@ -224,7 +224,7 @@ CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, ...@@ -224,7 +224,7 @@ CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
memory_pressure_level_(MemoryPressureLevel::kNone), memory_pressure_level_(MemoryPressureLevel::kNone),
abort_(false), abort_(false),
idle_task_scheduled_(false), idle_task_scheduled_(false),
num_scheduled_background_tasks_(0), num_background_tasks_(0),
main_thread_blocking_on_job_(nullptr), main_thread_blocking_on_job_(nullptr),
block_for_testing_(false), block_for_testing_(false),
semaphore_for_testing_(0) { semaphore_for_testing_(0) {
...@@ -567,10 +567,10 @@ void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() { ...@@ -567,10 +567,10 @@ void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() {
base::LockGuard<base::Mutex> lock(&mutex_); base::LockGuard<base::Mutex> lock(&mutex_);
if (pending_background_jobs_.empty()) return; if (pending_background_jobs_.empty()) return;
if (platform_->NumberOfAvailableBackgroundThreads() <= if (platform_->NumberOfAvailableBackgroundThreads() <=
num_scheduled_background_tasks_) { num_background_tasks_) {
return; return;
} }
++num_scheduled_background_tasks_; ++num_background_tasks_;
} }
platform_->CallOnBackgroundThread( platform_->CallOnBackgroundThread(
new BackgroundTask(isolate_, task_manager_.get(), this), new BackgroundTask(isolate_, task_manager_.get(), this),
...@@ -578,38 +578,47 @@ void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() { ...@@ -578,38 +578,47 @@ void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() {
} }
void CompilerDispatcher::DoBackgroundWork() { void CompilerDispatcher::DoBackgroundWork() {
CompilerDispatcherJob* job = nullptr; for (;;) {
{ CompilerDispatcherJob* job = nullptr;
base::LockGuard<base::Mutex> lock(&mutex_); {
--num_scheduled_background_tasks_; base::LockGuard<base::Mutex> lock(&mutex_);
if (!pending_background_jobs_.empty()) { if (!pending_background_jobs_.empty()) {
auto it = pending_background_jobs_.begin(); auto it = pending_background_jobs_.begin();
job = *it; job = *it;
pending_background_jobs_.erase(it); pending_background_jobs_.erase(it);
running_background_jobs_.insert(job); running_background_jobs_.insert(job);
}
} }
} if (job == nullptr) break;
if (job == nullptr) return;
if (V8_UNLIKELY(block_for_testing_.Value())) { if (V8_UNLIKELY(block_for_testing_.Value())) {
block_for_testing_.SetValue(false); block_for_testing_.SetValue(false);
semaphore_for_testing_.Wait(); semaphore_for_testing_.Wait();
} }
if (trace_compiler_dispatcher_) { if (trace_compiler_dispatcher_) {
PrintF("CompilerDispatcher: doing background work\n"); PrintF("CompilerDispatcher: doing background work\n");
} }
DoNextStepOnBackgroundThread(job); DoNextStepOnBackgroundThread(job);
// Unconditionally schedule an idle task, as all background steps have to be
// followed by a main thread step.
ScheduleIdleTaskFromAnyThread();
ScheduleMoreBackgroundTasksIfNeeded(); {
// Unconditionally schedule an idle task, as all background steps have to be base::LockGuard<base::Mutex> lock(&mutex_);
// followed by a main thread step. running_background_jobs_.erase(job);
ScheduleIdleTaskFromAnyThread();
if (main_thread_blocking_on_job_ == job) {
main_thread_blocking_on_job_ = nullptr;
main_thread_blocking_signal_.NotifyOne();
}
}
}
{ {
base::LockGuard<base::Mutex> lock(&mutex_); base::LockGuard<base::Mutex> lock(&mutex_);
running_background_jobs_.erase(job); --num_background_tasks_;
if (running_background_jobs_.empty() && abort_) { if (running_background_jobs_.empty() && abort_) {
// This is the last background job that finished. The abort task // This is the last background job that finished. The abort task
...@@ -617,11 +626,6 @@ void CompilerDispatcher::DoBackgroundWork() { ...@@ -617,11 +626,6 @@ void CompilerDispatcher::DoBackgroundWork() {
// one to be on the safe side. // one to be on the safe side.
ScheduleAbortTask(); ScheduleAbortTask();
} }
if (main_thread_blocking_on_job_ == job) {
main_thread_blocking_on_job_ = nullptr;
main_thread_blocking_signal_.NotifyOne();
}
} }
// Don't touch |this| anymore after this point, as it might have been // Don't touch |this| anymore after this point, as it might have been
// deleted. // deleted.
......
...@@ -127,6 +127,7 @@ class V8_EXPORT_PRIVATE CompilerDispatcher { ...@@ -127,6 +127,7 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
FRIEND_TEST(CompilerDispatcherTest, AsyncAbortAllPendingBackgroundTask); FRIEND_TEST(CompilerDispatcherTest, AsyncAbortAllPendingBackgroundTask);
FRIEND_TEST(CompilerDispatcherTest, AsyncAbortAllRunningBackgroundTask); FRIEND_TEST(CompilerDispatcherTest, AsyncAbortAllRunningBackgroundTask);
FRIEND_TEST(CompilerDispatcherTest, FinishNowDuringAbortAll); FRIEND_TEST(CompilerDispatcherTest, FinishNowDuringAbortAll);
FRIEND_TEST(CompilerDispatcherTest, CompileMultipleOnBackgroundThread);
typedef std::multimap<std::pair<int, int>, typedef std::multimap<std::pair<int, int>,
std::unique_ptr<CompilerDispatcherJob>> std::unique_ptr<CompilerDispatcherJob>>
...@@ -173,8 +174,8 @@ class V8_EXPORT_PRIVATE CompilerDispatcher { ...@@ -173,8 +174,8 @@ class V8_EXPORT_PRIVATE CompilerDispatcher {
bool idle_task_scheduled_; bool idle_task_scheduled_;
// Number of currently scheduled BackgroundTask objects. // Number of scheduled or running BackgroundTask objects.
size_t num_scheduled_background_tasks_; size_t num_background_tasks_;
// The set of CompilerDispatcherJobs that can be advanced on any thread. // The set of CompilerDispatcherJobs that can be advanced on any thread.
std::unordered_set<CompilerDispatcherJob*> pending_background_jobs_; std::unordered_set<CompilerDispatcherJob*> pending_background_jobs_;
......
...@@ -1102,5 +1102,68 @@ TEST_F(CompilerDispatcherTest, EnqueueAndStepTwice) { ...@@ -1102,5 +1102,68 @@ TEST_F(CompilerDispatcherTest, EnqueueAndStepTwice) {
platform.ClearBackgroundTasks(); platform.ClearBackgroundTasks();
} }
TEST_F(CompilerDispatcherTest, CompileMultipleOnBackgroundThread) {
MockPlatform platform;
CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
const char script1[] =
"function g() { var y = 1; function f19(x) { return x * y }; "
"return f19; } g();";
Handle<JSFunction> f1 = Handle<JSFunction>::cast(RunJS(isolate(), script1));
Handle<SharedFunctionInfo> shared1(f1->shared(), i_isolate());
const char script2[] =
"function g() { var y = 1; function f20(x) { return x * y }; "
"return f20; } g();";
Handle<JSFunction> f2 = Handle<JSFunction>::cast(RunJS(isolate(), script2));
Handle<SharedFunctionInfo> shared2(f2->shared(), i_isolate());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(dispatcher.Enqueue(shared1));
ASSERT_TRUE(dispatcher.Enqueue(shared2));
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_EQ(dispatcher.jobs_.size(), 2u);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kInitial);
ASSERT_TRUE((++dispatcher.jobs_.begin())->second->status() ==
CompileJobStatus::kInitial);
// Make compiling super expensive, and advance job as much as possible on the
// foreground thread.
dispatcher.tracer_->RecordCompile(50000.0, 1);
platform.RunIdleTask(10.0, 0.0);
ASSERT_EQ(dispatcher.jobs_.size(), 2u);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kReadyToCompile);
ASSERT_TRUE((++dispatcher.jobs_.begin())->second->status() ==
CompileJobStatus::kReadyToCompile);
ASSERT_TRUE(dispatcher.IsEnqueued(shared1));
ASSERT_TRUE(dispatcher.IsEnqueued(shared2));
ASSERT_FALSE(shared1->is_compiled());
ASSERT_FALSE(shared2->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
ASSERT_TRUE(platform.BackgroundTasksPending());
platform.RunBackgroundTasksAndBlock(V8::GetCurrentPlatform());
ASSERT_TRUE(platform.IdleTaskPending());
ASSERT_FALSE(platform.BackgroundTasksPending());
ASSERT_EQ(dispatcher.jobs_.size(), 2u);
ASSERT_TRUE(dispatcher.jobs_.begin()->second->status() ==
CompileJobStatus::kCompiled);
ASSERT_TRUE((++dispatcher.jobs_.begin())->second->status() ==
CompileJobStatus::kCompiled);
// Now grant a lot of idle time and freeze time.
platform.RunIdleTask(1000.0, 0.0);
ASSERT_FALSE(dispatcher.IsEnqueued(shared1));
ASSERT_FALSE(dispatcher.IsEnqueued(shared2));
ASSERT_TRUE(shared1->is_compiled());
ASSERT_TRUE(shared2->is_compiled());
ASSERT_FALSE(platform.IdleTaskPending());
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
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