1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cc/resources/worker_pool.h"
6
7#include "base/time/time.h"
8#include "cc/base/completion_event.h"
9#include "testing/gtest/include/gtest/gtest.h"
10
11namespace cc {
12
13namespace {
14
15static const int kTimeLimitMillis = 2000;
16static const int kWarmupRuns = 5;
17static const int kTimeCheckInterval = 10;
18
19class PerfWorkerPoolTaskImpl : public internal::WorkerPoolTask {
20 public:
21  // Overridden from internal::WorkerPoolTask:
22  virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {}
23  virtual void CompleteOnOriginThread() OVERRIDE {}
24
25 private:
26  virtual ~PerfWorkerPoolTaskImpl() {}
27};
28
29class PerfControlWorkerPoolTaskImpl : public internal::WorkerPoolTask {
30 public:
31  PerfControlWorkerPoolTaskImpl() : did_start_(new CompletionEvent),
32                                    can_finish_(new CompletionEvent) {}
33
34  // Overridden from internal::WorkerPoolTask:
35  virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
36    did_start_->Signal();
37    can_finish_->Wait();
38  }
39  virtual void CompleteOnOriginThread() OVERRIDE {}
40
41  void WaitForTaskToStartRunning() {
42    did_start_->Wait();
43  }
44
45  void AllowTaskToFinish() {
46    can_finish_->Signal();
47  }
48
49 private:
50  virtual ~PerfControlWorkerPoolTaskImpl() {}
51
52  scoped_ptr<CompletionEvent> did_start_;
53  scoped_ptr<CompletionEvent> can_finish_;
54
55  DISALLOW_COPY_AND_ASSIGN(PerfControlWorkerPoolTaskImpl);
56};
57
58class PerfWorkerPool : public WorkerPool {
59 public:
60  PerfWorkerPool() : WorkerPool(1, "test") {}
61  virtual ~PerfWorkerPool() {}
62
63  static scoped_ptr<PerfWorkerPool> Create() {
64    return make_scoped_ptr(new PerfWorkerPool);
65  }
66
67  void ScheduleTasks(internal::WorkerPoolTask* root_task,
68                     internal::WorkerPoolTask* leaf_task,
69                     unsigned max_depth,
70                     unsigned num_children_per_node) {
71    TaskVector tasks;
72    TaskGraph graph;
73
74    scoped_ptr<internal::GraphNode> root_node;
75    if (root_task)
76      root_node = make_scoped_ptr(new internal::GraphNode(root_task, 0u));
77
78    scoped_ptr<internal::GraphNode> leaf_node;
79    if (leaf_task)
80      leaf_node = make_scoped_ptr(new internal::GraphNode(leaf_task, 0u));
81
82    if (max_depth) {
83      BuildTaskGraph(&tasks,
84                     &graph,
85                     root_node.get(),
86                     leaf_node.get(),
87                     0,
88                     max_depth,
89                     num_children_per_node);
90    }
91
92    if (leaf_node)
93      graph.set(leaf_task, leaf_node.Pass());
94
95    if (root_node)
96      graph.set(root_task, root_node.Pass());
97
98    SetTaskGraph(&graph);
99
100    tasks_.swap(tasks);
101  }
102
103 private:
104  typedef std::vector<scoped_refptr<internal::WorkerPoolTask> > TaskVector;
105
106  void BuildTaskGraph(TaskVector* tasks,
107                      TaskGraph* graph,
108                      internal::GraphNode* dependent_node,
109                      internal::GraphNode* leaf_node,
110                      unsigned current_depth,
111                      unsigned max_depth,
112                      unsigned num_children_per_node) {
113    scoped_refptr<PerfWorkerPoolTaskImpl> task(new PerfWorkerPoolTaskImpl);
114    scoped_ptr<internal::GraphNode> node(
115        new internal::GraphNode(task.get(), 0u));
116
117    if (current_depth < max_depth) {
118      for (unsigned i = 0; i < num_children_per_node; ++i) {
119        BuildTaskGraph(tasks,
120                       graph,
121                       node.get(),
122                       leaf_node,
123                       current_depth + 1,
124                       max_depth,
125                       num_children_per_node);
126      }
127    } else if (leaf_node) {
128      leaf_node->add_dependent(node.get());
129      node->add_dependency();
130    }
131
132    if (dependent_node) {
133      node->add_dependent(dependent_node);
134      dependent_node->add_dependency();
135    }
136    graph->set(task.get(), node.Pass());
137    tasks->push_back(task.get());
138  }
139
140  TaskVector tasks_;
141
142  DISALLOW_COPY_AND_ASSIGN(PerfWorkerPool);
143};
144
145class WorkerPoolPerfTest : public testing::Test {
146 public:
147  WorkerPoolPerfTest() : num_runs_(0) {}
148
149  // Overridden from testing::Test:
150  virtual void SetUp() OVERRIDE {
151    worker_pool_ = PerfWorkerPool::Create();
152  }
153  virtual void TearDown() OVERRIDE {
154    worker_pool_->Shutdown();
155    worker_pool_->CheckForCompletedTasks();
156  }
157
158  void EndTest() {
159    elapsed_ = base::TimeTicks::HighResNow() - start_time_;
160  }
161
162  void AfterTest(const std::string test_name) {
163    // Format matches chrome/test/perf/perf_test.h:PrintResult
164    printf("*RESULT %s: %.2f runs/s\n",
165           test_name.c_str(),
166           num_runs_ / elapsed_.InSecondsF());
167  }
168
169  bool DidRun() {
170    ++num_runs_;
171    if (num_runs_ == kWarmupRuns)
172      start_time_ = base::TimeTicks::HighResNow();
173
174    if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
175      base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
176      if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
177        elapsed_ = elapsed;
178        return false;
179      }
180    }
181
182    return true;
183  }
184
185  void RunScheduleTasksTest(const std::string test_name,
186                            unsigned max_depth,
187                            unsigned num_children_per_node) {
188    start_time_ = base::TimeTicks();
189    num_runs_ = 0;
190    do {
191      scoped_refptr<PerfControlWorkerPoolTaskImpl> leaf_task(
192          new PerfControlWorkerPoolTaskImpl);
193      worker_pool_->ScheduleTasks(
194          NULL, leaf_task.get(), max_depth, num_children_per_node);
195      leaf_task->WaitForTaskToStartRunning();
196      worker_pool_->ScheduleTasks(NULL, NULL, 0, 0);
197      worker_pool_->CheckForCompletedTasks();
198      leaf_task->AllowTaskToFinish();
199    } while (DidRun());
200
201    AfterTest(test_name);
202  }
203
204  void RunExecuteTasksTest(const std::string test_name,
205                           unsigned max_depth,
206                           unsigned num_children_per_node) {
207    start_time_ = base::TimeTicks();
208    num_runs_ = 0;
209    do {
210      scoped_refptr<PerfControlWorkerPoolTaskImpl> root_task(
211          new PerfControlWorkerPoolTaskImpl);
212      worker_pool_->ScheduleTasks(
213          root_task.get(), NULL, max_depth, num_children_per_node);
214      root_task->WaitForTaskToStartRunning();
215      root_task->AllowTaskToFinish();
216      worker_pool_->CheckForCompletedTasks();
217    } while (DidRun());
218
219    AfterTest(test_name);
220  }
221
222 protected:
223  scoped_ptr<PerfWorkerPool> worker_pool_;
224  base::TimeTicks start_time_;
225  base::TimeDelta elapsed_;
226  int num_runs_;
227};
228
229TEST_F(WorkerPoolPerfTest, ScheduleTasks) {
230  RunScheduleTasksTest("schedule_tasks_1_10", 1, 10);
231  RunScheduleTasksTest("schedule_tasks_1_1000", 1, 1000);
232  RunScheduleTasksTest("schedule_tasks_2_10", 2, 10);
233  RunScheduleTasksTest("schedule_tasks_5_5", 5, 5);
234  RunScheduleTasksTest("schedule_tasks_10_2", 10, 2);
235  RunScheduleTasksTest("schedule_tasks_1000_1", 1000, 1);
236  RunScheduleTasksTest("schedule_tasks_10_1", 10, 1);
237}
238
239TEST_F(WorkerPoolPerfTest, ExecuteTasks) {
240  RunExecuteTasksTest("execute_tasks_1_10", 1, 10);
241  RunExecuteTasksTest("execute_tasks_1_1000", 1, 1000);
242  RunExecuteTasksTest("execute_tasks_2_10", 2, 10);
243  RunExecuteTasksTest("execute_tasks_5_5", 5, 5);
244  RunExecuteTasksTest("execute_tasks_10_2", 10, 2);
245  RunExecuteTasksTest("execute_tasks_1000_1", 1000, 1);
246  RunExecuteTasksTest("execute_tasks_10_1", 10, 1);
247}
248
249}  // namespace
250
251}  // namespace cc
252