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
16 namespace cc {
17 namespace {
18
19 static const int kTimeLimitMillis = 2000;
20 static const int kWarmupRuns = 5;
21 static const int kTimeCheckInterval = 10;
22
23 class PerfTaskImpl : public Task {
24 public:
25 typedef std::vector<scoped_refptr<PerfTaskImpl> > Vector;
26
PerfTaskImpl()27 PerfTaskImpl() {}
28
29 // Overridden from Task:
RunOnWorkerThread()30 virtual void RunOnWorkerThread() OVERRIDE {}
31
Reset()32 void Reset() { did_run_ = false; }
33
34 private:
~PerfTaskImpl()35 virtual ~PerfTaskImpl() {}
36
37 DISALLOW_COPY_AND_ASSIGN(PerfTaskImpl);
38 };
39
40 class TaskGraphRunnerPerfTest : public testing::Test {
41 public:
TaskGraphRunnerPerfTest()42 TaskGraphRunnerPerfTest()
43 : timer_(kWarmupRuns,
44 base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
45 kTimeCheckInterval) {}
46
47 // Overridden from testing::Test:
SetUp()48 virtual void SetUp() OVERRIDE {
49 task_graph_runner_ = make_scoped_ptr(new TaskGraphRunner);
50 namespace_token_ = task_graph_runner_->GetNamespaceToken();
51 }
TearDown()52 virtual void TearDown() OVERRIDE { task_graph_runner_.reset(); }
53
AfterTest(const std::string & test_name)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
RunBuildTaskGraphTest(const std::string & test_name,int num_top_level_tasks,int num_tasks,int num_leaf_tasks)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
RunScheduleTasksTest(const std::string & test_name,int num_top_level_tasks,int num_tasks,int num_leaf_tasks)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
RunScheduleAlternateTasksTest(const std::string & test_name,int num_top_level_tasks,int num_tasks,int num_leaf_tasks)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
RunScheduleAndExecuteTasksTest(const std::string & test_name,int num_top_level_tasks,int num_tasks,int num_leaf_tasks)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:
TestModifierString()213 static std::string TestModifierString() {
214 return std::string("_task_graph_runner");
215 }
216
CreateTasks(int num_tasks,PerfTaskImpl::Vector * tasks)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
ResetTasks(PerfTaskImpl::Vector * tasks)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
BuildTaskGraph(const PerfTaskImpl::Vector & top_level_tasks,const PerfTaskImpl::Vector & tasks,const PerfTaskImpl::Vector & leaf_tasks,TaskGraph * graph)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
CollectCompletedTasks(Task::Vector * completed_tasks)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
TEST_F(TaskGraphRunnerPerfTest,BuildTaskGraph)281 TEST_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
TEST_F(TaskGraphRunnerPerfTest,ScheduleTasks)290 TEST_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
TEST_F(TaskGraphRunnerPerfTest,ScheduleAlternateTasks)299 TEST_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
TEST_F(TaskGraphRunnerPerfTest,ScheduleAndExecuteTasks)308 TEST_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