Skip to content

Commit

Permalink
[scheduler] Monitor renderer main thread load level.
Browse files Browse the repository at this point in the history
Add monitoring of main thread load level for background and foreground renderers.

BUG=639852

Review-Url: https://codereview.chromium.org/2265873004
Cr-Commit-Position: refs/heads/master@{#413783}
  • Loading branch information
altimin authored and Commit bot committed Aug 23, 2016
1 parent 9383a84 commit 3ab9c0b
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 12 deletions.
18 changes: 10 additions & 8 deletions third_party/WebKit/Source/platform/blink_platform.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,8 @@
'scheduler/base/task_queue_selector.cc',
'scheduler/base/task_queue_selector.h',
'scheduler/base/task_time_tracker.h',
'scheduler/base/thread_load_tracker.cc',
'scheduler/base/thread_load_tracker.h',
'scheduler/base/time_domain.cc',
'scheduler/base/time_domain.h',
'scheduler/base/virtual_time_domain.cc',
Expand All @@ -961,7 +963,6 @@
'scheduler/base/work_queue.h',
'scheduler/base/work_queue_sets.cc',
'scheduler/base/work_queue_sets.h',
'scheduler/CancellableTaskFactory.h',
'scheduler/child/compositor_worker_scheduler.cc',
'scheduler/child/idle_helper.cc',
'scheduler/child/idle_helper.h',
Expand All @@ -986,14 +987,14 @@
'scheduler/renderer/deadline_task_runner.h',
'scheduler/renderer/idle_time_estimator.cc',
'scheduler/renderer/idle_time_estimator.h',
'scheduler/renderer/render_widget_scheduling_state.cc',
'scheduler/renderer/render_widget_signals.cc',
'scheduler/renderer/render_widget_signals.h',
'scheduler/renderer/renderer_scheduler.cc',
'scheduler/renderer/renderer_scheduler_impl.cc',
'scheduler/renderer/renderer_scheduler_impl.h',
'scheduler/renderer/renderer_web_scheduler_impl.cc',
'scheduler/renderer/renderer_web_scheduler_impl.h',
'scheduler/renderer/render_widget_scheduling_state.cc',
'scheduler/renderer/render_widget_signals.cc',
'scheduler/renderer/render_widget_signals.h',
'scheduler/renderer/task_cost_estimator.cc',
'scheduler/renderer/task_cost_estimator.h',
'scheduler/renderer/throttled_time_domain.cc',
Expand All @@ -1004,10 +1005,10 @@
'scheduler/renderer/user_model.h',
'scheduler/renderer/web_frame_scheduler_impl.cc',
'scheduler/renderer/web_frame_scheduler_impl.h',
'scheduler/renderer/webthread_impl_for_renderer_scheduler.cc',
'scheduler/renderer/webthread_impl_for_renderer_scheduler.h',
'scheduler/renderer/web_view_scheduler_impl.cc',
'scheduler/renderer/web_view_scheduler_impl.h',
'scheduler/renderer/webthread_impl_for_renderer_scheduler.cc',
'scheduler/renderer/webthread_impl_for_renderer_scheduler.h',
'scheduler/utility/webthread_impl_for_utility_thread.cc',
'scroll/MainThreadScrollingReason.h',
'scroll/ProgrammaticScrollAnimator.cpp',
Expand Down Expand Up @@ -1316,14 +1317,15 @@
'network/ResourceResponseTest.cpp',
'scheduler/CancellableTaskFactoryTest.cpp',
'scheduler/base/long_task_tracker_unittest.cc',
'scheduler/base/queueing_time_estimator_unittest.cc',
'scheduler/base/task_queue_manager_delegate_for_test.cc',
'scheduler/base/task_queue_manager_unittest.cc',
'scheduler/base/task_queue_selector_unittest.cc',
'scheduler/base/test_count_uses_time_source.cc',
'scheduler/base/test_time_source.cc',
'scheduler/base/thread_load_tracker_unittest.cc',
'scheduler/base/time_domain_unittest.cc',
'scheduler/base/work_queue_sets_unittest.cc',
'scheduler/base/queueing_time_estimator_unittest.cc',
'scheduler/child/idle_helper_unittest.cc',
'scheduler/child/scheduler_helper_unittest.cc',
'scheduler/child/scheduler_tqm_delegate_for_test.cc',
Expand All @@ -1337,8 +1339,8 @@
'scheduler/renderer/task_cost_estimator_unittest.cc',
'scheduler/renderer/throttling_helper_unittest.cc',
'scheduler/renderer/user_model_unittest.cc',
'scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc',
'scheduler/renderer/web_view_scheduler_impl_unittest.cc',
'scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc',
'scroll/ScrollableAreaTest.cpp',
'testing/ArenaTestHelpers.h',
'testing/TreeTestHelpers.cpp',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "platform/scheduler/base/thread_load_tracker.h"

#include <algorithm>

namespace blink {
namespace scheduler {

namespace {

const int kLoadReportingIntervalInSeconds = 1;
const int kWaitingPeriodBeforeReportingInSeconds = 10;

} // namespace

ThreadLoadTracker::ThreadLoadTracker(base::TimeTicks now,
const Callback& callback)
: time_(now),
next_reporting_time_(now),
thread_state_(ThreadState::ACTIVE),
last_state_change_time_(now),
waiting_period_(
base::TimeDelta::FromSeconds(kWaitingPeriodBeforeReportingInSeconds)),
reporting_interval_(
base::TimeDelta::FromSeconds(kLoadReportingIntervalInSeconds)),
callback_(callback) {}

ThreadLoadTracker::~ThreadLoadTracker() {}

void ThreadLoadTracker::Pause(base::TimeTicks now) {
Advance(now, TaskState::IDLE);
thread_state_ = ThreadState::PAUSED;
last_state_change_time_ = now;
}

void ThreadLoadTracker::Resume(base::TimeTicks now) {
Advance(now, TaskState::IDLE);
thread_state_ = ThreadState::ACTIVE;
last_state_change_time_ = now;
}

void ThreadLoadTracker::RecordTaskTime(base::TimeTicks start_time,
base::TimeTicks end_time) {
start_time = std::max(last_state_change_time_, start_time);
end_time = std::max(last_state_change_time_, end_time);

Advance(start_time, TaskState::IDLE);
Advance(end_time, TaskState::TASK_RUNNING);
}

void ThreadLoadTracker::RecordIdle(base::TimeTicks now) {
Advance(now, TaskState::IDLE);
}

void ThreadLoadTracker::Advance(base::TimeTicks now, TaskState task_state) {
// This function advances |time_| to now and calls |callback_|
// when appropriate.
if (time_ > now) {
return;
}

if (thread_state_ == ThreadState::PAUSED) {
// If the load tracker is paused, bail out early.
time_ = now;
next_reporting_time_ = now + reporting_interval_;
return;
}

while (time_ < now) {
// Forward time_ to the earliest of following:
// a) time to call |callback_|
// b) requested time to forward (|now|).
base::TimeTicks next_current_time = std::min(next_reporting_time_, now);

base::TimeDelta delta = next_current_time - time_;

// Forward time and recalculate |total_time_| and |total_runtime_|.
if (thread_state_ == ThreadState::ACTIVE) {
total_time_ += delta;
if (task_state == TaskState::TASK_RUNNING) {
total_runtime_ += delta;
}
}
time_ = next_current_time;

if (time_ == next_reporting_time_) {
// Call |callback_| if need and update next callback time.
if (thread_state_ == ThreadState::ACTIVE &&
total_time_ >= waiting_period_) {
callback_.Run(time_, Load());
}
next_reporting_time_ += reporting_interval_;
}
}
}

double ThreadLoadTracker::Load() {
if (total_time_.is_zero()) {
return 0;
}
return total_runtime_.InSecondsF() / total_time_.InSecondsF();
}

} // namespace scheduler
} // namespace blink
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_RENDERER_RENDERER_LOAD_TRACKER_H_
#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_RENDERER_RENDERER_LOAD_TRACKER_H_

#include "base/callback.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "public/platform/WebCommon.h"

namespace blink {
namespace scheduler {

// This class tracks thread load level, i.e. percentage of wall time spent
// running tasks.
// In order to avoid bias it reports load level at regular intervals.
class BLINK_PLATFORM_EXPORT ThreadLoadTracker {
public:
// Callback is called with (current_time, load_level) parameters.
using Callback = base::Callback<void(base::TimeTicks, double)>;

ThreadLoadTracker(base::TimeTicks now, const Callback& callback);
~ThreadLoadTracker();

void Pause(base::TimeTicks now);
void Resume(base::TimeTicks now);

void RecordTaskTime(base::TimeTicks start_time, base::TimeTicks end_time);

void RecordIdle(base::TimeTicks now);

// TODO(altimin): Count wakeups.

private:
enum class ThreadState { ACTIVE, PAUSED };

enum class TaskState { TASK_RUNNING, IDLE };

// This function advances |time_| to |now|, calling |callback_|
// in the process (multiple times if needed).
void Advance(base::TimeTicks now, TaskState task_state);

double Load();

// |time_| is the last timestamp LoadTracker knows about.
base::TimeTicks time_;
base::TimeTicks next_reporting_time_;

ThreadState thread_state_;
base::TimeTicks last_state_change_time_;

base::TimeDelta total_time_;
base::TimeDelta total_runtime_;

// Start reporting values after |waiting_period_|.
base::TimeDelta waiting_period_;
base::TimeDelta reporting_interval_;

Callback callback_;

DISALLOW_COPY_AND_ASSIGN(ThreadLoadTracker);
};

} // namespace scheduler
} // namespace blink

#endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_RENDERER_RENDERER_LOAD_TRACKER_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "platform/scheduler/base/thread_load_tracker.h"

#include "base/bind.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace blink {
namespace scheduler {

namespace {

void AddToVector(std::vector<std::pair<base::TimeTicks, double>>* vector,
base::TimeTicks time,
double load) {
vector->push_back({time, load});
}

base::TimeTicks SecondsToTime(int seconds) {
return base::TimeTicks() + base::TimeDelta::FromSeconds(seconds);
}

double Divide(int a, int b) {
return static_cast<double>(a) / b;
}

} // namespace

TEST(ThreadLoadTrackerTest, RecordTasks) {
std::vector<std::pair<base::TimeTicks, double>> result;

ThreadLoadTracker thread_load_tracker(
SecondsToTime(1), base::Bind(&AddToVector, base::Unretained(&result)));

thread_load_tracker.RecordTaskTime(SecondsToTime(3), SecondsToTime(5));
thread_load_tracker.RecordTaskTime(SecondsToTime(7), SecondsToTime(9));
thread_load_tracker.RecordTaskTime(SecondsToTime(10), SecondsToTime(13));
thread_load_tracker.RecordTaskTime(SecondsToTime(15), SecondsToTime(18));

// Because of 10-second delay we're getting information starting with 11s.
std::vector<std::pair<base::TimeTicks, double>> expected_result{
{SecondsToTime(11), Divide(5, 10)}, {SecondsToTime(12), Divide(6, 11)},
{SecondsToTime(13), Divide(7, 12)}, {SecondsToTime(14), Divide(7, 13)},
{SecondsToTime(15), Divide(7, 14)}, {SecondsToTime(16), Divide(8, 15)},
{SecondsToTime(17), Divide(9, 16)}, {SecondsToTime(18), Divide(10, 17)}};

EXPECT_EQ(result, expected_result);
}

TEST(ThreadLoadTrackerTest, PauseAndResume) {
std::vector<std::pair<base::TimeTicks, double>> result;

ThreadLoadTracker thread_load_tracker(
SecondsToTime(1), base::Bind(&AddToVector, base::Unretained(&result)));

thread_load_tracker.RecordTaskTime(SecondsToTime(3), SecondsToTime(4));
thread_load_tracker.Pause(SecondsToTime(5));
// This task should be ignored.
thread_load_tracker.RecordTaskTime(SecondsToTime(10), SecondsToTime(11));
thread_load_tracker.Resume(SecondsToTime(15));
// We're counting only last second.
thread_load_tracker.RecordTaskTime(SecondsToTime(14), SecondsToTime(16));
thread_load_tracker.RecordTaskTime(SecondsToTime(22), SecondsToTime(23));

std::vector<std::pair<base::TimeTicks, double>> expected_result{
{SecondsToTime(21), Divide(2, 10)},
{SecondsToTime(22), Divide(2, 11)},
{SecondsToTime(23), Divide(3, 12)}};

EXPECT_EQ(result, expected_result);
}

} // namespace scheduler
} // namespace blink
Loading

0 comments on commit 3ab9c0b

Please sign in to comment.