// Copyright 2013 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 "cc/resources/worker_pool.h"
#include "base/time/time.h"
#include "cc/base/completion_event.h"
#include "cc/test/lap_timer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_test.h"
namespace cc {
namespace {
static const int kTimeLimitMillis = 2000;
static const int kWarmupRuns = 5;
static const int kTimeCheckInterval = 10;
class PerfWorkerPoolTaskImpl : public internal::WorkerPoolTask {
public:
// Overridden from internal::WorkerPoolTask:
virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {}
virtual void CompleteOnOriginThread() OVERRIDE {}
private:
virtual ~PerfWorkerPoolTaskImpl() {}
};
class PerfControlWorkerPoolTaskImpl : public internal::WorkerPoolTask {
public:
PerfControlWorkerPoolTaskImpl() : did_start_(new CompletionEvent),
can_finish_(new CompletionEvent) {}
// Overridden from internal::WorkerPoolTask:
virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
did_start_->Signal();
can_finish_->Wait();
}
virtual void CompleteOnOriginThread() OVERRIDE {}
void WaitForTaskToStartRunning() {
did_start_->Wait();
}
void AllowTaskToFinish() {
can_finish_->Signal();
}
private:
virtual ~PerfControlWorkerPoolTaskImpl() {}
scoped_ptr<CompletionEvent> did_start_;
scoped_ptr<CompletionEvent> can_finish_;
DISALLOW_COPY_AND_ASSIGN(PerfControlWorkerPoolTaskImpl);
};
class PerfWorkerPool : public WorkerPool {
public:
PerfWorkerPool() : WorkerPool(1, "test") {}
virtual ~PerfWorkerPool() {}
static scoped_ptr<PerfWorkerPool> Create() {
return make_scoped_ptr(new PerfWorkerPool);
}
void ScheduleTasks(internal::WorkerPoolTask* root_task,
internal::WorkerPoolTask* leaf_task,
unsigned max_depth,
unsigned num_children_per_node) {
TaskVector tasks;
TaskGraph graph;
scoped_ptr<internal::GraphNode> root_node;
if (root_task)
root_node = make_scoped_ptr(new internal::GraphNode(root_task, 0u));
scoped_ptr<internal::GraphNode> leaf_node;
if (leaf_task)
leaf_node = make_scoped_ptr(new internal::GraphNode(leaf_task, 0u));
if (max_depth) {
BuildTaskGraph(&tasks,
&graph,
root_node.get(),
leaf_node.get(),
0,
max_depth,
num_children_per_node);
}
if (leaf_node)
graph.set(leaf_task, leaf_node.Pass());
if (root_node)
graph.set(root_task, root_node.Pass());
SetTaskGraph(&graph);
tasks_.swap(tasks);
}
private:
typedef std::vector<scoped_refptr<internal::WorkerPoolTask> > TaskVector;
void BuildTaskGraph(TaskVector* tasks,
TaskGraph* graph,
internal::GraphNode* dependent_node,
internal::GraphNode* leaf_node,
unsigned current_depth,
unsigned max_depth,
unsigned num_children_per_node) {
scoped_refptr<PerfWorkerPoolTaskImpl> task(new PerfWorkerPoolTaskImpl);
scoped_ptr<internal::GraphNode> node(
new internal::GraphNode(task.get(), 0u));
if (current_depth < max_depth) {
for (unsigned i = 0; i < num_children_per_node; ++i) {
BuildTaskGraph(tasks,
graph,
node.get(),
leaf_node,
current_depth + 1,
max_depth,
num_children_per_node);
}
} else if (leaf_node) {
leaf_node->add_dependent(node.get());
node->add_dependency();
}
if (dependent_node) {
node->add_dependent(dependent_node);
dependent_node->add_dependency();
}
graph->set(task.get(), node.Pass());
tasks->push_back(task.get());
}
TaskVector tasks_;
DISALLOW_COPY_AND_ASSIGN(PerfWorkerPool);
};
class WorkerPoolPerfTest : public testing::Test {
public:
WorkerPoolPerfTest()
: timer_(kWarmupRuns,
base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
kTimeCheckInterval) {}
// Overridden from testing::Test:
virtual void SetUp() OVERRIDE {
worker_pool_ = PerfWorkerPool::Create();
}
virtual void TearDown() OVERRIDE {
worker_pool_->Shutdown();
worker_pool_->CheckForCompletedTasks();
}
void AfterTest(const std::string& test_name) {
// Format matches chrome/test/perf/perf_test.h:PrintResult
printf(
"*RESULT %s: %.2f runs/s\n", test_name.c_str(), timer_.LapsPerSecond());
}
void RunScheduleTasksTest(const std::string& test_name,
unsigned max_depth,
unsigned num_children_per_node) {
timer_.Reset();
do {
scoped_refptr<PerfControlWorkerPoolTaskImpl> leaf_task(
new PerfControlWorkerPoolTaskImpl);
worker_pool_->ScheduleTasks(
NULL, leaf_task.get(), max_depth, num_children_per_node);
leaf_task->WaitForTaskToStartRunning();
worker_pool_->ScheduleTasks(NULL, NULL, 0, 0);
worker_pool_->CheckForCompletedTasks();
leaf_task->AllowTaskToFinish();
timer_.NextLap();
} while (!timer_.HasTimeLimitExpired());
perf_test::PrintResult("schedule_tasks", "", test_name,
timer_.LapsPerSecond(), "runs/s", true);
}
void RunExecuteTasksTest(const std::string& test_name,
unsigned max_depth,
unsigned num_children_per_node) {
timer_.Reset();
do {
scoped_refptr<PerfControlWorkerPoolTaskImpl> root_task(
new PerfControlWorkerPoolTaskImpl);
worker_pool_->ScheduleTasks(
root_task.get(), NULL, max_depth, num_children_per_node);
root_task->WaitForTaskToStartRunning();
root_task->AllowTaskToFinish();
worker_pool_->CheckForCompletedTasks();
timer_.NextLap();
} while (!timer_.HasTimeLimitExpired());
perf_test::PrintResult("execute_tasks", "", test_name,
timer_.LapsPerSecond(), "runs/s", true);
}
protected:
scoped_ptr<PerfWorkerPool> worker_pool_;
LapTimer timer_;
};
TEST_F(WorkerPoolPerfTest, ScheduleTasks) {
RunScheduleTasksTest("1_10", 1, 10);
RunScheduleTasksTest("1_1000", 1, 1000);
RunScheduleTasksTest("2_10", 2, 10);
RunScheduleTasksTest("5_5", 5, 5);
RunScheduleTasksTest("10_2", 10, 2);
RunScheduleTasksTest("1000_1", 1000, 1);
RunScheduleTasksTest("10_1", 10, 1);
}
TEST_F(WorkerPoolPerfTest, ExecuteTasks) {
RunExecuteTasksTest("1_10", 1, 10);
RunExecuteTasksTest("1_1000", 1, 1000);
RunExecuteTasksTest("2_10", 2, 10);
RunExecuteTasksTest("5_5", 5, 5);
RunExecuteTasksTest("10_2", 10, 2);
RunExecuteTasksTest("1000_1", 1000, 1);
RunExecuteTasksTest("10_1", 10, 1);
}
} // namespace
} // namespace cc