blob: cadd58e64f3eeb68bd7cff0b317c1db0c8923aa2 [file] [log] [blame]
jamesr8d731062014-09-30 06:12:44 +09001// 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
avia6a6a682015-12-27 07:15:14 +09005#include <stddef.h>
6#include <stdint.h>
7
jamesr8d731062014-09-30 06:12:44 +09008#include "base/bind.h"
Peter Kasting88430fa2018-02-13 15:22:40 +09009#include "base/bind_helpers.h"
jamesr8d731062014-09-30 06:12:44 +090010#include "base/format_macros.h"
dchengcc8e4d82016-04-05 06:25:51 +090011#include "base/memory/ptr_util.h"
fdoray85919bb2016-07-01 03:17:39 +090012#include "base/single_thread_task_runner.h"
jamesr8d731062014-09-30 06:12:44 +090013#include "base/strings/stringprintf.h"
14#include "base/synchronization/condition_variable.h"
15#include "base/synchronization/lock.h"
16#include "base/synchronization/waitable_event.h"
17#include "base/threading/thread.h"
18#include "base/time/time.h"
19#include "build/build_config.h"
20#include "testing/gtest/include/gtest/gtest.h"
21#include "testing/perf/perf_test.h"
22
23#if defined(OS_ANDROID)
24#include "base/android/java_handler_thread.h"
25#endif
26
27namespace base {
jamesr8d731062014-09-30 06:12:44 +090028
29class ScheduleWorkTest : public testing::Test {
30 public:
31 ScheduleWorkTest() : counter_(0) {}
32
fdorayeabadbd2015-10-15 12:46:40 +090033 void SetUp() override {
34 if (base::ThreadTicks::IsSupported())
35 base::ThreadTicks::WaitUntilInitialized();
36 }
37
jamesr8d731062014-09-30 06:12:44 +090038 void Increment(uint64_t amount) { counter_ += amount; }
39
40 void Schedule(int index) {
charlieaddf2b792015-01-27 02:35:41 +090041 base::TimeTicks start = base::TimeTicks::Now();
miu85c35562015-05-30 08:57:12 +090042 base::ThreadTicks thread_start;
43 if (ThreadTicks::IsSupported())
44 thread_start = base::ThreadTicks::Now();
jamesr8d731062014-09-30 06:12:44 +090045 base::TimeDelta minimum = base::TimeDelta::Max();
46 base::TimeDelta maximum = base::TimeDelta();
47 base::TimeTicks now, lastnow = start;
48 uint64_t schedule_calls = 0u;
49 do {
50 for (size_t i = 0; i < kBatchSize; ++i) {
jamesrd74fa8c2014-12-13 10:55:30 +090051 target_message_loop()->ScheduleWork();
jamesr8d731062014-09-30 06:12:44 +090052 schedule_calls++;
53 }
charlieaddf2b792015-01-27 02:35:41 +090054 now = base::TimeTicks::Now();
jamesr8d731062014-09-30 06:12:44 +090055 base::TimeDelta laptime = now - lastnow;
56 lastnow = now;
57 minimum = std::min(minimum, laptime);
58 maximum = std::max(maximum, laptime);
59 } while (now - start < base::TimeDelta::FromSeconds(kTargetTimeSec));
60
61 scheduling_times_[index] = now - start;
miu85c35562015-05-30 08:57:12 +090062 if (ThreadTicks::IsSupported())
jamesr8d731062014-09-30 06:12:44 +090063 scheduling_thread_times_[index] =
miu85c35562015-05-30 08:57:12 +090064 base::ThreadTicks::Now() - thread_start;
jamesr8d731062014-09-30 06:12:44 +090065 min_batch_times_[index] = minimum;
66 max_batch_times_[index] = maximum;
fdoray85919bb2016-07-01 03:17:39 +090067 target_message_loop()->task_runner()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +090068 FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment,
69 base::Unretained(this), schedule_calls));
jamesr8d731062014-09-30 06:12:44 +090070 }
71
72 void ScheduleWork(MessageLoop::Type target_type, int num_scheduling_threads) {
73#if defined(OS_ANDROID)
74 if (target_type == MessageLoop::TYPE_JAVA) {
75 java_thread_.reset(new android::JavaHandlerThread("target"));
76 java_thread_->Start();
77 } else
78#endif
79 {
80 target_.reset(new Thread("target"));
81 target_->StartWithOptions(Thread::Options(target_type, 0u));
amistryb70d89a2015-08-11 08:50:44 +090082
83 // Without this, it's possible for the scheduling threads to start and run
84 // before the target thread. In this case, the scheduling threads will
85 // call target_message_loop()->ScheduleWork(), which dereferences the
86 // loop's message pump, which is only created after the target thread has
87 // finished starting.
88 target_->WaitUntilThreadStarted();
jamesr8d731062014-09-30 06:12:44 +090089 }
90
dchengcc8e4d82016-04-05 06:25:51 +090091 std::vector<std::unique_ptr<Thread>> scheduling_threads;
jamesr8d731062014-09-30 06:12:44 +090092 scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]);
93 scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]);
94 min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
95 max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
96
97 for (int i = 0; i < num_scheduling_threads; ++i) {
Jeremy Romancd0c4672017-08-17 08:27:24 +090098 scheduling_threads.push_back(std::make_unique<Thread>("posting thread"));
jamesr8d731062014-09-30 06:12:44 +090099 scheduling_threads[i]->Start();
100 }
101
102 for (int i = 0; i < num_scheduling_threads; ++i) {
fdoray851719c2016-08-26 00:36:37 +0900103 scheduling_threads[i]->task_runner()->PostTask(
tzik6bdbeb22017-04-12 00:00:44 +0900104 FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule,
105 base::Unretained(this), i));
jamesr8d731062014-09-30 06:12:44 +0900106 }
107
108 for (int i = 0; i < num_scheduling_threads; ++i) {
109 scheduling_threads[i]->Stop();
110 }
111#if defined(OS_ANDROID)
112 if (target_type == MessageLoop::TYPE_JAVA) {
113 java_thread_->Stop();
114 java_thread_.reset();
115 } else
116#endif
117 {
118 target_->Stop();
119 target_.reset();
120 }
121 base::TimeDelta total_time;
122 base::TimeDelta total_thread_time;
123 base::TimeDelta min_batch_time = base::TimeDelta::Max();
124 base::TimeDelta max_batch_time = base::TimeDelta();
125 for (int i = 0; i < num_scheduling_threads; ++i) {
126 total_time += scheduling_times_[i];
127 total_thread_time += scheduling_thread_times_[i];
128 min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
129 max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
130 }
131 std::string trace = StringPrintf(
132 "%d_threads_scheduling_to_%s_pump",
133 num_scheduling_threads,
134 target_type == MessageLoop::TYPE_IO
135 ? "io"
136 : (target_type == MessageLoop::TYPE_UI ? "ui" : "default"));
137 perf_test::PrintResult(
138 "task",
139 "",
140 trace,
141 total_time.InMicroseconds() / static_cast<double>(counter_),
142 "us/task",
143 true);
144 perf_test::PrintResult(
145 "task",
146 "_min_batch_time",
147 trace,
148 min_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
149 "us/task",
150 false);
151 perf_test::PrintResult(
152 "task",
153 "_max_batch_time",
154 trace,
155 max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
156 "us/task",
157 false);
miu85c35562015-05-30 08:57:12 +0900158 if (ThreadTicks::IsSupported()) {
jamesr8d731062014-09-30 06:12:44 +0900159 perf_test::PrintResult(
160 "task",
161 "_thread_time",
162 trace,
163 total_thread_time.InMicroseconds() / static_cast<double>(counter_),
164 "us/task",
165 true);
166 }
167 }
168
169 MessageLoop* target_message_loop() {
170#if defined(OS_ANDROID)
171 if (java_thread_)
172 return java_thread_->message_loop();
173#endif
174 return target_->message_loop();
175 }
176
177 private:
dchengcc8e4d82016-04-05 06:25:51 +0900178 std::unique_ptr<Thread> target_;
jamesr8d731062014-09-30 06:12:44 +0900179#if defined(OS_ANDROID)
dchengcc8e4d82016-04-05 06:25:51 +0900180 std::unique_ptr<android::JavaHandlerThread> java_thread_;
jamesr8d731062014-09-30 06:12:44 +0900181#endif
dchengcc8e4d82016-04-05 06:25:51 +0900182 std::unique_ptr<base::TimeDelta[]> scheduling_times_;
183 std::unique_ptr<base::TimeDelta[]> scheduling_thread_times_;
184 std::unique_ptr<base::TimeDelta[]> min_batch_times_;
185 std::unique_ptr<base::TimeDelta[]> max_batch_times_;
jamesr8d731062014-09-30 06:12:44 +0900186 uint64_t counter_;
187
188 static const size_t kTargetTimeSec = 5;
189 static const size_t kBatchSize = 1000;
190};
191
192TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
193 ScheduleWork(MessageLoop::TYPE_IO, 1);
194}
195
196TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
197 ScheduleWork(MessageLoop::TYPE_IO, 2);
198}
199
200TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
201 ScheduleWork(MessageLoop::TYPE_IO, 4);
202}
203
204TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
205 ScheduleWork(MessageLoop::TYPE_UI, 1);
206}
207
208TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
209 ScheduleWork(MessageLoop::TYPE_UI, 2);
210}
211
212TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
213 ScheduleWork(MessageLoop::TYPE_UI, 4);
214}
215
216TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
217 ScheduleWork(MessageLoop::TYPE_DEFAULT, 1);
218}
219
220TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
221 ScheduleWork(MessageLoop::TYPE_DEFAULT, 2);
222}
223
224TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
225 ScheduleWork(MessageLoop::TYPE_DEFAULT, 4);
226}
227
228#if defined(OS_ANDROID)
229TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
230 ScheduleWork(MessageLoop::TYPE_JAVA, 1);
231}
232
233TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
234 ScheduleWork(MessageLoop::TYPE_JAVA, 2);
235}
236
237TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
238 ScheduleWork(MessageLoop::TYPE_JAVA, 4);
239}
240#endif
241
jamesr8d731062014-09-30 06:12:44 +0900242class FakeMessagePump : public MessagePump {
243 public:
Chris Watkinsd155d9f2017-11-29 16:16:38 +0900244 FakeMessagePump() = default;
245 ~FakeMessagePump() override = default;
jamesr8d731062014-09-30 06:12:44 +0900246
dcheng7dc8df52014-10-21 19:54:51 +0900247 void Run(Delegate* delegate) override {}
jamesr8d731062014-09-30 06:12:44 +0900248
dcheng7dc8df52014-10-21 19:54:51 +0900249 void Quit() override {}
250 void ScheduleWork() override {}
251 void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override {}
jamesr8d731062014-09-30 06:12:44 +0900252};
253
254class PostTaskTest : public testing::Test {
255 public:
256 void Run(int batch_size, int tasks_per_reload) {
charlieaddf2b792015-01-27 02:35:41 +0900257 base::TimeTicks start = base::TimeTicks::Now();
jamesr8d731062014-09-30 06:12:44 +0900258 base::TimeTicks now;
dchengcc8e4d82016-04-05 06:25:51 +0900259 MessageLoop loop(std::unique_ptr<MessagePump>(new FakeMessagePump));
jamesr8d731062014-09-30 06:12:44 +0900260 scoped_refptr<internal::IncomingTaskQueue> queue(
261 new internal::IncomingTaskQueue(&loop));
262 uint32_t num_posted = 0;
263 do {
264 for (int i = 0; i < batch_size; ++i) {
265 for (int j = 0; j < tasks_per_reload; ++j) {
Peter Kasting24efe5e2018-02-24 09:03:01 +0900266 queue->AddToIncomingQueue(FROM_HERE, DoNothing(), base::TimeDelta(),
267 Nestable::kNonNestable);
jamesr8d731062014-09-30 06:12:44 +0900268 num_posted++;
269 }
270 TaskQueue loop_local_queue;
271 queue->ReloadWorkQueue(&loop_local_queue);
272 while (!loop_local_queue.empty()) {
tzikc74f6fe2016-07-08 05:20:06 +0900273 PendingTask t = std::move(loop_local_queue.front());
jamesr8d731062014-09-30 06:12:44 +0900274 loop_local_queue.pop();
tzike82b7e82016-10-14 23:34:58 +0900275 loop.RunTask(&t);
jamesr8d731062014-09-30 06:12:44 +0900276 }
277 }
278
charlieaddf2b792015-01-27 02:35:41 +0900279 now = base::TimeTicks::Now();
jamesr8d731062014-09-30 06:12:44 +0900280 } while (now - start < base::TimeDelta::FromSeconds(5));
281 std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload);
282 perf_test::PrintResult(
283 "task",
284 "",
285 trace,
286 (now - start).InMicroseconds() / static_cast<double>(num_posted),
287 "us/task",
288 true);
Robert Liaodd95ec52017-08-15 02:08:01 +0900289 queue->WillDestroyCurrentMessageLoop();
jamesr8d731062014-09-30 06:12:44 +0900290 }
291};
292
293TEST_F(PostTaskTest, OneTaskPerReload) {
294 Run(10000, 1);
295}
296
297TEST_F(PostTaskTest, TenTasksPerReload) {
298 Run(10000, 10);
299}
300
301TEST_F(PostTaskTest, OneHundredTasksPerReload) {
302 Run(1000, 100);
303}
304
jamesr8d731062014-09-30 06:12:44 +0900305} // namespace base