Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |