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

Side by Side Diff: third_party/WebKit/Source/platform/scheduler/renderer/budget_pool.cc

Issue 2778123003: [scheduler] Add WakeupBudgetPool. (Closed)
Patch Set: First meaningful version Created 3 years, 8 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
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium 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 "platform/scheduler/renderer/budget_pool.h" 5 #include "platform/scheduler/renderer/budget_pool.h"
6 6
7 #include <cstdint> 7 #include <cstdint>
8 8
9 #include "base/format_macros.h" 9 #include "base/format_macros.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h" 11 #include "base/memory/ptr_util.h"
12 #include "base/optional.h" 12 #include "base/optional.h"
13 #include "base/strings/stringprintf.h" 13 #include "base/strings/stringprintf.h"
14 #include "platform/WebFrameScheduler.h" 14 #include "platform/WebFrameScheduler.h"
15 #include "platform/scheduler/base/real_time_domain.h" 15 #include "platform/scheduler/base/real_time_domain.h"
16 #include "platform/scheduler/child/scheduler_tqm_delegate.h" 16 #include "platform/scheduler/child/scheduler_tqm_delegate.h"
17 #include "platform/scheduler/renderer/renderer_scheduler_impl.h" 17 #include "platform/scheduler/renderer/renderer_scheduler_impl.h"
18 #include "platform/scheduler/renderer/task_queue_throttler.h" 18 #include "platform/scheduler/renderer/task_queue_throttler.h"
19 #include "platform/scheduler/renderer/throttled_time_domain.h" 19 #include "platform/scheduler/renderer/throttled_time_domain.h"
20 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" 20 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h"
21 21
22 namespace blink { 22 namespace blink {
23 namespace scheduler { 23 namespace scheduler {
24 24
25 namespace { 25 namespace {
26 26
27 base::Optional<base::TimeTicks> NextTaskRunTime(base::TimeTicks now,
28 TaskQueue* queue) {
29 if (queue->HasPendingImmediateWork())
30 return now;
31 return queue->GetNextScheduledWakeUp();
32 }
33
27 std::string PointerToId(void* pointer) { 34 std::string PointerToId(void* pointer) {
28 return base::StringPrintf( 35 return base::StringPrintf(
29 "0x%" PRIx64, 36 "0x%" PRIx64,
30 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer))); 37 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer)));
31 } 38 }
32 39
33 } // namespace 40 } // namespace
34 41
35 BudgetPool::BudgetPool(const char* name, 42 BudgetPool::BudgetPool(const char* name,
36 BudgetPoolController* budget_pool_controller) 43 BudgetPoolController* budget_pool_controller)
37 : name_(name), 44 : name_(name),
38 budget_pool_controller_(budget_pool_controller), 45 budget_pool_controller_(budget_pool_controller),
39 is_enabled_(true) {} 46 is_enabled_(true) {}
40 47
41 BudgetPool::~BudgetPool() {} 48 BudgetPool::~BudgetPool() {}
42 49
43 const char* BudgetPool::Name() const { 50 const char* BudgetPool::Name() const {
44 return name_; 51 return name_;
45 } 52 }
46 53
47 void BudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) { 54 void BudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) {
48 budget_pool_controller_->AddQueueToBudgetPool(queue, this); 55 budget_pool_controller_->AddQueueToBudgetPool(queue, this);
49 associated_task_queues_.insert(queue); 56 associated_task_queues_.insert(queue);
50 57
51 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) 58 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue))
52 return; 59 return;
53 60
54 budget_pool_controller_->BlockQueue(now, queue); 61 base::Optional<base::TimeTicks> next_desired_run_time =
62 NextTaskRunTime(now, queue);
63
64 if (next_desired_run_time && !CanRunTasksAt(next_desired_run_time.value())) {
65 budget_pool_controller_->BlockQueue(GetBlockType(), now, queue);
66 }
67 }
68
69 void BudgetPool::UnregisterQueue(TaskQueue* queue) {
70 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this);
71 associated_task_queues_.erase(queue);
55 } 72 }
56 73
57 void BudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) { 74 void BudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) {
58 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this); 75 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this);
alex clarke (OOO till 29th) 2017/04/21 09:14:19 Would it be confusing to call UnregisterQueue(queu
altimin 2017/04/25 13:22:35 I think yes. But given that I though about this to
59 associated_task_queues_.erase(queue); 76 associated_task_queues_.erase(queue);
60 77
61 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) 78 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue))
62 return; 79 return;
63 80
64 budget_pool_controller_->UnblockQueue(now, queue); 81 budget_pool_controller_->UnblockQueue(now, queue);
65 } 82 }
66 83
67 void BudgetPool::EnableThrottling(LazyNow* lazy_now) { 84 void BudgetPool::EnableThrottling(LazyNow* lazy_now) {
68 if (is_enabled_) 85 if (is_enabled_)
(...skipping 28 matching lines...) Expand all
97 } 114 }
98 115
99 void BudgetPool::Close() { 116 void BudgetPool::Close() {
100 DCHECK_EQ(0u, associated_task_queues_.size()); 117 DCHECK_EQ(0u, associated_task_queues_.size());
101 118
102 budget_pool_controller_->UnregisterBudgetPool(this); 119 budget_pool_controller_->UnregisterBudgetPool(this);
103 } 120 }
104 121
105 void BudgetPool::BlockThrottledQueues(base::TimeTicks now) { 122 void BudgetPool::BlockThrottledQueues(base::TimeTicks now) {
106 for (TaskQueue* queue : associated_task_queues_) 123 for (TaskQueue* queue : associated_task_queues_)
107 budget_pool_controller_->BlockQueue(now, queue); 124 budget_pool_controller_->BlockQueue(GetBlockType(), now, queue);
108 } 125 }
109 126
110 CPUTimeBudgetPool::CPUTimeBudgetPool( 127 CPUTimeBudgetPool::CPUTimeBudgetPool(
111 const char* name, 128 const char* name,
112 BudgetPoolController* budget_pool_controller, 129 BudgetPoolController* budget_pool_controller,
113 base::TimeTicks now) 130 base::TimeTicks now)
114 : BudgetPool(name, budget_pool_controller), 131 : BudgetPool(name, budget_pool_controller),
115 last_checkpoint_(now), 132 last_checkpoint_(now),
116 cpu_percentage_(1) {} 133 cpu_percentage_(1) {}
117 134
118 CPUTimeBudgetPool::~CPUTimeBudgetPool() {} 135 CPUTimeBudgetPool::~CPUTimeBudgetPool() {}
119 136
137 QueueBlockType CPUTimeBudgetPool::GetBlockType() const {
138 return QueueBlockType::FULL;
139 }
140
120 void CPUTimeBudgetPool::SetMaxBudgetLevel( 141 void CPUTimeBudgetPool::SetMaxBudgetLevel(
121 base::TimeTicks now, 142 base::TimeTicks now,
122 base::Optional<base::TimeDelta> max_budget_level) { 143 base::Optional<base::TimeDelta> max_budget_level) {
123 Advance(now); 144 Advance(now);
124 max_budget_level_ = max_budget_level; 145 max_budget_level_ = max_budget_level;
125 EnforceBudgetLevelRestrictions(); 146 EnforceBudgetLevelRestrictions();
126 } 147 }
127 148
128 void CPUTimeBudgetPool::SetMaxThrottlingDelay( 149 void CPUTimeBudgetPool::SetMaxThrottlingDelay(
129 base::TimeTicks now, 150 base::TimeTicks now,
(...skipping 22 matching lines...) Expand all
152 Advance(now); 173 Advance(now);
153 current_budget_level_ += budget_level; 174 current_budget_level_ += budget_level;
154 EnforceBudgetLevelRestrictions(); 175 EnforceBudgetLevelRestrictions();
155 } 176 }
156 177
157 void CPUTimeBudgetPool::SetReportingCallback( 178 void CPUTimeBudgetPool::SetReportingCallback(
158 base::Callback<void(base::TimeDelta)> reporting_callback) { 179 base::Callback<void(base::TimeDelta)> reporting_callback) {
159 reporting_callback_ = reporting_callback; 180 reporting_callback_ = reporting_callback;
160 } 181 }
161 182
162 bool CPUTimeBudgetPool::HasEnoughBudgetToRun(base::TimeTicks now) { 183 bool CPUTimeBudgetPool::CanRunTasksAt(base::TimeTicks now) const {
163 return now >= GetNextAllowedRunTime(); 184 return now >= GetNextAllowedRunTime(now);
164 } 185 }
165 186
166 base::TimeTicks CPUTimeBudgetPool::GetNextAllowedRunTime() { 187 bool CPUTimeBudgetPool::CanRunTasksUntil(base::TimeTicks now,
188 base::TimeTicks moment) const {
189 return CanRunTasksAt(now);
190 }
191
192 base::TimeTicks CPUTimeBudgetPool::GetNextAllowedRunTime(
193 base::TimeTicks desired_run_time) const {
167 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { 194 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) {
168 return last_checkpoint_; 195 return last_checkpoint_;
169 } else { 196 } else {
170 // Subtract because current_budget is negative. 197 // Subtract because current_budget is negative.
171 return last_checkpoint_ + 198 return last_checkpoint_ +
172 (-current_budget_level_ + min_budget_level_to_run_) / 199 (-current_budget_level_ + min_budget_level_to_run_) /
173 cpu_percentage_; 200 cpu_percentage_;
174 } 201 }
175 } 202 }
176 203
177 void CPUTimeBudgetPool::RecordTaskRunTime(base::TimeTicks start_time, 204 void CPUTimeBudgetPool::RecordTaskRunTime(TaskQueue* queue,
205 base::TimeTicks start_time,
178 base::TimeTicks end_time) { 206 base::TimeTicks end_time) {
179 DCHECK_LE(start_time, end_time); 207 DCHECK_LE(start_time, end_time);
180 Advance(end_time); 208 Advance(end_time);
181 if (is_enabled_) { 209 if (is_enabled_) {
182 base::TimeDelta old_budget_level = current_budget_level_; 210 base::TimeDelta old_budget_level = current_budget_level_;
183 current_budget_level_ -= (end_time - start_time); 211 current_budget_level_ -= (end_time - start_time);
184 EnforceBudgetLevelRestrictions(); 212 EnforceBudgetLevelRestrictions();
185 213
186 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 && 214 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 &&
187 current_budget_level_.InSecondsF() < 0) { 215 current_budget_level_.InSecondsF() < 0) {
188 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_); 216 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_);
189 } 217 }
190 } 218 }
191 } 219 }
192 220
221 void CPUTimeBudgetPool::OnTaskQueueHasWork(TaskQueue* queue,
222 base::TimeTicks now,
223 base::TimeTicks desired_run_time) {
224 if (!CanRunTasksAt(desired_run_time)) {
225 budget_pool_controller_->BlockQueue(GetBlockType(), now, queue);
226 } else {
227 budget_pool_controller_->UnblockQueue(now, queue);
228 }
229 }
230
231 void CPUTimeBudgetPool::OnWakeup(base::TimeTicks now) {}
232
193 void CPUTimeBudgetPool::AsValueInto(base::trace_event::TracedValue* state, 233 void CPUTimeBudgetPool::AsValueInto(base::trace_event::TracedValue* state,
194 base::TimeTicks now) const { 234 base::TimeTicks now) const {
195 state->BeginDictionary(name_); 235 state->BeginDictionary(name_);
196 236
197 state->SetString("name", name_); 237 state->SetString("name", name_);
198 state->SetDouble("time_budget", cpu_percentage_); 238 state->SetDouble("time_budget", cpu_percentage_);
199 state->SetDouble("time_budget_level_in_seconds", 239 state->SetDouble("time_budget_level_in_seconds",
200 current_budget_level_.InSecondsF()); 240 current_budget_level_.InSecondsF());
201 state->SetDouble("last_checkpoint_seconds_ago", 241 state->SetDouble("last_checkpoint_seconds_ago",
202 (now - last_checkpoint_).InSecondsF()); 242 (now - last_checkpoint_).InSecondsF());
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
238 std::min(current_budget_level_, max_budget_level_.value()); 278 std::min(current_budget_level_, max_budget_level_.value());
239 } 279 }
240 if (max_throttling_delay_) { 280 if (max_throttling_delay_) {
241 // Current budget level may be negative. 281 // Current budget level may be negative.
242 current_budget_level_ = 282 current_budget_level_ =
243 std::max(current_budget_level_, 283 std::max(current_budget_level_,
244 -max_throttling_delay_.value() * cpu_percentage_); 284 -max_throttling_delay_.value() * cpu_percentage_);
245 } 285 }
246 } 286 }
247 287
288 WakeupBudgetPool::WakeupBudgetPool(const char* name,
289 BudgetPoolController* budget_pool_controller,
290 base::TimeTicks now)
291 : BudgetPool(name, budget_pool_controller),
292 wakeups_per_second_(1),
293 last_wakeup_(now) {}
294
295 WakeupBudgetPool::~WakeupBudgetPool() {}
296
297 QueueBlockType WakeupBudgetPool::GetBlockType() const {
298 return QueueBlockType::NEW_TASKS_ONLY;
299 }
300
301 void WakeupBudgetPool::SetWakeupRate(base::TimeTicks now,
302 double wakeups_per_second) {
303 last_wakeup_ = now;
304 wakeups_per_second_ = wakeups_per_second;
305 }
306
307 void WakeupBudgetPool::SetWakeupDuration(base::TimeTicks now,
308 base::TimeDelta duration) {
309 last_wakeup_ = now;
310 wakeup_duration_ = duration;
311 }
312
313 void WakeupBudgetPool::RecordTaskRunTime(TaskQueue* queue,
314 base::TimeTicks start_time,
315 base::TimeTicks end_time) {
316 base::Optional<base::TimeTicks> next_desired_run_time =
317 NextTaskRunTime(end_time, queue);
318
319 if (next_desired_run_time && !CanRunTasksAt(next_desired_run_time.value()))
320 budget_pool_controller_->BlockQueue(GetBlockType(), end_time, queue);
321 }
322
323 base::TimeTicks WakeupBudgetPool::NextWakeup() const {
324 // Subtract 1 microsecond to work with time alignment in task queue throttler.
alex clarke (OOO till 29th) 2017/04/21 09:14:19 What happens if you don't do that?
altimin 2017/04/25 13:22:34 Alignment in PumpThrottledTasks happens. Improved
325 return last_wakeup_ + base::TimeDelta::FromSeconds(1 / wakeups_per_second_) -
326 base::TimeDelta::FromMicroseconds(1);
327 }
328
329 bool WakeupBudgetPool::CanRunTasksAt(base::TimeTicks now) const {
330 if (now < last_wakeup_ + wakeup_duration_)
alex clarke (OOO till 29th) 2017/04/21 09:14:18 Shouldn't that be <= ?
altimin 2017/04/25 13:22:35 Added a comment.
331 return true;
332 return now >= NextWakeup();
333 }
334
335 bool WakeupBudgetPool::CanRunTasksUntil(base::TimeTicks now,
336 base::TimeTicks moment) const {
337 DCHECK_LE(now, moment);
338 return now < last_wakeup_ + wakeup_duration_ &&
alex clarke (OOO till 29th) 2017/04/21 09:14:19 Ditto.
altimin 2017/04/25 13:22:34 Acknowledged.
339 moment < last_wakeup_ + wakeup_duration_;
340 }
341
342 base::TimeTicks WakeupBudgetPool::GetNextAllowedRunTime(
343 base::TimeTicks desired_run_time) const {
344 if (desired_run_time <= last_wakeup_ + wakeup_duration_) {
345 return desired_run_time;
346 }
347 return std::max(desired_run_time, NextWakeup());
348 }
349
350 void WakeupBudgetPool::OnTaskQueueHasWork(TaskQueue* queue,
351 base::TimeTicks now,
352 base::TimeTicks desired_run_time) {
353 if (!CanRunTasksAt(desired_run_time)) {
354 budget_pool_controller_->BlockQueue(GetBlockType(), now, queue);
alex clarke (OOO till 29th) 2017/04/21 09:14:19 Maybe swap the order of these to avoid the negatio
altimin 2017/04/25 13:22:34 Done.
355 } else {
356 budget_pool_controller_->UnblockQueue(now, queue);
357 }
358 }
359
360 void WakeupBudgetPool::OnWakeup(base::TimeTicks now) {
361 last_wakeup_ = now;
362 }
363
364 void WakeupBudgetPool::AsValueInto(base::trace_event::TracedValue* state,
365 base::TimeTicks now) const {
366 state->BeginDictionary(name_);
367
368 state->SetString("name", name_);
369 state->SetDouble("wakeups_per_second_rate", wakeups_per_second_);
370 state->SetDouble("wakeup_duration_in_seconds", wakeup_duration_.InSecondsF());
371 state->SetDouble("last_wakeup_seconds_ago",
372 (now - last_wakeup_).InSecondsF());
373 state->SetBoolean("is_enabled", is_enabled_);
374
375 state->BeginArray("task_queues");
376 for (TaskQueue* queue : associated_task_queues_) {
377 state->AppendString(PointerToId(queue));
378 }
379 state->EndArray();
380
381 state->EndDictionary();
382 }
383
248 } // namespace scheduler 384 } // namespace scheduler
249 } // namespace blink 385 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698