xref: /aosp_15_r20/external/cronet/base/message_loop/message_pump_perftest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2014 The Chromium Authors
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 <memory>
9 
10 #include "base/format_macros.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/message_loop/message_pump_type.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/synchronization/condition_variable.h"
17 #include "base/synchronization/lock.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/task/current_thread.h"
20 #include "base/task/sequence_manager/sequence_manager_impl.h"
21 #include "base/task/single_thread_task_runner.h"
22 #include "base/threading/thread.h"
23 #include "base/time/time.h"
24 #include "build/build_config.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "testing/perf/perf_result_reporter.h"
27 
28 #if BUILDFLAG(IS_ANDROID)
29 #include "base/android/java_handler_thread.h"
30 #endif
31 
32 namespace base {
33 namespace {
34 
35 constexpr char kMetricPrefixScheduleWork[] = "ScheduleWork.";
36 constexpr char kMetricMinBatchTime[] = "min_batch_time_per_task";
37 constexpr char kMetricMaxBatchTime[] = "max_batch_time_per_task";
38 constexpr char kMetricTotalTime[] = "total_time_per_task";
39 constexpr char kMetricThreadTime[] = "thread_time_per_task";
40 
SetUpReporter(const std::string & story_name)41 perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
42   perf_test::PerfResultReporter reporter(kMetricPrefixScheduleWork, story_name);
43   reporter.RegisterImportantMetric(kMetricMinBatchTime, "us");
44   reporter.RegisterImportantMetric(kMetricMaxBatchTime, "us");
45   reporter.RegisterImportantMetric(kMetricTotalTime, "us");
46   reporter.RegisterImportantMetric(kMetricThreadTime, "us");
47   return reporter;
48 }
49 
50 #if BUILDFLAG(IS_ANDROID)
51 class JavaHandlerThreadForTest : public android::JavaHandlerThread {
52  public:
JavaHandlerThreadForTest(const char * name)53   explicit JavaHandlerThreadForTest(const char* name)
54       : android::JavaHandlerThread(name, base::ThreadType::kDefault) {}
55 
56   using android::JavaHandlerThread::state;
57   using android::JavaHandlerThread::State;
58 };
59 #endif
60 
61 }  // namespace
62 
63 class ScheduleWorkTest : public testing::Test {
64  public:
ScheduleWorkTest()65   ScheduleWorkTest() : counter_(0) {}
66 
SetUp()67   void SetUp() override {
68     if (base::ThreadTicks::IsSupported())
69       base::ThreadTicks::WaitUntilInitialized();
70   }
71 
Increment(uint64_t amount)72   void Increment(uint64_t amount) { counter_ += amount; }
73 
Schedule(int index)74   void Schedule(int index) {
75     base::TimeTicks start = base::TimeTicks::Now();
76     base::ThreadTicks thread_start;
77     if (ThreadTicks::IsSupported())
78       thread_start = base::ThreadTicks::Now();
79     base::TimeDelta minimum = base::TimeDelta::Max();
80     base::TimeDelta maximum = base::TimeDelta();
81     base::TimeTicks now, lastnow = start;
82     uint64_t schedule_calls = 0u;
83     do {
84       for (size_t i = 0; i < kBatchSize; ++i) {
85         target_message_loop_base()->GetMessagePump()->ScheduleWork();
86         schedule_calls++;
87       }
88       now = base::TimeTicks::Now();
89       base::TimeDelta laptime = now - lastnow;
90       lastnow = now;
91       minimum = std::min(minimum, laptime);
92       maximum = std::max(maximum, laptime);
93     } while (now - start < base::Seconds(kTargetTimeSec));
94 
95     scheduling_times_[index] = now - start;
96     if (ThreadTicks::IsSupported())
97       scheduling_thread_times_[index] =
98           base::ThreadTicks::Now() - thread_start;
99     min_batch_times_[index] = minimum;
100     max_batch_times_[index] = maximum;
101     target_message_loop_base()->GetTaskRunner()->PostTask(
102         FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment,
103                                   base::Unretained(this), schedule_calls));
104   }
105 
ScheduleWork(MessagePumpType target_type,int num_scheduling_threads)106   void ScheduleWork(MessagePumpType target_type, int num_scheduling_threads) {
107 #if BUILDFLAG(IS_ANDROID)
108     if (target_type == MessagePumpType::JAVA) {
109       java_thread_ = std::make_unique<JavaHandlerThreadForTest>("target");
110       java_thread_->Start();
111     } else
112 #endif
113     {
114       target_ = std::make_unique<Thread>("test");
115 
116       Thread::Options options(target_type, 0u);
117       options.message_pump_type = target_type;
118       target_->StartWithOptions(std::move(options));
119 
120       // Without this, it's possible for the scheduling threads to start and run
121       // before the target thread. In this case, the scheduling threads will
122       // call target_message_loop()->ScheduleWork(), which dereferences the
123       // loop's message pump, which is only created after the target thread has
124       // finished starting.
125       target_->WaitUntilThreadStarted();
126     }
127 
128     std::vector<std::unique_ptr<Thread>> scheduling_threads;
129     scheduling_times_ =
130         std::make_unique<base::TimeDelta[]>(num_scheduling_threads);
131     scheduling_thread_times_ =
132         std::make_unique<base::TimeDelta[]>(num_scheduling_threads);
133     min_batch_times_ =
134         std::make_unique<base::TimeDelta[]>(num_scheduling_threads);
135     max_batch_times_ =
136         std::make_unique<base::TimeDelta[]>(num_scheduling_threads);
137 
138     for (int i = 0; i < num_scheduling_threads; ++i) {
139       scheduling_threads.push_back(std::make_unique<Thread>("posting thread"));
140       scheduling_threads[i]->Start();
141     }
142 
143     for (int i = 0; i < num_scheduling_threads; ++i) {
144       scheduling_threads[i]->task_runner()->PostTask(
145           FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule,
146                                     base::Unretained(this), i));
147     }
148 
149     for (int i = 0; i < num_scheduling_threads; ++i) {
150       scheduling_threads[i]->Stop();
151     }
152 #if BUILDFLAG(IS_ANDROID)
153     if (target_type == MessagePumpType::JAVA) {
154       java_thread_->Stop();
155       java_thread_.reset();
156     } else
157 #endif
158     {
159       target_->Stop();
160       target_.reset();
161     }
162     base::TimeDelta total_time;
163     base::TimeDelta total_thread_time;
164     base::TimeDelta min_batch_time = base::TimeDelta::Max();
165     base::TimeDelta max_batch_time = base::TimeDelta();
166     for (int i = 0; i < num_scheduling_threads; ++i) {
167       total_time += scheduling_times_[i];
168       total_thread_time += scheduling_thread_times_[i];
169       min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
170       max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
171     }
172 
173     std::string story_name = StringPrintf(
174         "%s_pump_from_%d_threads",
175         target_type == MessagePumpType::IO
176             ? "io"
177             : (target_type == MessagePumpType::UI ? "ui" : "default"),
178         num_scheduling_threads);
179     auto reporter = SetUpReporter(story_name);
180     reporter.AddResult(kMetricMinBatchTime, total_time.InMicroseconds() /
181                                                 static_cast<double>(counter_));
182     reporter.AddResult(
183         kMetricMaxBatchTime,
184         max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize));
185     reporter.AddResult(kMetricTotalTime, total_time.InMicroseconds() /
186                                              static_cast<double>(counter_));
187     if (ThreadTicks::IsSupported()) {
188       reporter.AddResult(kMetricThreadTime, total_thread_time.InMicroseconds() /
189                                                 static_cast<double>(counter_));
190     }
191   }
192 
target_message_loop_base()193   sequence_manager::internal::SequenceManagerImpl* target_message_loop_base() {
194 #if BUILDFLAG(IS_ANDROID)
195     if (java_thread_) {
196       return static_cast<sequence_manager::internal::SequenceManagerImpl*>(
197           java_thread_->state()->sequence_manager.get());
198     }
199 #endif
200     return CurrentThread::Get()->GetCurrentSequenceManagerImpl();
201   }
202 
203  private:
204   std::unique_ptr<Thread> target_;
205 #if BUILDFLAG(IS_ANDROID)
206   std::unique_ptr<JavaHandlerThreadForTest> java_thread_;
207 #endif
208   std::unique_ptr<base::TimeDelta[]> scheduling_times_;
209   std::unique_ptr<base::TimeDelta[]> scheduling_thread_times_;
210   std::unique_ptr<base::TimeDelta[]> min_batch_times_;
211   std::unique_ptr<base::TimeDelta[]> max_batch_times_;
212   uint64_t counter_;
213 
214   static const size_t kTargetTimeSec = 5;
215   static const size_t kBatchSize = 1000;
216 };
217 
TEST_F(ScheduleWorkTest,ThreadTimeToIOFromOneThread)218 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
219   ScheduleWork(MessagePumpType::IO, 1);
220 }
221 
TEST_F(ScheduleWorkTest,ThreadTimeToIOFromTwoThreads)222 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
223   ScheduleWork(MessagePumpType::IO, 2);
224 }
225 
TEST_F(ScheduleWorkTest,ThreadTimeToIOFromFourThreads)226 TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
227   ScheduleWork(MessagePumpType::IO, 4);
228 }
229 
TEST_F(ScheduleWorkTest,ThreadTimeToUIFromOneThread)230 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
231   ScheduleWork(MessagePumpType::UI, 1);
232 }
233 
TEST_F(ScheduleWorkTest,ThreadTimeToUIFromTwoThreads)234 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
235   ScheduleWork(MessagePumpType::UI, 2);
236 }
237 
TEST_F(ScheduleWorkTest,ThreadTimeToUIFromFourThreads)238 TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
239   ScheduleWork(MessagePumpType::UI, 4);
240 }
241 
TEST_F(ScheduleWorkTest,ThreadTimeToDefaultFromOneThread)242 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
243   ScheduleWork(MessagePumpType::DEFAULT, 1);
244 }
245 
TEST_F(ScheduleWorkTest,ThreadTimeToDefaultFromTwoThreads)246 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
247   ScheduleWork(MessagePumpType::DEFAULT, 2);
248 }
249 
TEST_F(ScheduleWorkTest,ThreadTimeToDefaultFromFourThreads)250 TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
251   ScheduleWork(MessagePumpType::DEFAULT, 4);
252 }
253 
254 #if BUILDFLAG(IS_ANDROID)
TEST_F(ScheduleWorkTest,ThreadTimeToJavaFromOneThread)255 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
256   ScheduleWork(MessagePumpType::JAVA, 1);
257 }
258 
TEST_F(ScheduleWorkTest,ThreadTimeToJavaFromTwoThreads)259 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
260   ScheduleWork(MessagePumpType::JAVA, 2);
261 }
262 
TEST_F(ScheduleWorkTest,ThreadTimeToJavaFromFourThreads)263 TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
264   ScheduleWork(MessagePumpType::JAVA, 4);
265 }
266 #endif
267 
268 }  // namespace base
269