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.
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"
24 class ScheduleWorkTest
: public testing::Test
{
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;
40 for (size_t i
= 0; i
< kBatchSize
; ++i
) {
41 target_message_loop()->ScheduleWork();
44 now
= base::TimeTicks::Now();
45 base::TimeDelta laptime
= now
- lastnow
;
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),
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();
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
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(
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();
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
127 : (target_type
== MessageLoop::TYPE_UI
? "ui" : "default"));
128 perf_test::PrintResult(
132 total_time
.InMicroseconds() / static_cast<double>(counter_
),
135 perf_test::PrintResult(
139 min_batch_time
.InMicroseconds() / static_cast<double>(kBatchSize
),
142 perf_test::PrintResult(
146 max_batch_time
.InMicroseconds() / static_cast<double>(kBatchSize
),
149 if (ThreadTicks::IsSupported()) {
150 perf_test::PrintResult(
154 total_thread_time
.InMicroseconds() / static_cast<double>(counter_
),
160 MessageLoop
* target_message_loop() {
161 #if defined(OS_ANDROID)
163 return java_thread_
->message_loop();
165 return target_
->message_loop();
169 scoped_ptr
<Thread
> target_
;
170 #if defined(OS_ANDROID)
171 scoped_ptr
<android::JavaHandlerThread
> java_thread_
;
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_
;
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);
233 class FakeMessagePump
: public MessagePump
{
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
{
247 void Run(int batch_size
, int tasks_per_reload
) {
248 base::TimeTicks start
= 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;
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);
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();
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(
277 (now
- start
).InMicroseconds() / static_cast<double>(num_posted
),
283 TEST_F(PostTaskTest
, OneTaskPerReload
) {
287 TEST_F(PostTaskTest
, TenTasksPerReload
) {
291 TEST_F(PostTaskTest
, OneHundredTasksPerReload
) {