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

Side by Side Diff: base/task_scheduler/scheduler_worker_pool_impl.cc

Issue 2362743002: Add TaskScheduler.NumTasksBetweenWaits.[pool] histogram. (Closed)
Patch Set: self-review Created 4 years, 3 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 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "base/task_scheduler/scheduler_worker_pool_impl.h" 5 #include "base/task_scheduler/scheduler_worker_pool_impl.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <utility> 10 #include <utility>
(...skipping 16 matching lines...) Expand all
27 #include "base/time/time.h" 27 #include "base/time/time.h"
28 28
29 namespace base { 29 namespace base {
30 namespace internal { 30 namespace internal {
31 31
32 namespace { 32 namespace {
33 33
34 constexpr char kPoolNameSuffix[] = "Pool"; 34 constexpr char kPoolNameSuffix[] = "Pool";
35 constexpr char kDetachDurationHistogramPrefix[] = 35 constexpr char kDetachDurationHistogramPrefix[] =
36 "TaskScheduler.DetachDuration."; 36 "TaskScheduler.DetachDuration.";
37 constexpr char kNumTasksBetweenWaitsHistogramPrefix[] =
38 "TaskScheduler.NumTasksBetweenWaits.";
37 constexpr char kTaskLatencyHistogramPrefix[] = "TaskScheduler.TaskLatency."; 39 constexpr char kTaskLatencyHistogramPrefix[] = "TaskScheduler.TaskLatency.";
38 40
39 // SchedulerWorker that owns the current thread, if any. 41 // SchedulerWorker that owns the current thread, if any.
40 LazyInstance<ThreadLocalPointer<const SchedulerWorker>>::Leaky 42 LazyInstance<ThreadLocalPointer<const SchedulerWorker>>::Leaky
41 tls_current_worker = LAZY_INSTANCE_INITIALIZER; 43 tls_current_worker = LAZY_INSTANCE_INITIALIZER;
42 44
43 // SchedulerWorkerPool that owns the current thread, if any. 45 // SchedulerWorkerPool that owns the current thread, if any.
44 LazyInstance<ThreadLocalPointer<const SchedulerWorkerPool>>::Leaky 46 LazyInstance<ThreadLocalPointer<const SchedulerWorkerPool>>::Leaky
45 tls_current_worker_pool = LAZY_INSTANCE_INITIALIZER; 47 tls_current_worker_pool = LAZY_INSTANCE_INITIALIZER;
46 48
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 // Single-threaded PriorityQueue for the worker. 261 // Single-threaded PriorityQueue for the worker.
260 PriorityQueue single_threaded_priority_queue_; 262 PriorityQueue single_threaded_priority_queue_;
261 263
262 // True if the last Sequence returned by GetWork() was extracted from 264 // True if the last Sequence returned by GetWork() was extracted from
263 // |single_threaded_priority_queue_|. 265 // |single_threaded_priority_queue_|.
264 bool last_sequence_is_single_threaded_ = false; 266 bool last_sequence_is_single_threaded_ = false;
265 267
266 // Time when GetWork() first returned nullptr. 268 // Time when GetWork() first returned nullptr.
267 TimeTicks idle_start_time_; 269 TimeTicks idle_start_time_;
268 270
271 // Indicates whether the last call to GetWork() returned nullptr.
272 bool last_get_work_returned_nullptr_ = true;
273
274 // Indicates whether the SchedulerWorker was detached since the last call to
275 // GetWork().
276 bool did_detach_since_last_get_work_ = false;
277
278 // Number of tasks executed since the last time the
279 // TaskScheduler.NumTasksBetweenWaits histogram was recorded.
280 size_t num_tasks_since_last_wait_ = 0;
281
269 subtle::Atomic32 num_single_threaded_runners_ = 0; 282 subtle::Atomic32 num_single_threaded_runners_ = 0;
270 283
271 const int index_; 284 const int index_;
272 285
273 DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerDelegateImpl); 286 DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerDelegateImpl);
274 }; 287 };
275 288
276 SchedulerWorkerPoolImpl::~SchedulerWorkerPoolImpl() { 289 SchedulerWorkerPoolImpl::~SchedulerWorkerPoolImpl() {
277 // SchedulerWorkerPool should never be deleted in production unless its 290 // SchedulerWorkerPool should never be deleted in production unless its
278 // initialization failed. 291 // initialization failed.
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnMainEntry( 476 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnMainEntry(
464 SchedulerWorker* worker, 477 SchedulerWorker* worker,
465 const TimeDelta& detach_duration) { 478 const TimeDelta& detach_duration) {
466 #if DCHECK_IS_ON() 479 #if DCHECK_IS_ON()
467 // Wait for |outer_->workers_created_| to avoid traversing 480 // Wait for |outer_->workers_created_| to avoid traversing
468 // |outer_->workers_| while it is being filled by Initialize(). 481 // |outer_->workers_| while it is being filled by Initialize().
469 outer_->workers_created_.Wait(); 482 outer_->workers_created_.Wait();
470 DCHECK(ContainsWorker(outer_->workers_, worker)); 483 DCHECK(ContainsWorker(outer_->workers_, worker));
471 #endif 484 #endif
472 485
473 if (!detach_duration.is_max()) 486 DCHECK_EQ(num_tasks_since_last_wait_, 0U);
487
488 if (!detach_duration.is_max()) {
474 outer_->detach_duration_histogram_->AddTime(detach_duration); 489 outer_->detach_duration_histogram_->AddTime(detach_duration);
490 did_detach_since_last_get_work_ = true;
491 }
475 492
476 PlatformThread::SetName( 493 PlatformThread::SetName(
477 StringPrintf("TaskScheduler%sWorker%d", outer_->name_.c_str(), index_)); 494 StringPrintf("TaskScheduler%sWorker%d", outer_->name_.c_str(), index_));
478 495
479 DCHECK(!tls_current_worker.Get().Get()); 496 DCHECK(!tls_current_worker.Get().Get());
480 DCHECK(!tls_current_worker_pool.Get().Get()); 497 DCHECK(!tls_current_worker_pool.Get().Get());
481 tls_current_worker.Get().Set(worker); 498 tls_current_worker.Get().Set(worker);
482 tls_current_worker_pool.Get().Set(outer_); 499 tls_current_worker_pool.Get().Set(outer_);
483 500
484 // New threads haven't run GetWork() yet, so reset the idle_start_time_. 501 // New threads haven't run GetWork() yet, so reset the |idle_start_time_|.
485 idle_start_time_ = TimeTicks(); 502 idle_start_time_ = TimeTicks();
486 503
487 ThreadRestrictions::SetIOAllowed( 504 ThreadRestrictions::SetIOAllowed(
488 outer_->io_restriction_ == 505 outer_->io_restriction_ ==
489 SchedulerWorkerPoolParams::IORestriction::ALLOWED); 506 SchedulerWorkerPoolParams::IORestriction::ALLOWED);
490 } 507 }
491 508
492 scoped_refptr<Sequence> 509 scoped_refptr<Sequence>
493 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetWork( 510 SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetWork(
494 SchedulerWorker* worker) { 511 SchedulerWorker* worker) {
495 DCHECK(ContainsWorker(outer_->workers_, worker)); 512 DCHECK(ContainsWorker(outer_->workers_, worker));
496 513
514 // Record the TaskScheduler.NumTasksBetweenWaits histogram if the
515 // SchedulerWorker waited on its WaitableEvent since the last GetWork().
516 //
517 // Note: When GetWork() returns nullptr for the first time after returning a
518 // Sequence, SchedulerWorker waits on its WaitableEvent. When the wait stops
519 // (either because WakeUp() was called or because the sleep timeout expired),
520 // GetWork() is called and the histogram is recorded. If GetWork() returns
521 // nullptr again, the SchedulerWorker may detach. If a detach happens,
522 // |did_detach_since_last_get_work_| is set to true and the next call to
robliao 2016/09/23 00:55:30 I would say here that did_detach_since_last_get_wo
fdoray 2016/09/23 19:30:02 Done.
523 // GetWork() won't record the histogram (which is correct since the
524 // SchedulerWorker didn't wait on its WaitableEvent since the last time the
525 // histogram was recorded).
526 if (last_get_work_returned_nullptr_ && !did_detach_since_last_get_work_) {
527 outer_->num_tasks_between_waits_histogram_->Add(num_tasks_since_last_wait_);
528 num_tasks_since_last_wait_ = 0;
529 }
530 did_detach_since_last_get_work_ = false;
531
497 scoped_refptr<Sequence> sequence; 532 scoped_refptr<Sequence> sequence;
498 { 533 {
499 std::unique_ptr<PriorityQueue::Transaction> shared_transaction( 534 std::unique_ptr<PriorityQueue::Transaction> shared_transaction(
500 outer_->shared_priority_queue_.BeginTransaction()); 535 outer_->shared_priority_queue_.BeginTransaction());
501 std::unique_ptr<PriorityQueue::Transaction> single_threaded_transaction( 536 std::unique_ptr<PriorityQueue::Transaction> single_threaded_transaction(
502 single_threaded_priority_queue_.BeginTransaction()); 537 single_threaded_priority_queue_.BeginTransaction());
503 538
504 if (shared_transaction->IsEmpty() && 539 if (shared_transaction->IsEmpty() &&
505 single_threaded_transaction->IsEmpty()) { 540 single_threaded_transaction->IsEmpty()) {
506 single_threaded_transaction.reset(); 541 single_threaded_transaction.reset();
507 542
508 // |shared_transaction| is kept alive while |worker| is added to 543 // |shared_transaction| is kept alive while |worker| is added to
509 // |idle_workers_stack_| to avoid this race: 544 // |idle_workers_stack_| to avoid this race:
510 // 1. This thread creates a Transaction, finds |shared_priority_queue_| 545 // 1. This thread creates a Transaction, finds |shared_priority_queue_|
511 // empty and ends the Transaction. 546 // empty and ends the Transaction.
512 // 2. Other thread creates a Transaction, inserts a Sequence into 547 // 2. Other thread creates a Transaction, inserts a Sequence into
513 // |shared_priority_queue_| and ends the Transaction. This can't happen 548 // |shared_priority_queue_| and ends the Transaction. This can't happen
514 // if the Transaction of step 1 is still active because because there 549 // if the Transaction of step 1 is still active because because there
515 // can only be one active Transaction per PriorityQueue at a time. 550 // can only be one active Transaction per PriorityQueue at a time.
516 // 3. Other thread calls WakeUpOneWorker(). No thread is woken up because 551 // 3. Other thread calls WakeUpOneWorker(). No thread is woken up because
517 // |idle_workers_stack_| is empty. 552 // |idle_workers_stack_| is empty.
518 // 4. This thread adds itself to |idle_workers_stack_| and goes to sleep. 553 // 4. This thread adds itself to |idle_workers_stack_| and goes to sleep.
519 // No thread runs the Sequence inserted in step 2. 554 // No thread runs the Sequence inserted in step 2.
520 outer_->AddToIdleWorkersStack(worker); 555 outer_->AddToIdleWorkersStack(worker);
521 if (idle_start_time_.is_null()) 556 if (idle_start_time_.is_null())
522 idle_start_time_ = TimeTicks::Now(); 557 idle_start_time_ = TimeTicks::Now();
558 last_get_work_returned_nullptr_ = true;
523 return nullptr; 559 return nullptr;
524 } 560 }
525 561
526 // True if both PriorityQueues have Sequences and the Sequence at the top of 562 // True if both PriorityQueues have Sequences and the Sequence at the top of
527 // the shared PriorityQueue is more important. 563 // the shared PriorityQueue is more important.
528 const bool shared_sequence_is_more_important = 564 const bool shared_sequence_is_more_important =
529 !shared_transaction->IsEmpty() && 565 !shared_transaction->IsEmpty() &&
530 !single_threaded_transaction->IsEmpty() && 566 !single_threaded_transaction->IsEmpty() &&
531 shared_transaction->PeekSortKey() > 567 shared_transaction->PeekSortKey() >
532 single_threaded_transaction->PeekSortKey(); 568 single_threaded_transaction->PeekSortKey();
533 569
534 if (single_threaded_transaction->IsEmpty() || 570 if (single_threaded_transaction->IsEmpty() ||
535 shared_sequence_is_more_important) { 571 shared_sequence_is_more_important) {
536 sequence = shared_transaction->PopSequence(); 572 sequence = shared_transaction->PopSequence();
537 last_sequence_is_single_threaded_ = false; 573 last_sequence_is_single_threaded_ = false;
538 } else { 574 } else {
539 DCHECK(!single_threaded_transaction->IsEmpty()); 575 DCHECK(!single_threaded_transaction->IsEmpty());
540 sequence = single_threaded_transaction->PopSequence(); 576 sequence = single_threaded_transaction->PopSequence();
541 last_sequence_is_single_threaded_ = true; 577 last_sequence_is_single_threaded_ = true;
542 } 578 }
543 } 579 }
544 DCHECK(sequence); 580 DCHECK(sequence);
545 581
546 idle_start_time_ = TimeTicks(); 582 idle_start_time_ = TimeTicks();
583 outer_->RemoveFromIdleWorkersStack(worker);
robliao 2016/09/23 00:55:30 Nit: The ordering here isn't sensitive, right? Let
fdoray 2016/09/23 19:30:02 Local state update is now grouped together, and th
584 last_get_work_returned_nullptr_ = false;
547 585
548 outer_->RemoveFromIdleWorkersStack(worker);
549 return sequence; 586 return sequence;
550 } 587 }
551 588
552 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::DidRunTask( 589 void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::DidRunTask(
553 const Task* task, 590 const Task* task,
554 const TimeDelta& task_latency) { 591 const TimeDelta& task_latency) {
592 ++num_tasks_since_last_wait_;
593
555 const int priority_index = static_cast<int>(task->traits.priority()); 594 const int priority_index = static_cast<int>(task->traits.priority());
556 595
557 // As explained in the header file, histograms are allocated on demand. It 596 // As explained in the header file, histograms are allocated on demand. It
558 // doesn't matter if an element of |task_latency_histograms_| is set multiple 597 // doesn't matter if an element of |task_latency_histograms_| is set multiple
559 // times since GetTaskLatencyHistogram() is idempotent. As explained in the 598 // times since GetTaskLatencyHistogram() is idempotent. As explained in the
560 // comment at the top of histogram_macros.h, barriers are required. 599 // comment at the top of histogram_macros.h, barriers are required.
561 HistogramBase* task_latency_histogram = reinterpret_cast<HistogramBase*>( 600 HistogramBase* task_latency_histogram = reinterpret_cast<HistogramBase*>(
562 subtle::Acquire_Load(&outer_->task_latency_histograms_[priority_index])); 601 subtle::Acquire_Load(&outer_->task_latency_histograms_[priority_index]));
563 if (!task_latency_histogram) { 602 if (!task_latency_histogram) {
564 task_latency_histogram = 603 task_latency_histogram =
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
617 suggested_reclaim_time_(suggested_reclaim_time), 656 suggested_reclaim_time_(suggested_reclaim_time),
618 idle_workers_stack_lock_(shared_priority_queue_.container_lock()), 657 idle_workers_stack_lock_(shared_priority_queue_.container_lock()),
619 idle_workers_stack_cv_for_testing_( 658 idle_workers_stack_cv_for_testing_(
620 idle_workers_stack_lock_.CreateConditionVariable()), 659 idle_workers_stack_lock_.CreateConditionVariable()),
621 join_for_testing_returned_(WaitableEvent::ResetPolicy::MANUAL, 660 join_for_testing_returned_(WaitableEvent::ResetPolicy::MANUAL,
622 WaitableEvent::InitialState::NOT_SIGNALED), 661 WaitableEvent::InitialState::NOT_SIGNALED),
623 #if DCHECK_IS_ON() 662 #if DCHECK_IS_ON()
624 workers_created_(WaitableEvent::ResetPolicy::MANUAL, 663 workers_created_(WaitableEvent::ResetPolicy::MANUAL,
625 WaitableEvent::InitialState::NOT_SIGNALED), 664 WaitableEvent::InitialState::NOT_SIGNALED),
626 #endif 665 #endif
666 // Mimics the UMA_HISTOGRAM_LONG_TIMES macro.
627 detach_duration_histogram_(Histogram::FactoryTimeGet( 667 detach_duration_histogram_(Histogram::FactoryTimeGet(
628 kDetachDurationHistogramPrefix + name_ + kPoolNameSuffix, 668 kDetachDurationHistogramPrefix + name_ + kPoolNameSuffix,
629 TimeDelta::FromMilliseconds(1), 669 TimeDelta::FromMilliseconds(1),
630 TimeDelta::FromHours(1), 670 TimeDelta::FromHours(1),
631 50, 671 50,
632 HistogramBase::kUmaTargetedHistogramFlag)), 672 HistogramBase::kUmaTargetedHistogramFlag)),
673 // Mimics the UMA_HISTOGRAM_COUNTS_10000 macro.
robliao 2016/09/23 00:55:30 Worth mentioning why we chose the 10000 macro to c
fdoray 2016/09/23 19:30:02 Done.
674 num_tasks_between_waits_histogram_(Histogram::FactoryGet(
675 kNumTasksBetweenWaitsHistogramPrefix + name_ + kPoolNameSuffix,
676 1,
677 10000,
678 50,
679 HistogramBase::kUmaTargetedHistogramFlag)),
633 task_tracker_(task_tracker), 680 task_tracker_(task_tracker),
634 delayed_task_manager_(delayed_task_manager) { 681 delayed_task_manager_(delayed_task_manager) {
635 DCHECK(task_tracker_); 682 DCHECK(task_tracker_);
636 DCHECK(delayed_task_manager_); 683 DCHECK(delayed_task_manager_);
637 } 684 }
638 685
639 bool SchedulerWorkerPoolImpl::Initialize( 686 bool SchedulerWorkerPoolImpl::Initialize(
640 ThreadPriority priority_hint, 687 ThreadPriority priority_hint,
641 size_t max_threads, 688 size_t max_threads,
642 const ReEnqueueSequenceCallback& re_enqueue_sequence_callback) { 689 const ReEnqueueSequenceCallback& re_enqueue_sequence_callback) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
700 AutoSchedulerLock auto_lock(idle_workers_stack_lock_); 747 AutoSchedulerLock auto_lock(idle_workers_stack_lock_);
701 idle_workers_stack_.Remove(worker); 748 idle_workers_stack_.Remove(worker);
702 } 749 }
703 750
704 bool SchedulerWorkerPoolImpl::CanWorkerDetachForTesting() { 751 bool SchedulerWorkerPoolImpl::CanWorkerDetachForTesting() {
705 return !worker_detachment_disallowed_.IsSet(); 752 return !worker_detachment_disallowed_.IsSet();
706 } 753 }
707 754
708 } // namespace internal 755 } // namespace internal
709 } // namespace base 756 } // namespace base
OLDNEW
« no previous file with comments | « base/task_scheduler/scheduler_worker_pool_impl.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698