xref: /aosp_15_r20/external/libchrome/base/message_loop/message_pump_perftest.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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