Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(579)

Side by Side Diff: src/compiler-dispatcher/compiler-dispatcher.cc

Issue 2606263002: Use background tasks for the compiler dispatcher (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler-dispatcher/compiler-dispatcher.h ('k') | src/flag-definitions.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/compiler-dispatcher/compiler-dispatcher.h" 5 #include "src/compiler-dispatcher/compiler-dispatcher.h"
6 6
7 #include "include/v8-platform.h" 7 #include "include/v8-platform.h"
8 #include "include/v8.h" 8 #include "include/v8.h"
9 #include "src/base/platform/time.h" 9 #include "src/base/platform/time.h"
10 #include "src/cancelable-task.h" 10 #include "src/cancelable-task.h"
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 try_catch.ReThrow(); 59 try_catch.ReThrow();
60 } 60 }
61 return job->status() != CompileJobStatus::kFailed; 61 return job->status() != CompileJobStatus::kFailed;
62 } 62 }
63 63
64 bool IsFinished(CompilerDispatcherJob* job) { 64 bool IsFinished(CompilerDispatcherJob* job) {
65 return job->status() == CompileJobStatus::kDone || 65 return job->status() == CompileJobStatus::kDone ||
66 job->status() == CompileJobStatus::kFailed; 66 job->status() == CompileJobStatus::kFailed;
67 } 67 }
68 68
69 bool CanRunOnAnyThread(CompilerDispatcherJob* job) {
70 return (job->status() == CompileJobStatus::kReadyToParse &&
71 job->can_parse_on_background_thread()) ||
72 (job->status() == CompileJobStatus::kReadyToCompile &&
73 job->can_compile_on_background_thread());
74 }
75
76 void DoNextStepOnBackgroundThread(CompilerDispatcherJob* job) {
77 DCHECK(CanRunOnAnyThread(job));
78 switch (job->status()) {
79 case CompileJobStatus::kReadyToParse:
80 job->Parse();
81 break;
82
83 case CompileJobStatus::kReadyToCompile:
84 job->Compile();
85 break;
86
87 default:
88 UNREACHABLE();
89 }
90 }
91
69 // Theoretically we get 50ms of idle time max, however it's unlikely that 92 // Theoretically we get 50ms of idle time max, however it's unlikely that
70 // we'll get all of it so try to be a conservative. 93 // we'll get all of it so try to be a conservative.
71 const double kMaxIdleTimeToExpectInMs = 40; 94 const double kMaxIdleTimeToExpectInMs = 40;
72 95
73 } // namespace 96 } // namespace
74 97
98 class CompilerDispatcher::BackgroundTask : public CancelableTask {
99 public:
100 BackgroundTask(Isolate* isolate, CompilerDispatcher* dispatcher);
101 ~BackgroundTask() override;
102
103 // CancelableTask implementation.
104 void RunInternal() override;
105
106 private:
107 CompilerDispatcher* dispatcher_;
108
109 DISALLOW_COPY_AND_ASSIGN(BackgroundTask);
110 };
111
112 CompilerDispatcher::BackgroundTask::BackgroundTask(
113 Isolate* isolate, CompilerDispatcher* dispatcher)
114 : CancelableTask(isolate), dispatcher_(dispatcher) {}
115
116 CompilerDispatcher::BackgroundTask::~BackgroundTask() {}
117
118 void CompilerDispatcher::BackgroundTask::RunInternal() {
119 dispatcher_->DoBackgroundWork();
120 }
121
75 class CompilerDispatcher::IdleTask : public CancelableIdleTask { 122 class CompilerDispatcher::IdleTask : public CancelableIdleTask {
76 public: 123 public:
77 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher); 124 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher);
78 ~IdleTask() override; 125 ~IdleTask() override;
79 126
80 // CancelableIdleTask implementation. 127 // CancelableIdleTask implementation.
81 void RunInternal(double deadline_in_seconds) override; 128 void RunInternal(double deadline_in_seconds) override;
82 129
83 private: 130 private:
84 CompilerDispatcher* dispatcher_; 131 CompilerDispatcher* dispatcher_;
(...skipping 10 matching lines...) Expand all
95 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) { 142 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) {
96 dispatcher_->DoIdleWork(deadline_in_seconds); 143 dispatcher_->DoIdleWork(deadline_in_seconds);
97 } 144 }
98 145
99 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform, 146 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
100 size_t max_stack_size) 147 size_t max_stack_size)
101 : isolate_(isolate), 148 : isolate_(isolate),
102 platform_(platform), 149 platform_(platform),
103 max_stack_size_(max_stack_size), 150 max_stack_size_(max_stack_size),
104 tracer_(new CompilerDispatcherTracer(isolate_)), 151 tracer_(new CompilerDispatcherTracer(isolate_)),
105 idle_task_scheduled_(false) {} 152 idle_task_scheduled_(false),
153 num_scheduled_background_tasks_(0),
154 num_available_background_jobs_(0),
155 main_thread_blocking_on_job_(nullptr) {}
106 156
107 CompilerDispatcher::~CompilerDispatcher() { 157 CompilerDispatcher::~CompilerDispatcher() {
108 // To avoid crashing in unit tests due to unfished jobs. 158 // To avoid crashing in unit tests due to unfished jobs.
109 AbortAll(BlockingBehavior::kBlock); 159 AbortAll(BlockingBehavior::kBlock);
110 } 160 }
111 161
112 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { 162 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
113 if (!IsEnabled()) return false; 163 if (!IsEnabled()) return false;
114 164
115 // We only handle functions (no eval / top-level code / wasm) that are 165 // We only handle functions (no eval / top-level code / wasm) that are
116 // attached to a script. 166 // attached to a script.
117 if (!function->script()->IsScript() || !function->is_function() || 167 if (!function->script()->IsScript() || !function->is_function() ||
118 function->asm_function() || function->native()) { 168 function->asm_function() || function->native()) {
119 return false; 169 return false;
120 } 170 }
121 171
122 if (IsEnqueued(function)) return true; 172 if (IsEnqueued(function)) return true;
123 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( 173 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
124 isolate_, tracer_.get(), function, max_stack_size_)); 174 isolate_, tracer_.get(), function, max_stack_size_));
125 std::pair<int, int> key(Script::cast(function->script())->id(), 175 std::pair<int, int> key(Script::cast(function->script())->id(),
126 function->function_literal_id()); 176 function->function_literal_id());
127 jobs_.insert(std::make_pair(key, std::move(job))); 177 jobs_.insert(std::make_pair(key, std::move(job)));
128 ScheduleIdleTaskIfNeeded(); 178 ScheduleIdleTaskIfNeeded();
129 return true; 179 return true;
130 } 180 }
131 181
132 bool CompilerDispatcher::IsEnabled() const { 182 bool CompilerDispatcher::IsEnabled() const {
133 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); 183 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
134 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate); 184 return FLAG_compiler_dispatcher && platform_->IdleTasksEnabled(v8_isolate) &&
185 platform_->NumberOfAvailableBackgroundThreads() > 0;
vogelheim 2017/01/02 17:16:24 Generally speaking, it would be nice if the dispat
jochen (gone - plz use gerrit) 2017/01/02 19:39:39 fair point. Since we only schedule background task
135 } 186 }
136 187
137 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { 188 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
138 return GetJobFor(function) != jobs_.end(); 189 return GetJobFor(function) != jobs_.end();
139 } 190 }
140 191
192 void CompilerDispatcher::EnsureNotOnBackground(CompilerDispatcherJob* job) {
193 base::LockGuard<base::Mutex> lock(&mutex_);
194 if (running_background_jobs_.find(job) == running_background_jobs_.end()) {
195 pending_background_jobs_.erase(job);
vogelheim 2017/01/02 17:16:24 Do you need to update num_available_background_job
jochen (gone - plz use gerrit) 2017/01/02 19:39:39 actually, I don't need that variable anymore, so I
196 return;
197 }
198 DCHECK_NULL(main_thread_blocking_on_job_);
199 main_thread_blocking_on_job_ = job;
200 while (main_thread_blocking_on_job_ != nullptr) {
201 main_thread_blocking_signal_.Wait(&mutex_);
202 }
203 DCHECK(pending_background_jobs_.find(job) == pending_background_jobs_.end());
204 }
205
141 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { 206 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
142 JobMap::const_iterator job = GetJobFor(function); 207 JobMap::const_iterator job = GetJobFor(function);
143 CHECK(job != jobs_.end()); 208 CHECK(job != jobs_.end());
144 209
145 // TODO(jochen): Check if there's an in-flight background task working on this 210 EnsureNotOnBackground(job->second.get());
146 // job.
147 while (!IsFinished(job->second.get())) { 211 while (!IsFinished(job->second.get())) {
148 DoNextStepOnMainThread(isolate_, job->second.get(), 212 DoNextStepOnMainThread(isolate_, job->second.get(),
149 ExceptionHandling::kThrow); 213 ExceptionHandling::kThrow);
150 } 214 }
151 bool result = job->second->status() != CompileJobStatus::kFailed; 215 bool result = job->second->status() != CompileJobStatus::kFailed;
152 job->second->ResetOnMainThread(); 216 job->second->ResetOnMainThread();
153 jobs_.erase(job); 217 jobs_.erase(job);
154 return result; 218 return result;
155 } 219 }
156 220
157 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, 221 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function,
158 BlockingBehavior blocking) { 222 BlockingBehavior blocking) {
159 USE(blocking); 223 USE(blocking);
160 JobMap::const_iterator job = GetJobFor(function); 224 JobMap::const_iterator job = GetJobFor(function);
161 CHECK(job != jobs_.end()); 225 CHECK(job != jobs_.end());
162 226
163 // TODO(jochen): Check if there's an in-flight background task working on this 227 EnsureNotOnBackground(job->second.get());
164 // job.
165 job->second->ResetOnMainThread(); 228 job->second->ResetOnMainThread();
166 jobs_.erase(job); 229 jobs_.erase(job);
167 } 230 }
168 231
169 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { 232 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
170 USE(blocking); 233 USE(blocking);
171 // TODO(jochen): Check if there's an in-flight background task working on this
172 // job.
173 for (auto& kv : jobs_) { 234 for (auto& kv : jobs_) {
235 EnsureNotOnBackground(kv.second.get());
174 kv.second->ResetOnMainThread(); 236 kv.second->ResetOnMainThread();
175 } 237 }
176 jobs_.clear(); 238 jobs_.clear();
177 } 239 }
178 240
179 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( 241 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
180 Handle<SharedFunctionInfo> shared) const { 242 Handle<SharedFunctionInfo> shared) const {
181 if (!shared->script()->IsScript()) return jobs_.end(); 243 if (!shared->script()->IsScript()) return jobs_.end();
182 std::pair<int, int> key(Script::cast(shared->script())->id(), 244 std::pair<int, int> key(Script::cast(shared->script())->id(),
183 shared->function_literal_id()); 245 shared->function_literal_id());
184 auto range = jobs_.equal_range(key); 246 auto range = jobs_.equal_range(key);
185 for (auto job = range.first; job != range.second; ++job) { 247 for (auto job = range.first; job != range.second; ++job) {
186 if (job->second->IsAssociatedWith(shared)) return job; 248 if (job->second->IsAssociatedWith(shared)) return job;
187 } 249 }
188 return jobs_.end(); 250 return jobs_.end();
189 } 251 }
190 252
191 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() { 253 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
192 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); 254 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
193 DCHECK(platform_->IdleTasksEnabled(v8_isolate)); 255 DCHECK(platform_->IdleTasksEnabled(v8_isolate));
194 if (idle_task_scheduled_) return; 256 if (idle_task_scheduled_) return;
195 if (jobs_.empty()) return; 257 if (jobs_.empty()) return;
196 idle_task_scheduled_ = true; 258 idle_task_scheduled_ = true;
197 platform_->CallIdleOnForegroundThread(v8_isolate, 259 platform_->CallIdleOnForegroundThread(v8_isolate,
198 new IdleTask(isolate_, this)); 260 new IdleTask(isolate_, this));
199 } 261 }
200 262
263 void CompilerDispatcher::ConsiderJobForBackgroundProcessing(
264 CompilerDispatcherJob* job) {
265 if (!CanRunOnAnyThread(job)) return;
266 {
267 base::LockGuard<base::Mutex> lock(&mutex_);
268 pending_background_jobs_.insert(job);
269 ++num_available_background_jobs_;
270 }
271 ScheduleMoreBackgroundTasksIfNeeded();
272 }
273
274 void CompilerDispatcher::ScheduleMoreBackgroundTasksIfNeeded() {
275 base::LockGuard<base::Mutex> lock(&mutex_);
276 if (num_available_background_jobs_ == 0) return;
277 if (platform_->NumberOfAvailableBackgroundThreads() <=
278 num_scheduled_background_tasks_) {
279 return;
280 }
281 ++num_scheduled_background_tasks_;
282 platform_->CallOnBackgroundThread(new BackgroundTask(isolate_, this),
283 v8::Platform::kShortRunningTask);
284 }
285
286 void CompilerDispatcher::DoBackgroundWork() {
287 CompilerDispatcherJob* job = nullptr;
288 {
289 base::LockGuard<base::Mutex> lock(&mutex_);
290 --num_scheduled_background_tasks_;
291 if (!pending_background_jobs_.empty()) {
292 auto it = pending_background_jobs_.begin();
293 job = *it;
294 pending_background_jobs_.erase(it);
295 running_background_jobs_.insert(job);
296 --num_available_background_jobs_;
297 }
298 }
299 if (job == nullptr) return;
300 DoNextStepOnBackgroundThread(job);
301 {
302 base::LockGuard<base::Mutex> lock(&mutex_);
303 running_background_jobs_.erase(job);
304
305 if (main_thread_blocking_on_job_ == job) {
306 main_thread_blocking_on_job_ = nullptr;
307 main_thread_blocking_signal_.NotifyOne();
308 }
309 }
310 ScheduleMoreBackgroundTasksIfNeeded();
311 }
312
201 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) { 313 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
202 idle_task_scheduled_ = false; 314 idle_task_scheduled_ = false;
203 315
204 // Number of jobs that are unlikely to make progress during any idle callback 316 // Number of jobs that are unlikely to make progress during any idle callback
205 // due to their estimated duration. 317 // due to their estimated duration.
206 size_t too_long_jobs = 0; 318 size_t too_long_jobs = 0;
207 319
208 // Iterate over all available jobs & remaining time. For each job, decide 320 // Iterate over all available jobs & remaining time. For each job, decide
209 // whether to 1) skip it (if it would take too long), 2) erase it (if it's 321 // whether to 1) skip it (if it would take too long), 2) erase it (if it's
210 // finished), or 3) make progress on it. 322 // finished), or 3) make progress on it.
211 double idle_time_in_seconds = 323 double idle_time_in_seconds =
212 deadline_in_seconds - platform_->MonotonicallyIncreasingTime(); 324 deadline_in_seconds - platform_->MonotonicallyIncreasingTime();
213 for (auto job = jobs_.begin(); 325 for (auto job = jobs_.begin();
214 job != jobs_.end() && idle_time_in_seconds > 0.0; 326 job != jobs_.end() && idle_time_in_seconds > 0.0;
215 idle_time_in_seconds = 327 idle_time_in_seconds =
216 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) { 328 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) {
329 {
330 // Don't work on jobs that are being worked on by background tasks.
331 // Similarly, remove jobs we work on from the set of available background
332 // jobs.
333 base::LockGuard<base::Mutex> lock(&mutex_);
334 if (running_background_jobs_.find(job->second.get()) !=
335 running_background_jobs_.end()) {
336 ++job;
337 continue;
338 }
339 auto it = pending_background_jobs_.find(job->second.get());
340 if (it != pending_background_jobs_.end()) {
341 pending_background_jobs_.erase(it);
342 --num_available_background_jobs_;
343 }
vogelheim 2017/01/02 17:16:24 This logic is a bit weird, because first we take i
344 }
vogelheim 2017/01/02 17:16:24 [opinion ahead; feel free to ignore:] The can-id
jochen (gone - plz use gerrit) 2017/01/02 19:39:39 the can-idle property can change, when the estimat
vogelheim 2017/01/03 10:32:37 Hmm. Not sure I'm willing to give this up yet. For
jochen (gone - plz use gerrit) 2017/01/03 12:59:35 in d8, we do get predictable idle work calls, howe
217 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs(); 345 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs();
218 if (idle_time_in_seconds < 346 if (idle_time_in_seconds <
219 (estimate_in_ms / 347 (estimate_in_ms /
220 static_cast<double>(base::Time::kMillisecondsPerSecond))) { 348 static_cast<double>(base::Time::kMillisecondsPerSecond))) {
221 // If there's not enough time left, try to estimate whether we would 349 // If there's not enough time left, try to estimate whether we would
222 // have managed to finish the job in a large idle task to assess 350 // have managed to finish the job in a large idle task to assess
223 // whether we should ask for another idle callback. 351 // whether we should ask for another idle callback.
224 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs; 352 if (estimate_in_ms > kMaxIdleTimeToExpectInMs) ++too_long_jobs;
353 ConsiderJobForBackgroundProcessing(job->second.get());
225 ++job; 354 ++job;
226 } else if (IsFinished(job->second.get())) { 355 } else if (IsFinished(job->second.get())) {
227 job->second->ResetOnMainThread(); 356 job->second->ResetOnMainThread();
228 job = jobs_.erase(job); 357 job = jobs_.erase(job);
229 break; 358 break;
230 } else { 359 } else {
231 // Do one step, and keep processing the job (as we don't advance the 360 // Do one step, and keep processing the job (as we don't advance the
232 // iterator). 361 // iterator).
233 DoNextStepOnMainThread(isolate_, job->second.get(), 362 DoNextStepOnMainThread(isolate_, job->second.get(),
234 ExceptionHandling::kSwallow); 363 ExceptionHandling::kSwallow);
235 } 364 }
236 } 365 }
237 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded(); 366 if (jobs_.size() > too_long_jobs) ScheduleIdleTaskIfNeeded();
238 } 367 }
239 368
240 } // namespace internal 369 } // namespace internal
241 } // namespace v8 370 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler-dispatcher/compiler-dispatcher.h ('k') | src/flag-definitions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698