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" |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 41 BudgetPool::~BudgetPool() {} | 41 BudgetPool::~BudgetPool() {} |
| 42 | 42 |
| 43 const char* BudgetPool::Name() const { | 43 const char* BudgetPool::Name() const { |
| 44 return name_; | 44 return name_; |
| 45 } | 45 } |
| 46 | 46 |
| 47 void BudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) { | 47 void BudgetPool::AddQueue(base::TimeTicks now, TaskQueue* queue) { |
| 48 budget_pool_controller_->AddQueueToBudgetPool(queue, this); | 48 budget_pool_controller_->AddQueueToBudgetPool(queue, this); |
| 49 associated_task_queues_.insert(queue); | 49 associated_task_queues_.insert(queue); |
| 50 | 50 |
| 51 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) | 51 if (!is_enabled_) |
| 52 return; | 52 return; |
| 53 budget_pool_controller_->UpdateQueueThrottlingState(now, queue); | |
| 54 } | |
| 53 | 55 |
| 54 budget_pool_controller_->BlockQueue(now, queue); | 56 void BudgetPool::UnregisterQueue(TaskQueue* queue) { |
|
alex clarke (OOO till 29th)
2017/04/27 10:28:14
I wonder if this and DissociateQueue and TaskQueu
altimin
2017/04/27 11:07:41
I'll look into it, but it may be non-trivial due t
| |
| 57 DissociateQueue(queue); | |
| 55 } | 58 } |
| 56 | 59 |
| 57 void BudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) { | 60 void BudgetPool::RemoveQueue(base::TimeTicks now, TaskQueue* queue) { |
| 61 DissociateQueue(queue); | |
| 62 | |
| 63 if (!is_enabled_) | |
| 64 return; | |
| 65 | |
| 66 budget_pool_controller_->UpdateQueueThrottlingState(now, queue); | |
| 67 } | |
| 68 | |
| 69 void BudgetPool::DissociateQueue(TaskQueue* queue) { | |
| 58 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this); | 70 budget_pool_controller_->RemoveQueueFromBudgetPool(queue, this); |
| 59 associated_task_queues_.erase(queue); | 71 associated_task_queues_.erase(queue); |
| 60 | |
| 61 if (!is_enabled_ || !budget_pool_controller_->IsThrottled(queue)) | |
| 62 return; | |
| 63 | |
| 64 budget_pool_controller_->UnblockQueue(now, queue); | |
| 65 } | 72 } |
| 66 | 73 |
| 67 void BudgetPool::EnableThrottling(LazyNow* lazy_now) { | 74 void BudgetPool::EnableThrottling(LazyNow* lazy_now) { |
| 68 if (is_enabled_) | 75 if (is_enabled_) |
| 69 return; | 76 return; |
| 70 is_enabled_ = true; | 77 is_enabled_ = true; |
| 71 | 78 |
| 72 TRACE_EVENT0("renderer.scheduler", "BudgetPool_EnableThrottling"); | 79 TRACE_EVENT0("renderer.scheduler", "BudgetPool_EnableThrottling"); |
| 73 | 80 |
| 74 BlockThrottledQueues(lazy_now->Now()); | 81 BlockThrottledQueues(lazy_now->Now()); |
| 75 } | 82 } |
| 76 | 83 |
| 77 void BudgetPool::DisableThrottling(LazyNow* lazy_now) { | 84 void BudgetPool::DisableThrottling(LazyNow* lazy_now) { |
| 78 if (!is_enabled_) | 85 if (!is_enabled_) |
| 79 return; | 86 return; |
| 80 is_enabled_ = false; | 87 is_enabled_ = false; |
| 81 | 88 |
| 82 TRACE_EVENT0("renderer.scheduler", "BudgetPool_DisableThrottling"); | 89 TRACE_EVENT0("renderer.scheduler", "BudgetPool_DisableThrottling"); |
| 83 | 90 |
| 84 for (TaskQueue* queue : associated_task_queues_) { | 91 for (TaskQueue* queue : associated_task_queues_) { |
| 85 if (!budget_pool_controller_->IsThrottled(queue)) | 92 budget_pool_controller_->UpdateQueueThrottlingState(lazy_now->Now(), queue); |
| 86 continue; | |
| 87 | |
| 88 budget_pool_controller_->UnblockQueue(lazy_now->Now(), queue); | |
| 89 } | 93 } |
| 90 | 94 |
| 91 // TODO(altimin): We need to disable TimeBudgetQueues here or they will | 95 // TODO(altimin): We need to disable TimeBudgetQueues here or they will |
| 92 // regenerate extra time budget when they are disabled. | 96 // regenerate extra time budget when they are disabled. |
| 93 } | 97 } |
| 94 | 98 |
| 95 bool BudgetPool::IsThrottlingEnabled() const { | 99 bool BudgetPool::IsThrottlingEnabled() const { |
| 96 return is_enabled_; | 100 return is_enabled_; |
| 97 } | 101 } |
| 98 | 102 |
| 99 void BudgetPool::Close() { | 103 void BudgetPool::Close() { |
| 100 DCHECK_EQ(0u, associated_task_queues_.size()); | 104 DCHECK_EQ(0u, associated_task_queues_.size()); |
| 101 | 105 |
| 102 budget_pool_controller_->UnregisterBudgetPool(this); | 106 budget_pool_controller_->UnregisterBudgetPool(this); |
| 103 } | 107 } |
| 104 | 108 |
| 105 void BudgetPool::BlockThrottledQueues(base::TimeTicks now) { | 109 void BudgetPool::BlockThrottledQueues(base::TimeTicks now) { |
| 106 for (TaskQueue* queue : associated_task_queues_) | 110 for (TaskQueue* queue : associated_task_queues_) |
| 107 budget_pool_controller_->BlockQueue(now, queue); | 111 budget_pool_controller_->UpdateQueueThrottlingState(now, queue); |
| 108 } | 112 } |
| 109 | 113 |
| 110 CPUTimeBudgetPool::CPUTimeBudgetPool( | 114 CPUTimeBudgetPool::CPUTimeBudgetPool( |
| 111 const char* name, | 115 const char* name, |
| 112 BudgetPoolController* budget_pool_controller, | 116 BudgetPoolController* budget_pool_controller, |
| 113 base::TimeTicks now) | 117 base::TimeTicks now) |
| 114 : BudgetPool(name, budget_pool_controller), | 118 : BudgetPool(name, budget_pool_controller), |
| 115 last_checkpoint_(now), | 119 last_checkpoint_(now), |
| 116 cpu_percentage_(1) {} | 120 cpu_percentage_(1) {} |
| 117 | 121 |
| 118 CPUTimeBudgetPool::~CPUTimeBudgetPool() {} | 122 CPUTimeBudgetPool::~CPUTimeBudgetPool() {} |
| 119 | 123 |
| 124 QueueBlockType CPUTimeBudgetPool::GetBlockType() const { | |
| 125 return QueueBlockType::kAllTasks; | |
| 126 } | |
| 127 | |
| 120 void CPUTimeBudgetPool::SetMaxBudgetLevel( | 128 void CPUTimeBudgetPool::SetMaxBudgetLevel( |
| 121 base::TimeTicks now, | 129 base::TimeTicks now, |
| 122 base::Optional<base::TimeDelta> max_budget_level) { | 130 base::Optional<base::TimeDelta> max_budget_level) { |
| 123 Advance(now); | 131 Advance(now); |
| 124 max_budget_level_ = max_budget_level; | 132 max_budget_level_ = max_budget_level; |
| 125 EnforceBudgetLevelRestrictions(); | 133 EnforceBudgetLevelRestrictions(); |
| 126 } | 134 } |
| 127 | 135 |
| 128 void CPUTimeBudgetPool::SetMaxThrottlingDelay( | 136 void CPUTimeBudgetPool::SetMaxThrottlingDelay( |
| 129 base::TimeTicks now, | 137 base::TimeTicks now, |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 152 Advance(now); | 160 Advance(now); |
| 153 current_budget_level_ += budget_level; | 161 current_budget_level_ += budget_level; |
| 154 EnforceBudgetLevelRestrictions(); | 162 EnforceBudgetLevelRestrictions(); |
| 155 } | 163 } |
| 156 | 164 |
| 157 void CPUTimeBudgetPool::SetReportingCallback( | 165 void CPUTimeBudgetPool::SetReportingCallback( |
| 158 base::Callback<void(base::TimeDelta)> reporting_callback) { | 166 base::Callback<void(base::TimeDelta)> reporting_callback) { |
| 159 reporting_callback_ = reporting_callback; | 167 reporting_callback_ = reporting_callback; |
| 160 } | 168 } |
| 161 | 169 |
| 162 bool CPUTimeBudgetPool::HasEnoughBudgetToRun(base::TimeTicks now) { | 170 bool CPUTimeBudgetPool::CanRunTasksAt(base::TimeTicks now, |
| 163 return now >= GetNextAllowedRunTime(); | 171 bool is_wake_up) const { |
| 172 return now >= GetNextAllowedRunTime(now); | |
| 164 } | 173 } |
| 165 | 174 |
| 166 base::TimeTicks CPUTimeBudgetPool::GetNextAllowedRunTime() { | 175 bool CPUTimeBudgetPool::CanRunTasksUntil(base::TimeTicks now, |
| 176 base::TimeTicks moment) const { | |
| 177 return CanRunTasksAt(now, false); | |
| 178 } | |
| 179 | |
| 180 base::TimeTicks CPUTimeBudgetPool::GetNextAllowedRunTime( | |
| 181 base::TimeTicks desired_run_time) const { | |
| 167 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { | 182 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { |
| 168 return last_checkpoint_; | 183 return last_checkpoint_; |
| 169 } else { | 184 } else { |
| 170 // Subtract because current_budget is negative. | 185 // Subtract because current_budget is negative. |
| 171 return last_checkpoint_ + | 186 return last_checkpoint_ + |
| 172 (-current_budget_level_ + min_budget_level_to_run_) / | 187 (-current_budget_level_ + min_budget_level_to_run_) / |
| 173 cpu_percentage_; | 188 cpu_percentage_; |
| 174 } | 189 } |
| 175 } | 190 } |
| 176 | 191 |
| 177 void CPUTimeBudgetPool::RecordTaskRunTime(base::TimeTicks start_time, | 192 void CPUTimeBudgetPool::RecordTaskRunTime(TaskQueue* queue, |
| 193 base::TimeTicks start_time, | |
| 178 base::TimeTicks end_time) { | 194 base::TimeTicks end_time) { |
| 179 DCHECK_LE(start_time, end_time); | 195 DCHECK_LE(start_time, end_time); |
| 180 Advance(end_time); | 196 Advance(end_time); |
| 181 if (is_enabled_) { | 197 if (is_enabled_) { |
| 182 base::TimeDelta old_budget_level = current_budget_level_; | 198 base::TimeDelta old_budget_level = current_budget_level_; |
| 183 current_budget_level_ -= (end_time - start_time); | 199 current_budget_level_ -= (end_time - start_time); |
| 184 EnforceBudgetLevelRestrictions(); | 200 EnforceBudgetLevelRestrictions(); |
| 185 | 201 |
| 186 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 && | 202 if (!reporting_callback_.is_null() && old_budget_level.InSecondsF() > 0 && |
| 187 current_budget_level_.InSecondsF() < 0) { | 203 current_budget_level_.InSecondsF() < 0) { |
| 188 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_); | 204 reporting_callback_.Run(-current_budget_level_ / cpu_percentage_); |
| 189 } | 205 } |
| 190 } | 206 } |
| 207 | |
| 208 if (current_budget_level_.InSecondsF() < 0) { | |
|
alex clarke (OOO till 29th)
2017/04/27 10:28:14
nit: Elsewhere we tend not to use curly braces for
altimin
2017/04/27 11:07:41
Done.
| |
| 209 BlockThrottledQueues(end_time); | |
| 210 } | |
| 191 } | 211 } |
| 192 | 212 |
| 213 void CPUTimeBudgetPool::OnQueueNextWakeUpChanged( | |
| 214 TaskQueue* queue, | |
| 215 base::TimeTicks now, | |
| 216 base::TimeTicks desired_run_time) { | |
| 217 budget_pool_controller_->UpdateQueueThrottlingState(now, queue); | |
| 218 } | |
| 219 | |
| 220 void CPUTimeBudgetPool::OnWakeUp(base::TimeTicks now) {} | |
| 221 | |
| 193 void CPUTimeBudgetPool::AsValueInto(base::trace_event::TracedValue* state, | 222 void CPUTimeBudgetPool::AsValueInto(base::trace_event::TracedValue* state, |
| 194 base::TimeTicks now) const { | 223 base::TimeTicks now) const { |
| 195 state->BeginDictionary(name_); | 224 state->BeginDictionary(name_); |
| 196 | 225 |
| 197 state->SetString("name", name_); | 226 state->SetString("name", name_); |
| 198 state->SetDouble("time_budget", cpu_percentage_); | 227 state->SetDouble("time_budget", cpu_percentage_); |
| 199 state->SetDouble("time_budget_level_in_seconds", | 228 state->SetDouble("time_budget_level_in_seconds", |
| 200 current_budget_level_.InSecondsF()); | 229 current_budget_level_.InSecondsF()); |
| 201 state->SetDouble("last_checkpoint_seconds_ago", | 230 state->SetDouble("last_checkpoint_seconds_ago", |
| 202 (now - last_checkpoint_).InSecondsF()); | 231 (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()); | 267 std::min(current_budget_level_, max_budget_level_.value()); |
| 239 } | 268 } |
| 240 if (max_throttling_delay_) { | 269 if (max_throttling_delay_) { |
| 241 // Current budget level may be negative. | 270 // Current budget level may be negative. |
| 242 current_budget_level_ = | 271 current_budget_level_ = |
| 243 std::max(current_budget_level_, | 272 std::max(current_budget_level_, |
| 244 -max_throttling_delay_.value() * cpu_percentage_); | 273 -max_throttling_delay_.value() * cpu_percentage_); |
| 245 } | 274 } |
| 246 } | 275 } |
| 247 | 276 |
| 277 WakeUpBudgetPool::WakeUpBudgetPool(const char* name, | |
| 278 BudgetPoolController* budget_pool_controller, | |
| 279 base::TimeTicks now) | |
| 280 : BudgetPool(name, budget_pool_controller), wakeups_per_second_(1) {} | |
| 281 | |
| 282 WakeUpBudgetPool::~WakeUpBudgetPool() {} | |
| 283 | |
| 284 QueueBlockType WakeUpBudgetPool::GetBlockType() const { | |
| 285 return QueueBlockType::kNewTasksOnly; | |
| 286 } | |
| 287 | |
| 288 void WakeUpBudgetPool::SetWakeUpRate(double wakeups_per_second) { | |
| 289 wakeups_per_second_ = wakeups_per_second; | |
| 290 } | |
| 291 | |
| 292 void WakeUpBudgetPool::SetWakeUpDuration(base::TimeDelta duration) { | |
| 293 wakeup_duration_ = duration; | |
| 294 } | |
| 295 | |
| 296 void WakeUpBudgetPool::RecordTaskRunTime(TaskQueue* queue, | |
| 297 base::TimeTicks start_time, | |
| 298 base::TimeTicks end_time) { | |
| 299 budget_pool_controller_->UpdateQueueThrottlingState(end_time, queue); | |
| 300 } | |
| 301 | |
| 302 base::Optional<base::TimeTicks> WakeUpBudgetPool::NextWakeUp() const { | |
| 303 if (!last_wakeup_) | |
| 304 return base::nullopt; | |
| 305 // Subtract 1 microsecond to work with time alignment in task queue throttler. | |
| 306 // This is needed due to alignment mechanism in task queue throttler -- | |
| 307 // whole seconds need to be aligned to the next second to deal with immediate | |
| 308 // tasks correctly. By subtracting 1 microsecond we ensure that next wakeup | |
| 309 // gets aligned to a correct time. | |
| 310 return last_wakeup_.value() + | |
| 311 base::TimeDelta::FromSeconds(1 / wakeups_per_second_) - | |
| 312 base::TimeDelta::FromMicroseconds(1); | |
| 313 } | |
| 314 | |
| 315 bool WakeUpBudgetPool::CanRunTasksAt(base::TimeTicks now, | |
| 316 bool is_wake_up) const { | |
| 317 if (!last_wakeup_) | |
| 318 return false; | |
| 319 if (last_wakeup_ == now && is_wake_up) | |
| 320 return true; | |
| 321 return now < last_wakeup_.value() + wakeup_duration_; | |
| 322 } | |
| 323 | |
| 324 bool WakeUpBudgetPool::CanRunTasksUntil(base::TimeTicks now, | |
| 325 base::TimeTicks moment) const { | |
| 326 if (!last_wakeup_) | |
| 327 return false; | |
| 328 DCHECK_LE(now, moment); | |
| 329 return now < last_wakeup_.value() + wakeup_duration_ && | |
| 330 moment < last_wakeup_.value() + wakeup_duration_; | |
| 331 } | |
| 332 | |
| 333 base::TimeTicks WakeUpBudgetPool::GetNextAllowedRunTime( | |
| 334 base::TimeTicks desired_run_time) const { | |
| 335 if (!last_wakeup_) | |
| 336 return desired_run_time; | |
| 337 if (desired_run_time < last_wakeup_.value() + wakeup_duration_) | |
| 338 return desired_run_time; | |
| 339 return std::max(desired_run_time, NextWakeUp().value()); | |
| 340 } | |
| 341 | |
| 342 void WakeUpBudgetPool::OnQueueNextWakeUpChanged( | |
| 343 TaskQueue* queue, | |
| 344 base::TimeTicks now, | |
| 345 base::TimeTicks desired_run_time) { | |
| 346 budget_pool_controller_->UpdateQueueThrottlingState(now, queue); | |
| 347 } | |
| 348 | |
| 349 void WakeUpBudgetPool::OnWakeUp(base::TimeTicks now) { | |
| 350 last_wakeup_ = now; | |
| 351 } | |
| 352 | |
| 353 void WakeUpBudgetPool::AsValueInto(base::trace_event::TracedValue* state, | |
| 354 base::TimeTicks now) const { | |
| 355 state->BeginDictionary(name_); | |
| 356 | |
| 357 state->SetString("name", name_); | |
| 358 state->SetDouble("wakeups_per_second_rate", wakeups_per_second_); | |
| 359 state->SetDouble("wakeup_duration_in_seconds", wakeup_duration_.InSecondsF()); | |
| 360 if (last_wakeup_) { | |
| 361 state->SetDouble("last_wakeup_seconds_ago", | |
| 362 (now - last_wakeup_.value()).InSecondsF()); | |
| 363 } | |
| 364 state->SetBoolean("is_enabled", is_enabled_); | |
| 365 | |
| 366 state->BeginArray("task_queues"); | |
| 367 for (TaskQueue* queue : associated_task_queues_) { | |
| 368 state->AppendString(PointerToId(queue)); | |
| 369 } | |
| 370 state->EndArray(); | |
| 371 | |
| 372 state->EndDictionary(); | |
| 373 } | |
| 374 | |
| 248 } // namespace scheduler | 375 } // namespace scheduler |
| 249 } // namespace blink | 376 } // namespace blink |
| OLD | NEW |