blob: 0bf3d0ca9421ff865eff73d0a125e91e0a10a261 [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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 "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"
17
18#if defined(OS_ANDROID)
19#include "base/android/java_handler_thread.h"
20#endif
21
22namespace base {
James Robinson646469d2014-10-03 15:33:28 -070023
24class ScheduleWorkTest : public testing::Test {
25 public:
26 ScheduleWorkTest() : counter_(0) {}
27
28 void Increment(uint64_t amount) { counter_ += amount; }
29
30 void Schedule(int index) {
Elliot Glayshereae49292015-01-28 10:47:32 -080031 base::TimeTicks start = base::TimeTicks::Now();
James Robinson646469d2014-10-03 15:33:28 -070032 base::TimeTicks thread_start;
33 if (TimeTicks::IsThreadNowSupported())
34 thread_start = base::TimeTicks::ThreadNow();
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) {
Etienne Membrivesb1556b32014-12-16 13:56:09 +010041 target_message_loop()->ScheduleWork();
James Robinson646469d2014-10-03 15:33:28 -070042 schedule_calls++;
43 }
Elliot Glayshereae49292015-01-28 10:47:32 -080044 now = base::TimeTicks::Now();
James Robinson646469d2014-10-03 15:33:28 -070045 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));
50
51 scheduling_times_[index] = now - start;
52 if (TimeTicks::IsThreadNowSupported())
53 scheduling_thread_times_[index] =
54 base::TimeTicks::ThreadNow() - 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));
61 }
62
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
70 {
71 target_.reset(new Thread("target"));
72 target_->StartWithOptions(Thread::Options(target_type, 0u));
73 }
74
75 ScopedVector<Thread> scheduling_threads;
76 scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]);
77 scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]);
78 min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
79 max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
80
81 for (int i = 0; i < num_scheduling_threads; ++i) {
82 scheduling_threads.push_back(new Thread("posting thread"));
83 scheduling_threads[i]->Start();
84 }
85
86 for (int i = 0; i < num_scheduling_threads; ++i) {
87 scheduling_threads[i]->message_loop()->PostTask(
88 FROM_HERE,
89 base::Bind(&ScheduleWorkTest::Schedule, base::Unretained(this), i));
90 }
91
92 for (int i = 0; i < num_scheduling_threads; ++i) {
93 scheduling_threads[i]->Stop();
94 }
95#if defined(OS_ANDROID)
96 if (target_type == MessageLoop::TYPE_JAVA) {
97 java_thread_->Stop();
98 java_thread_.reset();
99 } else
100#endif
101 {
102 target_->Stop();
103 target_.reset();
104 }
105 base::TimeDelta total_time;
106 base::TimeDelta total_thread_time;
107 base::TimeDelta min_batch_time = base::TimeDelta::Max();
108 base::TimeDelta max_batch_time = base::TimeDelta();
109 for (int i = 0; i < num_scheduling_threads; ++i) {
110 total_time += scheduling_times_[i];
111 total_thread_time += scheduling_thread_times_[i];
112 min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
113 max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
114 }
115 std::string trace = StringPrintf(
116 "%d_threads_scheduling_to_%s_pump",
117 num_scheduling_threads,
118 target_type == MessageLoop::TYPE_IO
119 ? "io"
120 : (target_type == MessageLoop::TYPE_UI ? "ui" : "default"));
121 perf_test::PrintResult(
122 "task",
123 "",
124 trace,
125 total_time.InMicroseconds() / static_cast<double>(counter_),
126 "us/task",
127 true);
128 perf_test::PrintResult(
129 "task",
130 "_min_batch_time",
131 trace,
132 min_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
133 "us/task",
134 false);
135 perf_test::PrintResult(
136 "task",
137 "_max_batch_time",
138 trace,
139 max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
140 "us/task",
141 false);
142 if (TimeTicks::IsThreadNowSupported()) {
143 perf_test::PrintResult(
144 "task",
145 "_thread_time",
146 trace,
147 total_thread_time.InMicroseconds() / static_cast<double>(counter_),
148 "us/task",
149 true);
150 }
151 }
152
153 MessageLoop* target_message_loop() {
154#if defined(OS_ANDROID)
155 if (java_thread_)
156 return java_thread_->message_loop();
157#endif
158 return target_->message_loop();
159 }
160
161 private:
162 scoped_ptr<Thread> target_;
163#if defined(OS_ANDROID)
164 scoped_ptr<android::JavaHandlerThread> java_thread_;
165#endif
166 scoped_ptr<base::TimeDelta[]> scheduling_times_;
167 scoped_ptr<base::TimeDelta[]> scheduling_thread_times_;
168 scoped_ptr<base::TimeDelta[]> min_batch_times_;
169 scoped_ptr<base::TimeDelta[]> max_batch_times_;
170 uint64_t counter_;
171
172 static const size_t kTargetTimeSec = 5;
173 static const size_t kBatchSize = 1000;
174};
175
176TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
177 ScheduleWork(MessageLoop::TYPE_IO, 1);
178}
179
180TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
181 ScheduleWork(MessageLoop::TYPE_IO, 2);
182}
183
184TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
185 ScheduleWork(MessageLoop::TYPE_IO, 4);
186}
187
188TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
189 ScheduleWork(MessageLoop::TYPE_UI, 1);
190}
191
192TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
193 ScheduleWork(MessageLoop::TYPE_UI, 2);
194}
195
196TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
197 ScheduleWork(MessageLoop::TYPE_UI, 4);
198}
199
200TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
201 ScheduleWork(MessageLoop::TYPE_DEFAULT, 1);
202}
203
204TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
205 ScheduleWork(MessageLoop::TYPE_DEFAULT, 2);
206}
207
208TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
209 ScheduleWork(MessageLoop::TYPE_DEFAULT, 4);
210}
211
212#if defined(OS_ANDROID)
213TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
214 ScheduleWork(MessageLoop::TYPE_JAVA, 1);
215}
216
217TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
218 ScheduleWork(MessageLoop::TYPE_JAVA, 2);
219}
220
221TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
222 ScheduleWork(MessageLoop::TYPE_JAVA, 4);
223}
224#endif
225
James Robinson646469d2014-10-03 15:33:28 -0700226class FakeMessagePump : public MessagePump {
227 public:
228 FakeMessagePump() {}
James Robinsone1b30cf2014-10-21 12:25:40 -0700229 ~FakeMessagePump() override {}
James Robinson646469d2014-10-03 15:33:28 -0700230
James Robinsone1b30cf2014-10-21 12:25:40 -0700231 void Run(Delegate* delegate) override {}
James Robinson646469d2014-10-03 15:33:28 -0700232
James Robinsone1b30cf2014-10-21 12:25:40 -0700233 void Quit() override {}
234 void ScheduleWork() override {}
235 void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override {}
James Robinson646469d2014-10-03 15:33:28 -0700236};
237
238class PostTaskTest : public testing::Test {
239 public:
240 void Run(int batch_size, int tasks_per_reload) {
Elliot Glayshereae49292015-01-28 10:47:32 -0800241 base::TimeTicks start = base::TimeTicks::Now();
James Robinson646469d2014-10-03 15:33:28 -0700242 base::TimeTicks now;
243 MessageLoop loop(scoped_ptr<MessagePump>(new FakeMessagePump));
244 scoped_refptr<internal::IncomingTaskQueue> queue(
245 new internal::IncomingTaskQueue(&loop));
246 uint32_t num_posted = 0;
247 do {
248 for (int i = 0; i < batch_size; ++i) {
249 for (int j = 0; j < tasks_per_reload; ++j) {
250 queue->AddToIncomingQueue(
251 FROM_HERE, base::Bind(&DoNothing), base::TimeDelta(), false);
252 num_posted++;
253 }
254 TaskQueue loop_local_queue;
255 queue->ReloadWorkQueue(&loop_local_queue);
256 while (!loop_local_queue.empty()) {
257 PendingTask t = loop_local_queue.front();
258 loop_local_queue.pop();
259 loop.RunTask(t);
260 }
261 }
262
Elliot Glayshereae49292015-01-28 10:47:32 -0800263 now = base::TimeTicks::Now();
James Robinson646469d2014-10-03 15:33:28 -0700264 } while (now - start < base::TimeDelta::FromSeconds(5));
265 std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload);
266 perf_test::PrintResult(
267 "task",
268 "",
269 trace,
270 (now - start).InMicroseconds() / static_cast<double>(num_posted),
271 "us/task",
272 true);
273 }
274};
275
276TEST_F(PostTaskTest, OneTaskPerReload) {
277 Run(10000, 1);
278}
279
280TEST_F(PostTaskTest, TenTasksPerReload) {
281 Run(10000, 10);
282}
283
284TEST_F(PostTaskTest, OneHundredTasksPerReload) {
285 Run(1000, 100);
286}
287
James Robinson646469d2014-10-03 15:33:28 -0700288} // namespace base