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

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

Issue 2573493002: Use idle time to make progress on scheduled compilation jobs (Closed)
Patch Set: updates Created 4 years 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
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"
8 #include "include/v8.h"
9 #include "src/base/platform/time.h"
10 #include "src/cancelable-task.h"
7 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" 11 #include "src/compiler-dispatcher/compiler-dispatcher-job.h"
8 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" 12 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
9 #include "src/objects-inl.h" 13 #include "src/objects-inl.h"
10 14
11 namespace v8 { 15 namespace v8 {
12 namespace internal { 16 namespace internal {
13 17
14 namespace { 18 namespace {
15 19
16 bool DoNextStepOnMainThread(CompilerDispatcherJob* job) { 20 enum class ExceptionHandling { kSwallow, kKeep };
21
22 bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job,
23 ExceptionHandling exception_handling) {
24 DCHECK(ThreadId::Current().Equals(isolate->thread_id()));
25 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
17 switch (job->status()) { 26 switch (job->status()) {
18 case CompileJobStatus::kInitial: 27 case CompileJobStatus::kInitial:
19 job->PrepareToParseOnMainThread(); 28 job->PrepareToParseOnMainThread();
20 break; 29 break;
21 30
22 case CompileJobStatus::kReadyToParse: 31 case CompileJobStatus::kReadyToParse:
23 job->Parse(); 32 job->Parse();
24 break; 33 break;
25 34
26 case CompileJobStatus::kParsed: 35 case CompileJobStatus::kParsed:
(...skipping 10 matching lines...) Expand all
37 46
38 case CompileJobStatus::kCompiled: 47 case CompileJobStatus::kCompiled:
39 job->FinalizeCompilingOnMainThread(); 48 job->FinalizeCompilingOnMainThread();
40 break; 49 break;
41 50
42 case CompileJobStatus::kFailed: 51 case CompileJobStatus::kFailed:
43 case CompileJobStatus::kDone: 52 case CompileJobStatus::kDone:
44 break; 53 break;
45 } 54 }
46 55
56 if (exception_handling == ExceptionHandling::kKeep && try_catch.HasCaught()) {
57 DCHECK(job->status() == CompileJobStatus::kFailed);
vogelheim 2016/12/15 15:24:12 nitpick: DCHECK_EQ
jochen (gone - plz use gerrit) 2016/12/15 15:37:59 that would require implementation operator<< for t
58 try_catch.ReThrow();
59 }
47 return job->status() != CompileJobStatus::kFailed; 60 return job->status() != CompileJobStatus::kFailed;
48 } 61 }
49 62
50 bool IsFinished(CompilerDispatcherJob* job) { 63 bool IsFinished(CompilerDispatcherJob* job) {
51 return job->status() == CompileJobStatus::kDone || 64 return job->status() == CompileJobStatus::kDone ||
52 job->status() == CompileJobStatus::kFailed; 65 job->status() == CompileJobStatus::kFailed;
53 } 66 }
54 67
68 // Theoretically, we get 50ms of idle time max, however, it's unlikely that
vogelheim 2016/12/15 15:24:11 super nitpick: Remove some of the commas, like:
jochen (gone - plz use gerrit) 2016/12/15 15:37:59 done
69 // we'll get all of it, so try to be a conservative.
70 const double kMaxIdleTimeToExpectInMs = 40;
71
55 } // namespace 72 } // namespace
56 73
57 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, size_t max_stack_size) 74 class CompilerDispatcher::IdleTask : public CancelableIdleTask {
75 public:
76 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher);
77 ~IdleTask() override;
78
79 // CancelableIdleTask implementation.
80 void RunInternal(double deadline_in_seconds) override;
81
82 private:
83 CompilerDispatcher* dispatcher_;
84
85 DISALLOW_COPY_AND_ASSIGN(IdleTask);
86 };
87
88 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate,
89 CompilerDispatcher* dispatcher)
90 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {}
91
92 CompilerDispatcher::IdleTask::~IdleTask() {}
93
94 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) {
95 dispatcher_->DoIdleWork(deadline_in_seconds);
96 }
97
98 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
99 size_t max_stack_size)
58 : isolate_(isolate), 100 : isolate_(isolate),
101 platform_(platform),
59 max_stack_size_(max_stack_size), 102 max_stack_size_(max_stack_size),
60 tracer_(new CompilerDispatcherTracer(isolate_)) {} 103 tracer_(new CompilerDispatcherTracer(isolate_)),
104 idle_task_scheduled_(false) {}
61 105
62 CompilerDispatcher::~CompilerDispatcher() {} 106 CompilerDispatcher::~CompilerDispatcher() {
107 // To avoid crashing in unit tests due to unfished jobs.
108 AbortAll(BlockingBehavior::kBlock);
109 }
63 110
64 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { 111 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
65 // We only handle functions (no eval / top-level code / wasm) that are 112 // We only handle functions (no eval / top-level code / wasm) that are
66 // attached to a script. 113 // attached to a script.
67 if (!function->script()->IsScript() || !function->is_function() || 114 if (!function->script()->IsScript() || !function->is_function() ||
68 function->asm_function() || function->native()) { 115 function->asm_function() || function->native()) {
69 return false; 116 return false;
70 } 117 }
71 118
72 if (IsEnqueued(function)) return true; 119 if (IsEnqueued(function)) return true;
73 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( 120 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
74 isolate_, tracer_.get(), function, max_stack_size_)); 121 isolate_, tracer_.get(), function, max_stack_size_));
75 std::pair<int, int> key(Script::cast(function->script())->id(), 122 std::pair<int, int> key(Script::cast(function->script())->id(),
76 function->function_literal_id()); 123 function->function_literal_id());
77 jobs_.insert(std::make_pair(key, std::move(job))); 124 JobMap::const_iterator it = jobs_.insert(std::make_pair(key, std::move(job)));
125 ClassifyJob(it);
126 ScheduleIdleTaskIfNeeded();
78 return true; 127 return true;
79 } 128 }
80 129
81 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { 130 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
82 return GetJobFor(function) != jobs_.end(); 131 return GetJobFor(function) != jobs_.end();
83 } 132 }
84 133
85 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { 134 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
86 JobMap::const_iterator job = GetJobFor(function); 135 JobMap::const_iterator job = GetJobFor(function);
87 CHECK(job != jobs_.end()); 136 CHECK(job != jobs_.end());
88 137
89 // TODO(jochen): Check if there's an in-flight background task working on this 138 // TODO(jochen): Check if there's an in-flight background task working on this
90 // job. 139 // job.
91 while (!IsFinished(job->second.get())) { 140 while (!IsFinished(job->second.get())) {
92 DoNextStepOnMainThread(job->second.get()); 141 DoNextStepOnMainThread(isolate_, job->second.get(),
142 ExceptionHandling::kKeep);
93 } 143 }
94 bool result = job->second->status() != CompileJobStatus::kFailed; 144 bool result = job->second->status() != CompileJobStatus::kFailed;
145 job->second->ResetOnMainThread();
146 jobs_for_background_thread_.erase(job);
147 jobs_for_main_thread_.erase(job);
95 jobs_.erase(job); 148 jobs_.erase(job);
96 return result; 149 return result;
97 } 150 }
98 151
99 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, 152 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function,
100 BlockingBehavior blocking) { 153 BlockingBehavior blocking) {
101 USE(blocking); 154 USE(blocking);
102 JobMap::const_iterator job = GetJobFor(function); 155 JobMap::const_iterator job = GetJobFor(function);
103 CHECK(job != jobs_.end()); 156 CHECK(job != jobs_.end());
104 157
105 // TODO(jochen): Check if there's an in-flight background task working on this 158 // TODO(jochen): Check if there's an in-flight background task working on this
106 // job. 159 // job.
160 job->second->ResetOnMainThread();
161 jobs_for_background_thread_.erase(job);
162 jobs_for_main_thread_.erase(job);
107 jobs_.erase(job); 163 jobs_.erase(job);
108 } 164 }
109 165
110 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { 166 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
111 USE(blocking); 167 USE(blocking);
112 // TODO(jochen): Check if there's an in-flight background task working on this 168 // TODO(jochen): Check if there's an in-flight background task working on this
113 // job. 169 // job.
170 for (auto& kv : jobs_) {
171 kv.second->ResetOnMainThread();
172 }
173 jobs_for_background_thread_.clear();
174 jobs_for_main_thread_.clear();
114 jobs_.clear(); 175 jobs_.clear();
115 } 176 }
116 177
117 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( 178 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
118 Handle<SharedFunctionInfo> shared) const { 179 Handle<SharedFunctionInfo> shared) const {
119 if (!shared->script()->IsScript()) return jobs_.end(); 180 if (!shared->script()->IsScript()) return jobs_.end();
120 std::pair<int, int> key(Script::cast(shared->script())->id(), 181 std::pair<int, int> key(Script::cast(shared->script())->id(),
121 shared->function_literal_id()); 182 shared->function_literal_id());
122 auto range = jobs_.equal_range(key); 183 auto range = jobs_.equal_range(key);
123 for (auto job = range.first; job != range.second; ++job) { 184 for (auto job = range.first; job != range.second; ++job) {
124 if (job->second->IsAssociatedWith(shared)) return job; 185 if (job->second->IsAssociatedWith(shared)) return job;
125 } 186 }
126 return jobs_.end(); 187 return jobs_.end();
127 } 188 }
128 189
190 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
191 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
192 if (!platform_->IdleTasksEnabled(v8_isolate)) return;
193 if (idle_task_scheduled_) return;
194 if (jobs_.empty()) return;
195 idle_task_scheduled_ = true;
196 platform_->CallIdleOnForegroundThread(v8_isolate,
197 new IdleTask(isolate_, this));
198 }
199
200 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
201 idle_task_scheduled_ = false;
202
203 DoIdleWorkForJobSet(deadline_in_seconds, &jobs_for_main_thread_);
204 if (deadline_in_seconds <= platform_->MonotonicallyIncreasingTime()) return;
205 DoIdleWorkForJobSet(deadline_in_seconds, &jobs_for_background_thread_);
206 }
207
208 void CompilerDispatcher::DoIdleWorkForJobSet(
marja 2016/12/15 15:56:43 Can you add some high-level comments to CompilerDi
209 double deadline_in_seconds,
210 std::unordered_set<JobMap::const_iterator, JobIteratorHash>* job_set) {
211 bool work_left = false;
212
213 for (auto it = job_set->begin(); it != job_set->end();) {
214 double idle_time_in_seconds =
215 deadline_in_seconds - platform_->MonotonicallyIncreasingTime();
216 if (idle_time_in_seconds <= 0.0) {
217 work_left = true;
218 break;
219 }
220
221 auto job = (*it);
222 // We'll put the job back on the right queue if we don't manage to process
223 // it completely.
224 it = jobs_for_main_thread_.erase(it);
marja 2016/12/15 15:56:43 How is this supposed to work? it is an iterator fo
jochen (gone - plz use gerrit) 2016/12/15 20:18:44 erase() in C++11 returns the next iterator, it's s
225
226 // Advance the current job as much as possible.
227 for (;;) {
228 double estimate_in_ms = job->second->EstimateRuntimeOfNextStepInMs();
229 if (idle_time_in_seconds <
230 (estimate_in_ms /
231 static_cast<double>(base::Time::kMillisecondsPerSecond))) {
232 // If there's not enough time left, try to estimate whether we would
233 // have managed to finish the job in a large idle task to assess
234 // whether we should ask for another idle callback.
235 if (estimate_in_ms <= kMaxIdleTimeToExpectInMs) work_left = true;
236 // As there's remaining work, put the job back on the queue.
237 ClassifyJob(job);
marja 2016/12/15 15:56:43 As job_set is a pointer to one of the underlying u
marja 2016/12/15 15:56:43 The interaction between the queues and the job set
jochen (gone - plz use gerrit) 2016/12/15 20:18:44 once we also have a background scheduler, they'll
238 break;
239 }
240 DoNextStepOnMainThread(isolate_, job->second.get(),
241 ExceptionHandling::kSwallow);
242
243 if (IsFinished(job->second.get())) {
244 job->second->ResetOnMainThread();
245 jobs_.erase(job);
marja 2016/12/15 15:56:43 There's jobs_ and job_set, could you name them som
246 break;
247 }
248
249 idle_time_in_seconds =
250 deadline_in_seconds - platform_->MonotonicallyIncreasingTime();
251 }
252 }
253 if (work_left) ScheduleIdleTaskIfNeeded();
vogelheim 2016/12/15 15:24:12 This would potentially be called twice, since DoId
vogelheim 2016/12/15 15:24:12 At this point, wouldn't (work_left == !job_set.emp
jochen (gone - plz use gerrit) 2016/12/15 15:37:59 I found it more readable to put it here, instead o
254 }
255
256 void CompilerDispatcher::ClassifyJob(JobMap::const_iterator it) {
marja 2016/12/15 15:56:43 This function name is misleading, it doesn't just
257 bool can_go_on_background_thread = false;
258 CompilerDispatcherJob* job = it->second.get();
259 if (job->status() == CompileJobStatus::kReadyToParse &&
260 job->can_parse_on_background_thread()) {
261 can_go_on_background_thread = true;
262 } else if (job->status() == CompileJobStatus::kReadyToCompile &&
263 job->can_compile_on_background_thread()) {
264 can_go_on_background_thread = true;
265 }
vogelheim 2016/12/15 15:24:11 style nitpick; feel free to ignore... The if-else
jochen (gone - plz use gerrit) 2016/12/15 15:37:59 done
266
267 if (can_go_on_background_thread) {
268 jobs_for_background_thread_.insert(it);
269 } else {
270 jobs_for_main_thread_.insert(it);
271 }
272 }
273
129 } // namespace internal 274 } // namespace internal
130 } // namespace v8 275 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698