1// Copyright 2014 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/task_graph_runner.h"
6
7#include <vector>
8
9#include "base/memory/scoped_ptr.h"
10#include "base/time/time.h"
11#include "cc/base/completion_event.h"
12#include "cc/debug/lap_timer.h"
13#include "testing/gtest/include/gtest/gtest.h"
14#include "testing/perf/perf_test.h"
15
16namespace cc {
17namespace {
18
19static const int kTimeLimitMillis = 2000;
20static const int kWarmupRuns = 5;
21static const int kTimeCheckInterval = 10;
22
23class PerfTaskImpl : public Task {
24 public:
25  typedef std::vector<scoped_refptr<PerfTaskImpl> > Vector;
26
27  PerfTaskImpl() {}
28
29  // Overridden from Task:
30  virtual void RunOnWorkerThread() OVERRIDE {}
31
32  void Reset() { did_run_ = false; }
33
34 private:
35  virtual ~PerfTaskImpl() {}
36
37  DISALLOW_COPY_AND_ASSIGN(PerfTaskImpl);
38};
39
40class TaskGraphRunnerPerfTest : public testing::Test {
41 public:
42  TaskGraphRunnerPerfTest()
43      : timer_(kWarmupRuns,
44               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
45               kTimeCheckInterval) {}
46
47  // Overridden from testing::Test:
48  virtual void SetUp() OVERRIDE {
49    task_graph_runner_ = make_scoped_ptr(new TaskGraphRunner);
50    namespace_token_ = task_graph_runner_->GetNamespaceToken();
51  }
52  virtual void TearDown() OVERRIDE { task_graph_runner_.reset(); }
53
54  void AfterTest(const std::string& test_name) {
55    // Format matches chrome/test/perf/perf_test.h:PrintResult
56    printf(
57        "*RESULT %s: %.2f runs/s\n", test_name.c_str(), timer_.LapsPerSecond());
58  }
59
60  void RunBuildTaskGraphTest(const std::string& test_name,
61                             int num_top_level_tasks,
62                             int num_tasks,
63                             int num_leaf_tasks) {
64    PerfTaskImpl::Vector top_level_tasks;
65    PerfTaskImpl::Vector tasks;
66    PerfTaskImpl::Vector leaf_tasks;
67    CreateTasks(num_top_level_tasks, &top_level_tasks);
68    CreateTasks(num_tasks, &tasks);
69    CreateTasks(num_leaf_tasks, &leaf_tasks);
70
71    // Avoid unnecessary heap allocations by reusing the same graph.
72    TaskGraph graph;
73
74    timer_.Reset();
75    do {
76      graph.Reset();
77      BuildTaskGraph(top_level_tasks, tasks, leaf_tasks, &graph);
78      timer_.NextLap();
79    } while (!timer_.HasTimeLimitExpired());
80
81    perf_test::PrintResult("build_task_graph",
82                           TestModifierString(),
83                           test_name,
84                           timer_.LapsPerSecond(),
85                           "runs/s",
86                           true);
87  }
88
89  void RunScheduleTasksTest(const std::string& test_name,
90                            int num_top_level_tasks,
91                            int num_tasks,
92                            int num_leaf_tasks) {
93    PerfTaskImpl::Vector top_level_tasks;
94    PerfTaskImpl::Vector tasks;
95    PerfTaskImpl::Vector leaf_tasks;
96    CreateTasks(num_top_level_tasks, &top_level_tasks);
97    CreateTasks(num_tasks, &tasks);
98    CreateTasks(num_leaf_tasks, &leaf_tasks);
99
100    // Avoid unnecessary heap allocations by reusing the same graph and
101    // completed tasks vector.
102    TaskGraph graph;
103    Task::Vector completed_tasks;
104
105    timer_.Reset();
106    do {
107      graph.Reset();
108      BuildTaskGraph(top_level_tasks, tasks, leaf_tasks, &graph);
109      task_graph_runner_->ScheduleTasks(namespace_token_, &graph);
110      // Shouldn't be any tasks to collect as we reschedule the same set
111      // of tasks.
112      DCHECK_EQ(0u, CollectCompletedTasks(&completed_tasks));
113      timer_.NextLap();
114    } while (!timer_.HasTimeLimitExpired());
115
116    TaskGraph empty;
117    task_graph_runner_->ScheduleTasks(namespace_token_, &empty);
118    CollectCompletedTasks(&completed_tasks);
119
120    perf_test::PrintResult("schedule_tasks",
121                           TestModifierString(),
122                           test_name,
123                           timer_.LapsPerSecond(),
124                           "runs/s",
125                           true);
126  }
127
128  void RunScheduleAlternateTasksTest(const std::string& test_name,
129                                     int num_top_level_tasks,
130                                     int num_tasks,
131                                     int num_leaf_tasks) {
132    const size_t kNumVersions = 2;
133    PerfTaskImpl::Vector top_level_tasks[kNumVersions];
134    PerfTaskImpl::Vector tasks[kNumVersions];
135    PerfTaskImpl::Vector leaf_tasks[kNumVersions];
136    for (size_t i = 0; i < kNumVersions; ++i) {
137      CreateTasks(num_top_level_tasks, &top_level_tasks[i]);
138      CreateTasks(num_tasks, &tasks[i]);
139      CreateTasks(num_leaf_tasks, &leaf_tasks[i]);
140    }
141
142    // Avoid unnecessary heap allocations by reusing the same graph and
143    // completed tasks vector.
144    TaskGraph graph;
145    Task::Vector completed_tasks;
146
147    size_t count = 0;
148    timer_.Reset();
149    do {
150      graph.Reset();
151      BuildTaskGraph(top_level_tasks[count % kNumVersions],
152                     tasks[count % kNumVersions],
153                     leaf_tasks[count % kNumVersions],
154                     &graph);
155      task_graph_runner_->ScheduleTasks(namespace_token_, &graph);
156      CollectCompletedTasks(&completed_tasks);
157      completed_tasks.clear();
158      ++count;
159      timer_.NextLap();
160    } while (!timer_.HasTimeLimitExpired());
161
162    TaskGraph empty;
163    task_graph_runner_->ScheduleTasks(namespace_token_, &empty);
164    CollectCompletedTasks(&completed_tasks);
165
166    perf_test::PrintResult("schedule_alternate_tasks",
167                           TestModifierString(),
168                           test_name,
169                           timer_.LapsPerSecond(),
170                           "runs/s",
171                           true);
172  }
173
174  void RunScheduleAndExecuteTasksTest(const std::string& test_name,
175                                      int num_top_level_tasks,
176                                      int num_tasks,
177                                      int num_leaf_tasks) {
178    PerfTaskImpl::Vector top_level_tasks;
179    PerfTaskImpl::Vector tasks;
180    PerfTaskImpl::Vector leaf_tasks;
181    CreateTasks(num_top_level_tasks, &top_level_tasks);
182    CreateTasks(num_tasks, &tasks);
183    CreateTasks(num_leaf_tasks, &leaf_tasks);
184
185    // Avoid unnecessary heap allocations by reusing the same graph and
186    // completed tasks vector.
187    TaskGraph graph;
188    Task::Vector completed_tasks;
189
190    timer_.Reset();
191    do {
192      graph.Reset();
193      BuildTaskGraph(top_level_tasks, tasks, leaf_tasks, &graph);
194      task_graph_runner_->ScheduleTasks(namespace_token_, &graph);
195      task_graph_runner_->RunUntilIdle();
196      CollectCompletedTasks(&completed_tasks);
197      completed_tasks.clear();
198      ResetTasks(&top_level_tasks);
199      ResetTasks(&tasks);
200      ResetTasks(&leaf_tasks);
201      timer_.NextLap();
202    } while (!timer_.HasTimeLimitExpired());
203
204    perf_test::PrintResult("execute_tasks",
205                           TestModifierString(),
206                           test_name,
207                           timer_.LapsPerSecond(),
208                           "runs/s",
209                           true);
210  }
211
212 private:
213  static std::string TestModifierString() {
214    return std::string("_task_graph_runner");
215  }
216
217  void CreateTasks(int num_tasks, PerfTaskImpl::Vector* tasks) {
218    for (int i = 0; i < num_tasks; ++i)
219      tasks->push_back(make_scoped_refptr(new PerfTaskImpl));
220  }
221
222  void ResetTasks(PerfTaskImpl::Vector* tasks) {
223    for (PerfTaskImpl::Vector::iterator it = tasks->begin(); it != tasks->end();
224         ++it) {
225      PerfTaskImpl* task = it->get();
226      task->Reset();
227    }
228  }
229
230  void BuildTaskGraph(const PerfTaskImpl::Vector& top_level_tasks,
231                      const PerfTaskImpl::Vector& tasks,
232                      const PerfTaskImpl::Vector& leaf_tasks,
233                      TaskGraph* graph) {
234    DCHECK(graph->nodes.empty());
235    DCHECK(graph->edges.empty());
236
237    for (PerfTaskImpl::Vector::const_iterator it = leaf_tasks.begin();
238         it != leaf_tasks.end();
239         ++it) {
240      graph->nodes.push_back(TaskGraph::Node(it->get(), 0u, 0u));
241    }
242
243    for (PerfTaskImpl::Vector::const_iterator it = tasks.begin();
244         it != tasks.end();
245         ++it) {
246      graph->nodes.push_back(TaskGraph::Node(it->get(), 0u, leaf_tasks.size()));
247
248      for (PerfTaskImpl::Vector::const_iterator leaf_it = leaf_tasks.begin();
249           leaf_it != leaf_tasks.end();
250           ++leaf_it) {
251        graph->edges.push_back(TaskGraph::Edge(leaf_it->get(), it->get()));
252      }
253
254      for (PerfTaskImpl::Vector::const_iterator top_level_it =
255               top_level_tasks.begin();
256           top_level_it != top_level_tasks.end();
257           ++top_level_it) {
258        graph->edges.push_back(TaskGraph::Edge(it->get(), top_level_it->get()));
259      }
260    }
261
262    for (PerfTaskImpl::Vector::const_iterator it = top_level_tasks.begin();
263         it != top_level_tasks.end();
264         ++it) {
265      graph->nodes.push_back(TaskGraph::Node(it->get(), 0u, tasks.size()));
266    }
267  }
268
269  size_t CollectCompletedTasks(Task::Vector* completed_tasks) {
270    DCHECK(completed_tasks->empty());
271    task_graph_runner_->CollectCompletedTasks(namespace_token_,
272                                              completed_tasks);
273    return completed_tasks->size();
274  }
275
276  scoped_ptr<TaskGraphRunner> task_graph_runner_;
277  NamespaceToken namespace_token_;
278  LapTimer timer_;
279};
280
281TEST_F(TaskGraphRunnerPerfTest, BuildTaskGraph) {
282  RunBuildTaskGraphTest("0_1_0", 0, 1, 0);
283  RunBuildTaskGraphTest("0_32_0", 0, 32, 0);
284  RunBuildTaskGraphTest("2_1_0", 2, 1, 0);
285  RunBuildTaskGraphTest("2_32_0", 2, 32, 0);
286  RunBuildTaskGraphTest("2_1_1", 2, 1, 1);
287  RunBuildTaskGraphTest("2_32_1", 2, 32, 1);
288}
289
290TEST_F(TaskGraphRunnerPerfTest, ScheduleTasks) {
291  RunScheduleTasksTest("0_1_0", 0, 1, 0);
292  RunScheduleTasksTest("0_32_0", 0, 32, 0);
293  RunScheduleTasksTest("2_1_0", 2, 1, 0);
294  RunScheduleTasksTest("2_32_0", 2, 32, 0);
295  RunScheduleTasksTest("2_1_1", 2, 1, 1);
296  RunScheduleTasksTest("2_32_1", 2, 32, 1);
297}
298
299TEST_F(TaskGraphRunnerPerfTest, ScheduleAlternateTasks) {
300  RunScheduleAlternateTasksTest("0_1_0", 0, 1, 0);
301  RunScheduleAlternateTasksTest("0_32_0", 0, 32, 0);
302  RunScheduleAlternateTasksTest("2_1_0", 2, 1, 0);
303  RunScheduleAlternateTasksTest("2_32_0", 2, 32, 0);
304  RunScheduleAlternateTasksTest("2_1_1", 2, 1, 1);
305  RunScheduleAlternateTasksTest("2_32_1", 2, 32, 1);
306}
307
308TEST_F(TaskGraphRunnerPerfTest, ScheduleAndExecuteTasks) {
309  RunScheduleAndExecuteTasksTest("0_1_0", 0, 1, 0);
310  RunScheduleAndExecuteTasksTest("0_32_0", 0, 32, 0);
311  RunScheduleAndExecuteTasksTest("2_1_0", 2, 1, 0);
312  RunScheduleAndExecuteTasksTest("2_32_0", 2, 32, 0);
313  RunScheduleAndExecuteTasksTest("2_1_1", 2, 1, 1);
314  RunScheduleAndExecuteTasksTest("2_32_1", 2, 32, 1);
315}
316
317}  // namespace
318}  // namespace cc
319