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 <stddef.h>
6 #include <stdint.h>
7
8 #include "base/bind.h"
9 #include "base/bind_helpers.h"
10 #include "base/format_macros.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/synchronization/condition_variable.h"
16 #include "base/synchronization/lock.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/threading/thread.h"
19 #include "base/time/time.h"
20 #include "build/build_config.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "testing/perf/perf_test.h"
23
24 #if defined(OS_ANDROID)
25 #include "base/android/java_handler_thread.h"
26 #endif
27
28 namespace base {
29
30 class ScheduleWorkTest : public testing::Test {
31 public:
ScheduleWorkTest()32 ScheduleWorkTest() : counter_(0) {}
33
SetUp()34 void SetUp() override {
35 if (base::ThreadTicks::IsSupported())
36 base::ThreadTicks::WaitUntilInitialized();
37 }
38
Increment(uint64_t amount)39 void Increment(uint64_t amount) { counter_ += amount; }
40
Schedule(int index)41 void Schedule(int index) {
42 base::TimeTicks start = base::TimeTicks::Now();
43 base::ThreadTicks thread_start;
44 if (ThreadTicks::IsSupported())
45 thread_start = base::ThreadTicks::Now();
46 base::TimeDelta minimum = base::TimeDelta::Max();
47 base::TimeDelta maximum = base::TimeDelta();
48 base::TimeTicks now, lastnow = start;
49 uint64_t schedule_calls = 0u;
50 do {
51 for (size_t i = 0; i < kBatchSize; ++i) {
52 target_message_loop()->ScheduleWork();
53 schedule_calls++;
54 }
55 now = base::TimeTicks::Now();
56 base::TimeDelta laptime = now - lastnow;
57 lastnow = now;
58 minimum = std::min(minimum, laptime);
59 maximum = std::max(maximum, laptime);
60 } while (now - start < base::TimeDelta::FromSeconds(kTargetTimeSec));
61
62 scheduling_times_[index] = now - start;
63 if (ThreadTicks::IsSupported())
64 scheduling_thread_times_[index] =
65 base::ThreadTicks::Now() - thread_start;
66 min_batch_times_[index] = minimum;
67 max_batch_times_[index] = maximum;
68 target_message_loop()->task_runner()->PostTask(
69 FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment,
70 base::Unretained(this), schedule_calls));
71 }
72
ScheduleWork(MessageLoop::Type target_type,int num_scheduling_threads)73 void ScheduleWork(MessageLoop::Type target_type, int num_scheduling_threads) {
74 #if defined(OS_ANDROID)
75 if (target_type == MessageLoop::TYPE_JAVA) {
76 java_thread_.reset(new android::JavaHandlerThread("target"));
77 java_thread_->Start();
78 } else
79 #endif
80 {
81 target_.reset(new Thread("target"));
82 target_->StartWithOptions(Thread::Options(target_type, 0u));
83
84 // Without this, it's possible for the scheduling threads to start and run
85 // before the target thread. In this case, the scheduling threads will
86 // call target_message_loop()->ScheduleWork(), which dereferences the
87 // loop's message pump, which is only created after the target thread has
88 // finished starting.
89 target_->WaitUntilThreadStarted();
90 }
91
92 std::vector<std::unique_ptr<Thread>> scheduling_threads;
93 scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]);
94 scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]);
95 min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
96 max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
97
98 for (int i = 0; i < num_scheduling_threads; ++i) {
99 scheduling_threads.push_back(std::make_unique<Thread>("posting thread"));
100 scheduling_threads[i]->Start();
101 }
102
103 for (int i = 0; i < num_scheduling_threads; ++i) {
104 scheduling_threads[i]->task_runner()->PostTask(
105 FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule,
106 base::Unretained(this), i));
107 }
108
109 for (int i = 0; i < num_scheduling_threads; ++i) {
110 scheduling_threads[i]->Stop();
111 }
112 #if defined(OS_ANDROID)
113 if (target_type == MessageLoop::TYPE_JAVA) {
114 java_thread_->Stop();
115 java_thread_.reset();
116 } else
117 #endif
118 {
119 target_->Stop();
120 target_.reset();
121 }
122 base::TimeDelta total_time;
123 base::TimeDelta total_thread_time;
124 base::TimeDelta min_batch_time = base::TimeDelta::Max();
125 base::TimeDelta max_batch_time = base::TimeDelta();
126 for (int i = 0; i < num_scheduling_threads; ++i) {
127 total_time += scheduling_times_[i];
128 total_thread_time += scheduling_thread_times_[i];
129 min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
130 max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
131 }
132 std::string trace = StringPrintf(
133 "%d_threads_scheduling_to_%s_pump",
134 num_scheduling_threads,
135 target_type == MessageLoop::TYPE_IO
136 ? "io"
137 : (target_type == MessageLoop::TYPE_UI ? "ui" : "default"));
138 perf_test::PrintResult(
139 "task",
140 "",
141 trace,
142 total_time.InMicroseconds() / static_cast<double>(counter_),
143 "us/task",
144 true);
145 perf_test::PrintResult(
146 "task",
147 "_min_batch_time",
148 trace,
149 min_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
150 "us/task",
151 false);
152 perf_test::PrintResult(
153 "task",
154 "_max_batch_time",
155 trace,
156 max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
157 "us/task",
158 false);
159 if (ThreadTicks::IsSupported()) {
160 perf_test::PrintResult(
161 "task",
162 "_thread_time",
163 trace,
164 total_thread_time.InMicroseconds() / static_cast<double>(counter_),
165 "us/task",
166 true);
167 }
168 }
169
target_message_loop()170 MessageLoop* target_message_loop() {
171 #if defined(OS_ANDROID)
172 if (java_thread_)
173 return java_thread_->message_loop();
174 #endif
175 return target_->message_loop();
176 }
177
178 private:
179 std::unique_ptr<Thread> target_;
180 #if defined(OS_ANDROID)
181 std::unique_ptr<android::JavaHandlerThread> java_thread_;
182 #endif
183 std::unique_ptr<base::TimeDelta[]> scheduling_times_;
184 std::unique_ptr<base::TimeDelta[]> scheduling_thread_times_;
185 std::unique_ptr<base::TimeDelta[]> min_batch_times_;
186 std::unique_ptr<base::TimeDelta[]> max_batch_times_;
187 uint64_t counter_;
188
189 static const size_t kTargetTimeSec = 5;
190 static const size_t kBatchSize = 1000;
191 };
192
TEST_F(ScheduleWorkTest,ThreadTimeToIOFromOneThread)193 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
194 ScheduleWork(MessageLoop::TYPE_IO, 1);
195 }
196
TEST_F(ScheduleWorkTest,ThreadTimeToIOFromTwoThreads)197 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
198 ScheduleWork(MessageLoop::TYPE_IO, 2);
199 }
200
TEST_F(ScheduleWorkTest,ThreadTimeToIOFromFourThreads)201 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
202 ScheduleWork(MessageLoop::TYPE_IO, 4);
203 }
204
TEST_F(ScheduleWorkTest,ThreadTimeToUIFromOneThread)205 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
206 ScheduleWork(MessageLoop::TYPE_UI, 1);
207 }
208
TEST_F(ScheduleWorkTest,ThreadTimeToUIFromTwoThreads)209 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
210 ScheduleWork(MessageLoop::TYPE_UI, 2);
211 }
212
TEST_F(ScheduleWorkTest,ThreadTimeToUIFromFourThreads)213 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
214 ScheduleWork(MessageLoop::TYPE_UI, 4);
215 }
216
TEST_F(ScheduleWorkTest,ThreadTimeToDefaultFromOneThread)217 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
218 ScheduleWork(MessageLoop::TYPE_DEFAULT, 1);
219 }
220
TEST_F(ScheduleWorkTest,ThreadTimeToDefaultFromTwoThreads)221 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
222 ScheduleWork(MessageLoop::TYPE_DEFAULT, 2);
223 }
224
TEST_F(ScheduleWorkTest,ThreadTimeToDefaultFromFourThreads)225 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
226 ScheduleWork(MessageLoop::TYPE_DEFAULT, 4);
227 }
228
229 #if defined(OS_ANDROID)
TEST_F(ScheduleWorkTest,ThreadTimeToJavaFromOneThread)230 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
231 ScheduleWork(MessageLoop::TYPE_JAVA, 1);
232 }
233
TEST_F(ScheduleWorkTest,ThreadTimeToJavaFromTwoThreads)234 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
235 ScheduleWork(MessageLoop::TYPE_JAVA, 2);
236 }
237
TEST_F(ScheduleWorkTest,ThreadTimeToJavaFromFourThreads)238 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
239 ScheduleWork(MessageLoop::TYPE_JAVA, 4);
240 }
241 #endif
242
243 } // namespace base
244