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