Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / base / message_loop / message_pump_perftest.cc
blob9f76064ce38e4c586dcb0db1fd5bd1b406ec5da7
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.
5 #include "base/bind.h"
6 #include "base/format_macros.h"
7 #include "base/memory/scoped_vector.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/synchronization/condition_variable.h"
10 #include "base/synchronization/lock.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread.h"
13 #include "base/time/time.h"
14 #include "build/build_config.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "testing/perf/perf_test.h"
18 #if defined(OS_ANDROID)
19 #include "base/android/java_handler_thread.h"
20 #endif
22 namespace base {
24 class ScheduleWorkTest : public testing::Test {
25 public:
26 ScheduleWorkTest() : counter_(0) {}
28 void Increment(uint64_t amount) { counter_ += amount; }
30 void Schedule(int index) {
31 base::TimeTicks start = base::TimeTicks::Now();
32 base::ThreadTicks thread_start;
33 if (ThreadTicks::IsSupported())
34 thread_start = base::ThreadTicks::Now();
35 base::TimeDelta minimum = base::TimeDelta::Max();
36 base::TimeDelta maximum = base::TimeDelta();
37 base::TimeTicks now, lastnow = start;
38 uint64_t schedule_calls = 0u;
39 do {
40 for (size_t i = 0; i < kBatchSize; ++i) {
41 target_message_loop()->ScheduleWork();
42 schedule_calls++;
44 now = base::TimeTicks::Now();
45 base::TimeDelta laptime = now - lastnow;
46 lastnow = now;
47 minimum = std::min(minimum, laptime);
48 maximum = std::max(maximum, laptime);
49 } while (now - start < base::TimeDelta::FromSeconds(kTargetTimeSec));
51 scheduling_times_[index] = now - start;
52 if (ThreadTicks::IsSupported())
53 scheduling_thread_times_[index] =
54 base::ThreadTicks::Now() - thread_start;
55 min_batch_times_[index] = minimum;
56 max_batch_times_[index] = maximum;
57 target_message_loop()->PostTask(FROM_HERE,
58 base::Bind(&ScheduleWorkTest::Increment,
59 base::Unretained(this),
60 schedule_calls));
63 void ScheduleWork(MessageLoop::Type target_type, int num_scheduling_threads) {
64 #if defined(OS_ANDROID)
65 if (target_type == MessageLoop::TYPE_JAVA) {
66 java_thread_.reset(new android::JavaHandlerThread("target"));
67 java_thread_->Start();
68 } else
69 #endif
71 target_.reset(new Thread("target"));
72 target_->StartWithOptions(Thread::Options(target_type, 0u));
74 // Without this, it's possible for the scheduling threads to start and run
75 // before the target thread. In this case, the scheduling threads will
76 // call target_message_loop()->ScheduleWork(), which dereferences the
77 // loop's message pump, which is only created after the target thread has
78 // finished starting.
79 target_->WaitUntilThreadStarted();
82 ScopedVector<Thread> scheduling_threads;
83 scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]);
84 scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]);
85 min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
86 max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
88 for (int i = 0; i < num_scheduling_threads; ++i) {
89 scheduling_threads.push_back(new Thread("posting thread"));
90 scheduling_threads[i]->Start();
93 for (int i = 0; i < num_scheduling_threads; ++i) {
94 scheduling_threads[i]->message_loop()->PostTask(
95 FROM_HERE,
96 base::Bind(&ScheduleWorkTest::Schedule, base::Unretained(this), i));
99 for (int i = 0; i < num_scheduling_threads; ++i) {
100 scheduling_threads[i]->Stop();
102 #if defined(OS_ANDROID)
103 if (target_type == MessageLoop::TYPE_JAVA) {
104 java_thread_->Stop();
105 java_thread_.reset();
106 } else
107 #endif
109 target_->Stop();
110 target_.reset();
112 base::TimeDelta total_time;
113 base::TimeDelta total_thread_time;
114 base::TimeDelta min_batch_time = base::TimeDelta::Max();
115 base::TimeDelta max_batch_time = base::TimeDelta();
116 for (int i = 0; i < num_scheduling_threads; ++i) {
117 total_time += scheduling_times_[i];
118 total_thread_time += scheduling_thread_times_[i];
119 min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
120 max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
122 std::string trace = StringPrintf(
123 "%d_threads_scheduling_to_%s_pump",
124 num_scheduling_threads,
125 target_type == MessageLoop::TYPE_IO
126 ? "io"
127 : (target_type == MessageLoop::TYPE_UI ? "ui" : "default"));
128 perf_test::PrintResult(
129 "task",
131 trace,
132 total_time.InMicroseconds() / static_cast<double>(counter_),
133 "us/task",
134 true);
135 perf_test::PrintResult(
136 "task",
137 "_min_batch_time",
138 trace,
139 min_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
140 "us/task",
141 false);
142 perf_test::PrintResult(
143 "task",
144 "_max_batch_time",
145 trace,
146 max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
147 "us/task",
148 false);
149 if (ThreadTicks::IsSupported()) {
150 perf_test::PrintResult(
151 "task",
152 "_thread_time",
153 trace,
154 total_thread_time.InMicroseconds() / static_cast<double>(counter_),
155 "us/task",
156 true);
160 MessageLoop* target_message_loop() {
161 #if defined(OS_ANDROID)
162 if (java_thread_)
163 return java_thread_->message_loop();
164 #endif
165 return target_->message_loop();
168 private:
169 scoped_ptr<Thread> target_;
170 #if defined(OS_ANDROID)
171 scoped_ptr<android::JavaHandlerThread> java_thread_;
172 #endif
173 scoped_ptr<base::TimeDelta[]> scheduling_times_;
174 scoped_ptr<base::TimeDelta[]> scheduling_thread_times_;
175 scoped_ptr<base::TimeDelta[]> min_batch_times_;
176 scoped_ptr<base::TimeDelta[]> max_batch_times_;
177 uint64_t counter_;
179 static const size_t kTargetTimeSec = 5;
180 static const size_t kBatchSize = 1000;
183 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
184 ScheduleWork(MessageLoop::TYPE_IO, 1);
187 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
188 ScheduleWork(MessageLoop::TYPE_IO, 2);
191 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
192 ScheduleWork(MessageLoop::TYPE_IO, 4);
195 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
196 ScheduleWork(MessageLoop::TYPE_UI, 1);
199 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
200 ScheduleWork(MessageLoop::TYPE_UI, 2);
203 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
204 ScheduleWork(MessageLoop::TYPE_UI, 4);
207 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
208 ScheduleWork(MessageLoop::TYPE_DEFAULT, 1);
211 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
212 ScheduleWork(MessageLoop::TYPE_DEFAULT, 2);
215 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
216 ScheduleWork(MessageLoop::TYPE_DEFAULT, 4);
219 #if defined(OS_ANDROID)
220 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
221 ScheduleWork(MessageLoop::TYPE_JAVA, 1);
224 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
225 ScheduleWork(MessageLoop::TYPE_JAVA, 2);
228 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
229 ScheduleWork(MessageLoop::TYPE_JAVA, 4);
231 #endif
233 class FakeMessagePump : public MessagePump {
234 public:
235 FakeMessagePump() {}
236 ~FakeMessagePump() override {}
238 void Run(Delegate* delegate) override {}
240 void Quit() override {}
241 void ScheduleWork() override {}
242 void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override {}
245 class PostTaskTest : public testing::Test {
246 public:
247 void Run(int batch_size, int tasks_per_reload) {
248 base::TimeTicks start = base::TimeTicks::Now();
249 base::TimeTicks now;
250 MessageLoop loop(scoped_ptr<MessagePump>(new FakeMessagePump));
251 scoped_refptr<internal::IncomingTaskQueue> queue(
252 new internal::IncomingTaskQueue(&loop));
253 uint32_t num_posted = 0;
254 do {
255 for (int i = 0; i < batch_size; ++i) {
256 for (int j = 0; j < tasks_per_reload; ++j) {
257 queue->AddToIncomingQueue(
258 FROM_HERE, base::Bind(&DoNothing), base::TimeDelta(), false);
259 num_posted++;
261 TaskQueue loop_local_queue;
262 queue->ReloadWorkQueue(&loop_local_queue);
263 while (!loop_local_queue.empty()) {
264 PendingTask t = loop_local_queue.front();
265 loop_local_queue.pop();
266 loop.RunTask(t);
270 now = base::TimeTicks::Now();
271 } while (now - start < base::TimeDelta::FromSeconds(5));
272 std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload);
273 perf_test::PrintResult(
274 "task",
276 trace,
277 (now - start).InMicroseconds() / static_cast<double>(num_posted),
278 "us/task",
279 true);
283 TEST_F(PostTaskTest, OneTaskPerReload) {
284 Run(10000, 1);
287 TEST_F(PostTaskTest, TenTasksPerReload) {
288 Run(10000, 10);
291 TEST_F(PostTaskTest, OneHundredTasksPerReload) {
292 Run(1000, 100);
295 } // namespace base