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