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

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: 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 <queue>
8
9 #include "include/v8-platform.h"
10 #include "include/v8.h"
11 #include "src/base/platform/time.h"
12 #include "src/cancelable-task.h"
7 #include "src/compiler-dispatcher/compiler-dispatcher-job.h" 13 #include "src/compiler-dispatcher/compiler-dispatcher-job.h"
8 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h" 14 #include "src/compiler-dispatcher/compiler-dispatcher-tracer.h"
9 #include "src/objects-inl.h" 15 #include "src/objects-inl.h"
10 16
11 namespace v8 { 17 namespace v8 {
12 namespace internal { 18 namespace internal {
13 19
14 namespace { 20 namespace {
15 21
16 bool DoNextStepOnMainThread(CompilerDispatcherJob* job) { 22 enum class ExceptionHandling { kSwallow, kKeep };
23
24 bool DoNextStepOnMainThread(Isolate* isolate, CompilerDispatcherJob* job,
25 ExceptionHandling exception_handling) {
26 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
17 switch (job->status()) { 27 switch (job->status()) {
18 case CompileJobStatus::kInitial: 28 case CompileJobStatus::kInitial:
19 job->PrepareToParseOnMainThread(); 29 job->PrepareToParseOnMainThread();
20 break; 30 break;
21 31
22 case CompileJobStatus::kReadyToParse: 32 case CompileJobStatus::kReadyToParse:
23 job->Parse(); 33 job->Parse();
24 break; 34 break;
25 35
26 case CompileJobStatus::kParsed: 36 case CompileJobStatus::kParsed:
(...skipping 10 matching lines...) Expand all
37 47
38 case CompileJobStatus::kCompiled: 48 case CompileJobStatus::kCompiled:
39 job->FinalizeCompilingOnMainThread(); 49 job->FinalizeCompilingOnMainThread();
40 break; 50 break;
41 51
42 case CompileJobStatus::kFailed: 52 case CompileJobStatus::kFailed:
43 case CompileJobStatus::kDone: 53 case CompileJobStatus::kDone:
44 break; 54 break;
45 } 55 }
46 56
57 if (exception_handling == ExceptionHandling::kKeep && try_catch.HasCaught()) {
58 try_catch.ReThrow();
59 }
marja 2016/12/13 09:09:01 Can we assert here that if the try catch has caugh
jochen (gone - plz use gerrit) 2016/12/15 13:54:34 done
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
55 } // namespace 68 } // namespace
56 69
57 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, size_t max_stack_size) 70 class CompilerDispatcher::IdleTask : public CancelableIdleTask {
71 public:
72 IdleTask(Isolate* isolate, CompilerDispatcher* dispatcher);
73 ~IdleTask() override;
74
75 // CancelableIdleTask implementation.
vogelheim 2016/12/12 17:51:32 I don't understand the comment. If the intention i
jochen (gone - plz use gerrit) 2016/12/12 20:28:25 it's a common pattern in chromium code as it has m
vogelheim 2016/12/13 09:40:33 Acknowledged.
76 void RunInternal(double deadline_in_seconds) override;
77
78 private:
79 CompilerDispatcher* dispatcher_;
80
81 DISALLOW_COPY_AND_ASSIGN(IdleTask);
82 };
83
84 CompilerDispatcher::IdleTask::IdleTask(Isolate* isolate,
85 CompilerDispatcher* dispatcher)
86 : CancelableIdleTask(isolate), dispatcher_(dispatcher) {}
87
88 CompilerDispatcher::IdleTask::~IdleTask() {}
89
90 void CompilerDispatcher::IdleTask::RunInternal(double deadline_in_seconds) {
91 dispatcher_->DoIdleWork(deadline_in_seconds);
92 }
93
94 CompilerDispatcher::CompilerDispatcher(Isolate* isolate, Platform* platform,
95 size_t max_stack_size)
58 : isolate_(isolate), 96 : isolate_(isolate),
97 platform_(platform),
59 max_stack_size_(max_stack_size), 98 max_stack_size_(max_stack_size),
60 tracer_(new CompilerDispatcherTracer(isolate_)) {} 99 tracer_(new CompilerDispatcherTracer(isolate_)),
100 idle_task_scheduled_(false) {}
61 101
62 CompilerDispatcher::~CompilerDispatcher() {} 102 CompilerDispatcher::~CompilerDispatcher() {}
63 103
64 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) { 104 bool CompilerDispatcher::Enqueue(Handle<SharedFunctionInfo> function) {
65 // We only handle functions (no eval / top-level code / wasm) that are 105 // We only handle functions (no eval / top-level code / wasm) that are
66 // attached to a script. 106 // attached to a script.
67 if (!function->script()->IsScript() || !function->is_function() || 107 if (!function->script()->IsScript() || !function->is_function() ||
68 function->asm_function() || function->native()) { 108 function->asm_function() || function->native()) {
69 return false; 109 return false;
70 } 110 }
71 111
72 if (IsEnqueued(function)) return true; 112 if (IsEnqueued(function)) return true;
73 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob( 113 std::unique_ptr<CompilerDispatcherJob> job(new CompilerDispatcherJob(
74 isolate_, tracer_.get(), function, max_stack_size_)); 114 isolate_, tracer_.get(), function, max_stack_size_));
75 std::pair<int, int> key(Script::cast(function->script())->id(), 115 std::pair<int, int> key(Script::cast(function->script())->id(),
76 function->function_literal_id()); 116 function->function_literal_id());
77 jobs_.insert(std::make_pair(key, std::move(job))); 117 jobs_.insert(std::make_pair(key, std::move(job)));
118 ScheduleIdleTaskIfNeeded();
78 return true; 119 return true;
79 } 120 }
80 121
81 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const { 122 bool CompilerDispatcher::IsEnqueued(Handle<SharedFunctionInfo> function) const {
82 return GetJobFor(function) != jobs_.end(); 123 return GetJobFor(function) != jobs_.end();
83 } 124 }
84 125
85 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) { 126 bool CompilerDispatcher::FinishNow(Handle<SharedFunctionInfo> function) {
86 JobMap::const_iterator job = GetJobFor(function); 127 JobMap::const_iterator job = GetJobFor(function);
87 CHECK(job != jobs_.end()); 128 CHECK(job != jobs_.end());
88 129
89 // TODO(jochen): Check if there's an in-flight background task working on this 130 // TODO(jochen): Check if there's an in-flight background task working on this
90 // job. 131 // job.
91 while (!IsFinished(job->second.get())) { 132 while (!IsFinished(job->second.get())) {
92 DoNextStepOnMainThread(job->second.get()); 133 DoNextStepOnMainThread(isolate_, job->second.get(),
134 ExceptionHandling::kKeep);
93 } 135 }
94 bool result = job->second->status() != CompileJobStatus::kFailed; 136 bool result = job->second->status() != CompileJobStatus::kFailed;
137 job->second->ResetOnMainThread();
95 jobs_.erase(job); 138 jobs_.erase(job);
96 return result; 139 return result;
97 } 140 }
98 141
99 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function, 142 void CompilerDispatcher::Abort(Handle<SharedFunctionInfo> function,
100 BlockingBehavior blocking) { 143 BlockingBehavior blocking) {
101 USE(blocking); 144 USE(blocking);
102 JobMap::const_iterator job = GetJobFor(function); 145 JobMap::const_iterator job = GetJobFor(function);
103 CHECK(job != jobs_.end()); 146 CHECK(job != jobs_.end());
104 147
105 // TODO(jochen): Check if there's an in-flight background task working on this 148 // TODO(jochen): Check if there's an in-flight background task working on this
106 // job. 149 // job.
150 job->second->ResetOnMainThread();
107 jobs_.erase(job); 151 jobs_.erase(job);
108 } 152 }
109 153
110 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) { 154 void CompilerDispatcher::AbortAll(BlockingBehavior blocking) {
111 USE(blocking); 155 USE(blocking);
112 // TODO(jochen): Check if there's an in-flight background task working on this 156 // TODO(jochen): Check if there's an in-flight background task working on this
113 // job. 157 // job.
158 for (auto& kv : jobs_) {
159 kv.second->ResetOnMainThread();
160 }
114 jobs_.clear(); 161 jobs_.clear();
115 } 162 }
116 163
117 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor( 164 CompilerDispatcher::JobMap::const_iterator CompilerDispatcher::GetJobFor(
118 Handle<SharedFunctionInfo> shared) const { 165 Handle<SharedFunctionInfo> shared) const {
119 if (!shared->script()->IsScript()) return jobs_.end(); 166 if (!shared->script()->IsScript()) return jobs_.end();
120 std::pair<int, int> key(Script::cast(shared->script())->id(), 167 std::pair<int, int> key(Script::cast(shared->script())->id(),
121 shared->function_literal_id()); 168 shared->function_literal_id());
122 auto range = jobs_.equal_range(key); 169 auto range = jobs_.equal_range(key);
123 for (auto job = range.first; job != range.second; ++job) { 170 for (auto job = range.first; job != range.second; ++job) {
124 if (job->second->IsAssociatedWith(shared)) return job; 171 if (job->second->IsAssociatedWith(shared)) return job;
125 } 172 }
126 return jobs_.end(); 173 return jobs_.end();
127 } 174 }
128 175
176 void CompilerDispatcher::ScheduleIdleTaskIfNeeded() {
177 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
178 if (!platform_->IdleTasksEnabled(v8_isolate)) return;
179 if (idle_task_scheduled_) return;
180 if (jobs_.empty()) return;
181 idle_task_scheduled_ = true;
182 platform_->CallIdleOnForegroundThread(v8_isolate,
183 new IdleTask(isolate_, this));
184 }
185
186 void CompilerDispatcher::DoIdleWork(double deadline_in_seconds) {
187 idle_task_scheduled_ = false;
marja 2016/12/13 09:09:01 Could you add assert(we're on the main thread) to
188
189 // Collect all candidates for running their respective next step during
190 // idle time and sort by how long they're likely to take.
marja 2016/12/13 09:09:01 Hmm, it looks inefficient to iterate through all i
191 auto cmp = [](const std::pair<double, JobMap::const_iterator>& a,
192 const std::pair<double, JobMap::const_iterator>& b) {
193 return a.first < b.first;
194 };
195 std::priority_queue<std::pair<double, JobMap::const_iterator>,
196 std::vector<std::pair<double, JobMap::const_iterator>>,
197 decltype(cmp)>
198 runtimes(cmp);
199 for (JobMap::const_iterator it = jobs_.begin(); it != jobs_.end(); ++it) {
200 if (IsFinished(it->second.get())) continue;
201 // TODO(jochen): Check if there's an in-flight background task working on
202 // this job.
203 runtimes.push(std::make_pair(
204 it->second->EstimateRuntimeOfNextStepInMs() /
vogelheim 2016/12/12 17:51:32 Every one of these will look the same lock, right?
jochen (gone - plz use gerrit) 2016/12/12 20:28:25 right, dunno how much of an issue that is?
205 static_cast<double>(base::Time::kMillisecondsPerSecond),
206 it));
207 }
208
209 for (double idle_time_in_seconds =
marja 2016/12/13 09:21:13 What if coming up with the priority queue takes so
210 deadline_in_seconds - platform_->MonotonicallyIncreasingTime();
211 idle_time_in_seconds > 0.0;
212 idle_time_in_seconds =
213 deadline_in_seconds - platform_->MonotonicallyIncreasingTime()) {
marja 2016/12/13 09:09:01 This loop is mildly confusing. Why not just: whi
214 // Skip jobs we can't do in the remaining time budget.
215 while (!runtimes.empty() && runtimes.top().first > idle_time_in_seconds) {
216 runtimes.pop();
vogelheim 2016/12/12 17:51:32 If runtimes is a priority queue by expected runtim
vogelheim 2016/12/12 17:51:32 [Not sure:] What happens when there is a job whose
jochen (gone - plz use gerrit) 2016/12/12 20:28:25 So far, the biggest task comes first. I guess I co
vogelheim 2016/12/13 09:40:33 Ah, I misread that and assumed we'd process tasks
217 }
218 if (runtimes.empty()) break;
219
220 JobMap::const_iterator job = runtimes.top().second;
221 runtimes.pop();
222 DoNextStepOnMainThread(isolate_, job->second.get(),
223 ExceptionHandling::kSwallow);
224
225 if (IsFinished(job->second.get())) {
226 job->second->ResetOnMainThread();
227 jobs_.erase(job);
228 continue;
229 }
230
231 // Put the job back on the queue in case there is time left.
232 runtimes.push(std::make_pair(
233 job->second->EstimateRuntimeOfNextStepInMs() /
234 static_cast<double>(base::Time::kMillisecondsPerSecond),
235 job));
vogelheim 2016/12/12 17:51:32 [Not sure:] It might make sense to store auto cu
jochen (gone - plz use gerrit) 2016/12/12 20:28:25 we still need the mutex, because after having made
vogelheim 2016/12/13 09:40:33 Ah. For my understanding: Why would the time esti
jochen (gone - plz use gerrit) 2016/12/15 13:54:34 in line 222 the job does one step, and i gets rein
236 }
237 ScheduleIdleTaskIfNeeded();
238 }
239
129 } // namespace internal 240 } // namespace internal
130 } // namespace v8 241 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698