Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/task_queue_throttler.h" | 5 #include "platform/scheduler/renderer/task_queue_throttler.h" |
| 6 | 6 |
| 7 #include <cstdint> | 7 #include <cstdint> |
| 8 | 8 |
| 9 #include <iostream> // FIXME | |
| 10 | |
| 9 #include "base/format_macros.h" | 11 #include "base/format_macros.h" |
| 10 #include "base/logging.h" | 12 #include "base/logging.h" |
| 11 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 12 #include "base/optional.h" | 14 #include "base/optional.h" |
| 13 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 14 #include "platform/scheduler/base/real_time_domain.h" | 16 #include "platform/scheduler/base/real_time_domain.h" |
| 15 #include "platform/scheduler/child/scheduler_tqm_delegate.h" | 17 #include "platform/scheduler/child/scheduler_tqm_delegate.h" |
| 16 #include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h" | 18 #include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h" |
| 17 #include "platform/scheduler/renderer/renderer_scheduler_impl.h" | 19 #include "platform/scheduler/renderer/renderer_scheduler_impl.h" |
| 18 #include "platform/scheduler/renderer/throttled_time_domain.h" | 20 #include "platform/scheduler/renderer/throttled_time_domain.h" |
| 19 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" | 21 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" |
| 20 #include "public/platform/WebFrameScheduler.h" | 22 #include "public/platform/WebFrameScheduler.h" |
| 21 | 23 |
| 22 namespace blink { | 24 namespace blink { |
| 23 namespace scheduler { | 25 namespace scheduler { |
| 24 | 26 |
| 25 namespace { | 27 namespace { |
| 26 const int kMaxBudgetLevelInSeconds = 1; | 28 constexpr base::TimeDelta kMaxBudgetLevel = base::TimeDelta::FromSeconds(1); |
| 29 constexpr base::TimeDelta kMaxThrottlingDuration = | |
| 30 base::TimeDelta::FromMinutes(1); | |
| 27 | 31 |
| 28 base::Optional<base::TimeTicks> NextTaskRunTime(LazyNow* lazy_now, | 32 base::Optional<base::TimeTicks> NextTaskRunTime(LazyNow* lazy_now, |
| 29 TaskQueue* queue) { | 33 TaskQueue* queue) { |
| 30 if (queue->HasPendingImmediateWork()) | 34 if (queue->HasPendingImmediateWork()) |
| 31 return lazy_now->Now(); | 35 return lazy_now->Now(); |
| 32 return queue->GetNextScheduledWakeUp(); | 36 return queue->GetNextScheduledWakeUp(); |
| 33 } | 37 } |
| 34 | 38 |
| 35 template <class T> | 39 template <class T> |
| 36 T Min(const base::Optional<T>& optional, const T& value) { | 40 T Min(const base::Optional<T>& optional, const T& value) { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 63 if (!a) | 67 if (!a) |
| 64 return b; | 68 return b; |
| 65 return std::max(a.value(), b.value()); | 69 return std::max(a.value(), b.value()); |
| 66 } | 70 } |
| 67 | 71 |
| 68 } // namespace | 72 } // namespace |
| 69 | 73 |
| 70 TaskQueueThrottler::TimeBudgetPool::TimeBudgetPool( | 74 TaskQueueThrottler::TimeBudgetPool::TimeBudgetPool( |
| 71 const char* name, | 75 const char* name, |
| 72 TaskQueueThrottler* task_queue_throttler, | 76 TaskQueueThrottler* task_queue_throttler, |
| 73 base::TimeTicks now) | 77 base::TimeTicks now, |
| 78 base::Optional<base::TimeDelta> max_budget_level, | |
| 79 base::Optional<base::TimeDelta> max_throttling_duration) | |
| 74 : name_(name), | 80 : name_(name), |
| 75 task_queue_throttler_(task_queue_throttler), | 81 task_queue_throttler_(task_queue_throttler), |
| 76 max_budget_level_(base::TimeDelta::FromSeconds(kMaxBudgetLevelInSeconds)), | 82 max_budget_level_(max_budget_level), |
| 83 max_throttling_duration_(max_throttling_duration), | |
| 77 last_checkpoint_(now), | 84 last_checkpoint_(now), |
| 78 cpu_percentage_(1), | 85 cpu_percentage_(1), |
| 79 is_enabled_(true) {} | 86 is_enabled_(true) {} |
| 80 | 87 |
| 81 TaskQueueThrottler::TimeBudgetPool::~TimeBudgetPool() {} | 88 TaskQueueThrottler::TimeBudgetPool::~TimeBudgetPool() {} |
| 82 | 89 |
| 83 void TaskQueueThrottler::TimeBudgetPool::SetTimeBudget(base::TimeTicks now, | 90 void TaskQueueThrottler::TimeBudgetPool::SetTimeBudget(base::TimeTicks now, |
| 84 double cpu_percentage) { | 91 double cpu_percentage) { |
| 85 Advance(now); | 92 Advance(now); |
| 86 cpu_percentage_ = cpu_percentage; | 93 cpu_percentage_ = cpu_percentage; |
| 94 EnforceMaximalThrottlingDuration(); | |
| 87 } | 95 } |
| 88 | 96 |
| 89 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now, | 97 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now, |
| 90 TaskQueue* queue) { | 98 TaskQueue* queue) { |
| 91 std::pair<TaskQueueMap::iterator, bool> insert_result = | 99 std::pair<TaskQueueMap::iterator, bool> insert_result = |
| 92 task_queue_throttler_->queue_details_.insert( | 100 task_queue_throttler_->queue_details_.insert( |
| 93 std::make_pair(queue, Metadata(0, queue->IsQueueEnabled()))); | 101 std::make_pair(queue, Metadata(0, queue->IsQueueEnabled()))); |
| 94 Metadata& metadata = insert_result.first->second; | 102 Metadata& metadata = insert_result.first->second; |
| 95 DCHECK(!metadata.time_budget_pool); | 103 DCHECK(!metadata.time_budget_pool); |
| 96 metadata.time_budget_pool = this; | 104 metadata.time_budget_pool = this; |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 base::TimeTicks TaskQueueThrottler::TimeBudgetPool::GetNextAllowedRunTime() { | 173 base::TimeTicks TaskQueueThrottler::TimeBudgetPool::GetNextAllowedRunTime() { |
| 166 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { | 174 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { |
| 167 return last_checkpoint_; | 175 return last_checkpoint_; |
| 168 } else { | 176 } else { |
| 169 // Subtract because current_budget is negative. | 177 // Subtract because current_budget is negative. |
| 170 return last_checkpoint_ - current_budget_level_ / cpu_percentage_; | 178 return last_checkpoint_ - current_budget_level_ / cpu_percentage_; |
| 171 } | 179 } |
| 172 } | 180 } |
| 173 | 181 |
| 174 void TaskQueueThrottler::TimeBudgetPool::RecordTaskRunTime( | 182 void TaskQueueThrottler::TimeBudgetPool::RecordTaskRunTime( |
| 175 base::TimeDelta task_run_time) { | 183 base::TimeTicks start_time, |
| 176 if (is_enabled_) | 184 base::TimeTicks end_time) { |
| 177 current_budget_level_ -= task_run_time; | 185 Advance(end_time); |
|
alex clarke (OOO till 29th)
2016/10/14 16:37:22
Maybe DCHECK_LE(start_time, end_time);
altimin
2016/10/17 10:48:37
Done.
| |
| 186 if (is_enabled_) { | |
| 187 current_budget_level_ -= (end_time - start_time); | |
| 188 EnforceMaximalThrottlingDuration(); | |
| 189 } | |
| 178 } | 190 } |
| 179 | 191 |
| 180 const char* TaskQueueThrottler::TimeBudgetPool::Name() const { | 192 const char* TaskQueueThrottler::TimeBudgetPool::Name() const { |
| 181 return name_; | 193 return name_; |
| 182 } | 194 } |
| 183 | 195 |
| 184 void TaskQueueThrottler::TimeBudgetPool::AsValueInto( | 196 void TaskQueueThrottler::TimeBudgetPool::AsValueInto( |
| 185 base::trace_event::TracedValue* state, | 197 base::trace_event::TracedValue* state, |
| 186 base::TimeTicks now) const { | 198 base::TimeTicks now) const { |
| 187 state->BeginDictionary(); | 199 state->BeginDictionary(); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 199 "%" PRIx64, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(queue)))); | 211 "%" PRIx64, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(queue)))); |
| 200 } | 212 } |
| 201 state->EndArray(); | 213 state->EndArray(); |
| 202 | 214 |
| 203 state->EndDictionary(); | 215 state->EndDictionary(); |
| 204 } | 216 } |
| 205 | 217 |
| 206 void TaskQueueThrottler::TimeBudgetPool::Advance(base::TimeTicks now) { | 218 void TaskQueueThrottler::TimeBudgetPool::Advance(base::TimeTicks now) { |
| 207 if (now > last_checkpoint_) { | 219 if (now > last_checkpoint_) { |
| 208 if (is_enabled_) { | 220 if (is_enabled_) { |
| 209 current_budget_level_ = std::min( | 221 current_budget_level_ += cpu_percentage_ * (now - last_checkpoint_); |
| 210 current_budget_level_ + cpu_percentage_ * (now - last_checkpoint_), | 222 if (max_budget_level_) { |
| 211 max_budget_level_); | 223 current_budget_level_ = |
| 224 std::min(current_budget_level_, max_budget_level_.value()); | |
| 225 } | |
| 212 } | 226 } |
| 213 last_checkpoint_ = now; | 227 last_checkpoint_ = now; |
| 214 } | 228 } |
| 215 } | 229 } |
| 216 | 230 |
| 217 void TaskQueueThrottler::TimeBudgetPool::BlockThrottledQueues( | 231 void TaskQueueThrottler::TimeBudgetPool::BlockThrottledQueues( |
| 218 base::TimeTicks now) { | 232 base::TimeTicks now) { |
| 219 for (TaskQueue* queue : associated_task_queues_) { | 233 for (TaskQueue* queue : associated_task_queues_) { |
| 220 if (!task_queue_throttler_->IsThrottled(queue)) | 234 if (!task_queue_throttler_->IsThrottled(queue)) |
| 221 continue; | 235 continue; |
| 222 | 236 |
| 223 queue->SetQueueEnabled(false); | 237 queue->SetQueueEnabled(false); |
| 224 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | 238 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, |
| 225 base::nullopt); | 239 base::nullopt); |
| 226 } | 240 } |
| 227 } | 241 } |
| 228 | 242 |
| 243 void TaskQueueThrottler::TimeBudgetPool::EnforceMaximalThrottlingDuration() { | |
|
alex clarke (OOO till 29th)
2016/10/14 16:37:22
Out of curiosity would it make sense to enforce ma
altimin
2016/10/17 10:48:37
Thanks! Done.
| |
| 244 if (max_throttling_duration_) { | |
| 245 current_budget_level_ = | |
| 246 std::max(current_budget_level_, | |
|
alex clarke (OOO till 29th)
2016/10/14 16:37:22
Maybe add a comment pointing out the obvious: curr
altimin
2016/10/17 10:48:37
Done.
| |
| 247 -max_throttling_duration_.value() * cpu_percentage_); | |
| 248 } | |
| 249 } | |
| 250 | |
| 229 TaskQueueThrottler::TaskQueueThrottler( | 251 TaskQueueThrottler::TaskQueueThrottler( |
| 230 RendererSchedulerImpl* renderer_scheduler, | 252 RendererSchedulerImpl* renderer_scheduler, |
| 231 const char* tracing_category) | 253 const char* tracing_category) |
| 232 : task_runner_(renderer_scheduler->ControlTaskRunner()), | 254 : task_runner_(renderer_scheduler->ControlTaskRunner()), |
| 233 renderer_scheduler_(renderer_scheduler), | 255 renderer_scheduler_(renderer_scheduler), |
| 234 tick_clock_(renderer_scheduler->tick_clock()), | 256 tick_clock_(renderer_scheduler->tick_clock()), |
| 235 tracing_category_(tracing_category), | 257 tracing_category_(tracing_category), |
| 236 time_domain_(new ThrottledTimeDomain(this, tracing_category)), | 258 time_domain_(new ThrottledTimeDomain(this, tracing_category)), |
| 259 max_budget_level_(kMaxBudgetLevel), | |
| 260 max_throttling_duration_(kMaxThrottlingDuration), | |
| 237 virtual_time_(false), | 261 virtual_time_(false), |
| 238 weak_factory_(this) { | 262 weak_factory_(this) { |
| 239 pump_throttled_tasks_closure_.Reset(base::Bind( | 263 pump_throttled_tasks_closure_.Reset(base::Bind( |
| 240 &TaskQueueThrottler::PumpThrottledTasks, weak_factory_.GetWeakPtr())); | 264 &TaskQueueThrottler::PumpThrottledTasks, weak_factory_.GetWeakPtr())); |
| 241 forward_immediate_work_callback_ = | 265 forward_immediate_work_callback_ = |
| 242 base::Bind(&TaskQueueThrottler::OnTimeDomainHasImmediateWork, | 266 base::Bind(&TaskQueueThrottler::OnTimeDomainHasImmediateWork, |
| 243 weak_factory_.GetWeakPtr()); | 267 weak_factory_.GetWeakPtr()); |
| 244 | 268 |
| 245 renderer_scheduler_->RegisterTimeDomain(time_domain_.get()); | 269 renderer_scheduler_->RegisterTimeDomain(time_domain_.get()); |
| 246 } | 270 } |
| (...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 496 | 520 |
| 497 task_queue->SetTimeDomain(renderer_scheduler_->GetVirtualTimeDomain()); | 521 task_queue->SetTimeDomain(renderer_scheduler_->GetVirtualTimeDomain()); |
| 498 task_queue->RemoveFence(); | 522 task_queue->RemoveFence(); |
| 499 task_queue->SetQueueEnabled(enabled); | 523 task_queue->SetQueueEnabled(enabled); |
| 500 } | 524 } |
| 501 } | 525 } |
| 502 | 526 |
| 503 TaskQueueThrottler::TimeBudgetPool* TaskQueueThrottler::CreateTimeBudgetPool( | 527 TaskQueueThrottler::TimeBudgetPool* TaskQueueThrottler::CreateTimeBudgetPool( |
| 504 const char* name) { | 528 const char* name) { |
| 505 TimeBudgetPool* time_budget_pool = | 529 TimeBudgetPool* time_budget_pool = |
| 506 new TimeBudgetPool(name, this, tick_clock_->NowTicks()); | 530 new TimeBudgetPool(name, this, tick_clock_->NowTicks(), max_budget_level_, |
| 531 max_throttling_duration_); | |
| 507 time_budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool); | 532 time_budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool); |
| 508 return time_budget_pool; | 533 return time_budget_pool; |
| 509 } | 534 } |
| 510 | 535 |
| 511 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue, | 536 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue, |
| 512 base::TimeTicks start_time, | 537 base::TimeTicks start_time, |
| 513 base::TimeTicks end_time) { | 538 base::TimeTicks end_time) { |
| 514 if (!IsThrottled(task_queue)) | 539 if (!IsThrottled(task_queue)) |
| 515 return; | 540 return; |
| 516 | 541 |
| 517 TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(task_queue); | 542 TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(task_queue); |
| 518 if (!time_budget_pool) | 543 if (!time_budget_pool) |
| 519 return; | 544 return; |
| 520 | 545 |
| 521 time_budget_pool->RecordTaskRunTime(end_time - start_time); | 546 time_budget_pool->RecordTaskRunTime(start_time, end_time); |
| 522 if (!time_budget_pool->HasEnoughBudgetToRun(end_time)) | 547 if (!time_budget_pool->HasEnoughBudgetToRun(end_time)) |
| 523 time_budget_pool->BlockThrottledQueues(end_time); | 548 time_budget_pool->BlockThrottledQueues(end_time); |
| 524 } | 549 } |
| 525 | 550 |
| 526 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state, | 551 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state, |
| 527 base::TimeTicks now) const { | 552 base::TimeTicks now) const { |
| 528 if (pending_pump_throttled_tasks_runtime_) { | 553 if (pending_pump_throttled_tasks_runtime_) { |
| 529 state->SetDouble( | 554 state->SetDouble( |
| 530 "next_throttled_tasks_pump_in_seconds", | 555 "next_throttled_tasks_pump_in_seconds", |
| 531 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF()); | 556 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF()); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 571 return std::max(now, time_budget_pool->GetNextAllowedRunTime()); | 596 return std::max(now, time_budget_pool->GetNextAllowedRunTime()); |
| 572 } | 597 } |
| 573 | 598 |
| 574 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) { | 599 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) { |
| 575 if (!it->second.IsThrottled() && !it->second.time_budget_pool) | 600 if (!it->second.IsThrottled() && !it->second.time_budget_pool) |
| 576 queue_details_.erase(it); | 601 queue_details_.erase(it); |
| 577 } | 602 } |
| 578 | 603 |
| 579 } // namespace scheduler | 604 } // namespace scheduler |
| 580 } // namespace blink | 605 } // namespace blink |
| OLD | NEW |