blob: 387ea66d731958972c323aac7be64c2fe05db029 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2014 The Chromium Authors
jamesr2e146d72014-09-29 21:12:442// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
avi9b6f42932015-12-26 22:15:145#include <stddef.h>
6#include <stdint.h>
7
Peter Boströmfd851232021-03-31 17:05:338#include <memory>
9
jamesr2e146d72014-09-29 21:12:4410#include "base/format_macros.h"
Avi Drissman63e1f992023-01-13 18:54:4311#include "base/functional/bind.h"
12#include "base/functional/callback_helpers.h"
dcheng093de9b2016-04-04 21:25:5113#include "base/memory/ptr_util.h"
Carlos Caballerodd8bf7b042019-07-30 14:14:1514#include "base/message_loop/message_pump_type.h"
jamesr2e146d72014-09-29 21:12:4415#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"
Carlos Caballerob25fe8472020-07-17 10:27:1719#include "base/task/current_thread.h"
Carlos Caballero14712af92019-04-17 16:13:0120#include "base/task/sequence_manager/sequence_manager_impl.h"
Patrick Monette643cdf62021-10-15 19:13:4221#include "base/task/single_thread_task_runner.h"
jamesr2e146d72014-09-29 21:12:4422#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"
Brian Sheedyf7adb4b2019-12-19 02:23:1826#include "testing/perf/perf_result_reporter.h"
jamesr2e146d72014-09-29 21:12:4427
Xiaohan Wang29b5fdd2022-01-15 19:10:0228#if BUILDFLAG(IS_ANDROID)
jamesr2e146d72014-09-29 21:12:4429#include "base/android/java_handler_thread.h"
30#endif
31
32namespace base {
Alex Clarkee1cc1bb2019-06-06 17:24:2533namespace {
34
Brian Sheedyf7adb4b2019-12-19 02:23:1835constexpr char kMetricPrefixScheduleWork[] = "ScheduleWork.";
36constexpr char kMetricMinBatchTime[] = "min_batch_time_per_task";
37constexpr char kMetricMaxBatchTime[] = "max_batch_time_per_task";
38constexpr char kMetricTotalTime[] = "total_time_per_task";
39constexpr char kMetricThreadTime[] = "thread_time_per_task";
40
41perf_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
Xiaohan Wang29b5fdd2022-01-15 19:10:0250#if BUILDFLAG(IS_ANDROID)
Alex Clarkee1cc1bb2019-06-06 17:24:2551class JavaHandlerThreadForTest : public android::JavaHandlerThread {
52 public:
53 explicit JavaHandlerThreadForTest(const char* name)
Zhibo Wangd9e4a002022-07-07 04:34:5954 : android::JavaHandlerThread(name, base::ThreadType::kDefault) {}
Alex Clarkee1cc1bb2019-06-06 17:24:2555
Gabriel Charette35f26252019-08-12 12:01:2056 using android::JavaHandlerThread::state;
57 using android::JavaHandlerThread::State;
Alex Clarkee1cc1bb2019-06-06 17:24:2558};
59#endif
60
61} // namespace
jamesr2e146d72014-09-29 21:12:4462
63class ScheduleWorkTest : public testing::Test {
64 public:
Peter Kasting6218bbad2025-01-10 11:26:4065 ScheduleWorkTest() = default;
jamesr2e146d72014-09-29 21:12:4466
fdoraydc2a6592015-10-15 03:46:4067 void SetUp() override {
Peter Kasting134ef9af2024-12-28 02:30:0968 if (base::ThreadTicks::IsSupported()) {
fdoraydc2a6592015-10-15 03:46:4069 base::ThreadTicks::WaitUntilInitialized();
Peter Kasting134ef9af2024-12-28 02:30:0970 }
fdoraydc2a6592015-10-15 03:46:4071 }
72
jamesr2e146d72014-09-29 21:12:4473 void Increment(uint64_t amount) { counter_ += amount; }
74
75 void Schedule(int index) {
charliea3be839702015-01-26 17:35:4176 base::TimeTicks start = base::TimeTicks::Now();
miu1ab506a2015-05-29 23:57:1277 base::ThreadTicks thread_start;
Peter Kasting134ef9af2024-12-28 02:30:0978 if (ThreadTicks::IsSupported()) {
miu1ab506a2015-05-29 23:57:1279 thread_start = base::ThreadTicks::Now();
Peter Kasting134ef9af2024-12-28 02:30:0980 }
jamesr2e146d72014-09-29 21:12:4481 base::TimeDelta minimum = base::TimeDelta::Max();
82 base::TimeDelta maximum = base::TimeDelta();
83 base::TimeTicks now, lastnow = start;
84 uint64_t schedule_calls = 0u;
85 do {
86 for (size_t i = 0; i < kBatchSize; ++i) {
Alex Clarke00c56392019-02-15 20:22:5687 target_message_loop_base()->GetMessagePump()->ScheduleWork();
jamesr2e146d72014-09-29 21:12:4488 schedule_calls++;
89 }
charliea3be839702015-01-26 17:35:4190 now = base::TimeTicks::Now();
jamesr2e146d72014-09-29 21:12:4491 base::TimeDelta laptime = now - lastnow;
92 lastnow = now;
93 minimum = std::min(minimum, laptime);
94 maximum = std::max(maximum, laptime);
Peter Kastinge5a38ed2021-10-02 03:06:3595 } while (now - start < base::Seconds(kTargetTimeSec));
jamesr2e146d72014-09-29 21:12:4496
97 scheduling_times_[index] = now - start;
Peter Kasting134ef9af2024-12-28 02:30:0998 if (ThreadTicks::IsSupported()) {
99 scheduling_thread_times_[index] = base::ThreadTicks::Now() - thread_start;
100 }
jamesr2e146d72014-09-29 21:12:44101 min_batch_times_[index] = minimum;
102 max_batch_times_[index] = maximum;
Alex Clarke00c56392019-02-15 20:22:56103 target_message_loop_base()->GetTaskRunner()->PostTask(
tzik92b7a422017-04-11 15:00:44104 FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment,
105 base::Unretained(this), schedule_calls));
jamesr2e146d72014-09-29 21:12:44106 }
107
Carlos Caballerodd8bf7b042019-07-30 14:14:15108 void ScheduleWork(MessagePumpType target_type, int num_scheduling_threads) {
Xiaohan Wang29b5fdd2022-01-15 19:10:02109#if BUILDFLAG(IS_ANDROID)
Carlos Caballerodd8bf7b042019-07-30 14:14:15110 if (target_type == MessagePumpType::JAVA) {
Peter Boström08e7ed82021-04-19 17:49:59111 java_thread_ = std::make_unique<JavaHandlerThreadForTest>("target");
jamesr2e146d72014-09-29 21:12:44112 java_thread_->Start();
113 } else
114#endif
115 {
Peter Boströmfd851232021-03-31 17:05:33116 target_ = std::make_unique<Thread>("test");
Alex Clarke636d6b62019-02-22 01:39:04117
118 Thread::Options options(target_type, 0u);
Carlos Caballero6a2f39b2019-09-25 08:03:10119 options.message_pump_type = target_type;
Olivier Lid68d0792021-05-17 20:51:41120 target_->StartWithOptions(std::move(options));
amistry7fc519fa2015-08-10 23:50:44121
122 // Without this, it's possible for the scheduling threads to start and run
123 // before the target thread. In this case, the scheduling threads will
124 // call target_message_loop()->ScheduleWork(), which dereferences the
125 // loop's message pump, which is only created after the target thread has
126 // finished starting.
127 target_->WaitUntilThreadStarted();
jamesr2e146d72014-09-29 21:12:44128 }
129
dcheng093de9b2016-04-04 21:25:51130 std::vector<std::unique_ptr<Thread>> scheduling_threads;
Peter Boströmfd851232021-03-31 17:05:33131 scheduling_times_ =
132 std::make_unique<base::TimeDelta[]>(num_scheduling_threads);
133 scheduling_thread_times_ =
134 std::make_unique<base::TimeDelta[]>(num_scheduling_threads);
135 min_batch_times_ =
136 std::make_unique<base::TimeDelta[]>(num_scheduling_threads);
137 max_batch_times_ =
138 std::make_unique<base::TimeDelta[]>(num_scheduling_threads);
jamesr2e146d72014-09-29 21:12:44139
140 for (int i = 0; i < num_scheduling_threads; ++i) {
Jeremy Roman9532f252017-08-16 23:27:24141 scheduling_threads.push_back(std::make_unique<Thread>("posting thread"));
jamesr2e146d72014-09-29 21:12:44142 scheduling_threads[i]->Start();
143 }
144
145 for (int i = 0; i < num_scheduling_threads; ++i) {
fdoray6ef45cf2016-08-25 15:36:37146 scheduling_threads[i]->task_runner()->PostTask(
tzik92b7a422017-04-11 15:00:44147 FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule,
148 base::Unretained(this), i));
jamesr2e146d72014-09-29 21:12:44149 }
150
151 for (int i = 0; i < num_scheduling_threads; ++i) {
152 scheduling_threads[i]->Stop();
153 }
Xiaohan Wang29b5fdd2022-01-15 19:10:02154#if BUILDFLAG(IS_ANDROID)
Carlos Caballerodd8bf7b042019-07-30 14:14:15155 if (target_type == MessagePumpType::JAVA) {
jamesr2e146d72014-09-29 21:12:44156 java_thread_->Stop();
157 java_thread_.reset();
158 } else
159#endif
160 {
161 target_->Stop();
162 target_.reset();
163 }
164 base::TimeDelta total_time;
165 base::TimeDelta total_thread_time;
166 base::TimeDelta min_batch_time = base::TimeDelta::Max();
167 base::TimeDelta max_batch_time = base::TimeDelta();
168 for (int i = 0; i < num_scheduling_threads; ++i) {
169 total_time += scheduling_times_[i];
170 total_thread_time += scheduling_thread_times_[i];
171 min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
172 max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
173 }
Brian Sheedyf7adb4b2019-12-19 02:23:18174
175 std::string story_name = StringPrintf(
176 "%s_pump_from_%d_threads",
Carlos Caballerodd8bf7b042019-07-30 14:14:15177 target_type == MessagePumpType::IO
jamesr2e146d72014-09-29 21:12:44178 ? "io"
Brian Sheedyf7adb4b2019-12-19 02:23:18179 : (target_type == MessagePumpType::UI ? "ui" : "default"),
180 num_scheduling_threads);
181 auto reporter = SetUpReporter(story_name);
182 reporter.AddResult(kMetricMinBatchTime, total_time.InMicroseconds() /
183 static_cast<double>(counter_));
184 reporter.AddResult(
185 kMetricMaxBatchTime,
186 max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize));
187 reporter.AddResult(kMetricTotalTime, total_time.InMicroseconds() /
188 static_cast<double>(counter_));
miu1ab506a2015-05-29 23:57:12189 if (ThreadTicks::IsSupported()) {
Brian Sheedyf7adb4b2019-12-19 02:23:18190 reporter.AddResult(kMetricThreadTime, total_thread_time.InMicroseconds() /
191 static_cast<double>(counter_));
jamesr2e146d72014-09-29 21:12:44192 }
193 }
194
Carlos Caballero14712af92019-04-17 16:13:01195 sequence_manager::internal::SequenceManagerImpl* target_message_loop_base() {
Xiaohan Wang29b5fdd2022-01-15 19:10:02196#if BUILDFLAG(IS_ANDROID)
Alex Clarkee1cc1bb2019-06-06 17:24:25197 if (java_thread_) {
198 return static_cast<sequence_manager::internal::SequenceManagerImpl*>(
Gabriel Charette35f26252019-08-12 12:01:20199 java_thread_->state()->sequence_manager.get());
Alex Clarkee1cc1bb2019-06-06 17:24:25200 }
jamesr2e146d72014-09-29 21:12:44201#endif
Carlos Caballerob25fe8472020-07-17 10:27:17202 return CurrentThread::Get()->GetCurrentSequenceManagerImpl();
jamesr2e146d72014-09-29 21:12:44203 }
204
205 private:
Alex Clarke636d6b62019-02-22 01:39:04206 std::unique_ptr<Thread> target_;
Xiaohan Wang29b5fdd2022-01-15 19:10:02207#if BUILDFLAG(IS_ANDROID)
Alex Clarkee1cc1bb2019-06-06 17:24:25208 std::unique_ptr<JavaHandlerThreadForTest> java_thread_;
jamesr2e146d72014-09-29 21:12:44209#endif
dcheng093de9b2016-04-04 21:25:51210 std::unique_ptr<base::TimeDelta[]> scheduling_times_;
211 std::unique_ptr<base::TimeDelta[]> scheduling_thread_times_;
212 std::unique_ptr<base::TimeDelta[]> min_batch_times_;
213 std::unique_ptr<base::TimeDelta[]> max_batch_times_;
Peter Kasting811504a72025-01-09 03:18:50214 uint64_t counter_ = 0;
jamesr2e146d72014-09-29 21:12:44215
216 static const size_t kTargetTimeSec = 5;
217 static const size_t kBatchSize = 1000;
218};
219
220TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15221 ScheduleWork(MessagePumpType::IO, 1);
jamesr2e146d72014-09-29 21:12:44222}
223
224TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15225 ScheduleWork(MessagePumpType::IO, 2);
jamesr2e146d72014-09-29 21:12:44226}
227
228TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15229 ScheduleWork(MessagePumpType::IO, 4);
jamesr2e146d72014-09-29 21:12:44230}
231
232TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15233 ScheduleWork(MessagePumpType::UI, 1);
jamesr2e146d72014-09-29 21:12:44234}
235
236TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15237 ScheduleWork(MessagePumpType::UI, 2);
jamesr2e146d72014-09-29 21:12:44238}
239
240TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15241 ScheduleWork(MessagePumpType::UI, 4);
jamesr2e146d72014-09-29 21:12:44242}
243
244TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15245 ScheduleWork(MessagePumpType::DEFAULT, 1);
jamesr2e146d72014-09-29 21:12:44246}
247
248TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15249 ScheduleWork(MessagePumpType::DEFAULT, 2);
jamesr2e146d72014-09-29 21:12:44250}
251
252TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15253 ScheduleWork(MessagePumpType::DEFAULT, 4);
jamesr2e146d72014-09-29 21:12:44254}
255
Xiaohan Wang29b5fdd2022-01-15 19:10:02256#if BUILDFLAG(IS_ANDROID)
jamesr2e146d72014-09-29 21:12:44257TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15258 ScheduleWork(MessagePumpType::JAVA, 1);
jamesr2e146d72014-09-29 21:12:44259}
260
261TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15262 ScheduleWork(MessagePumpType::JAVA, 2);
jamesr2e146d72014-09-29 21:12:44263}
264
265TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
Carlos Caballerodd8bf7b042019-07-30 14:14:15266 ScheduleWork(MessagePumpType::JAVA, 4);
jamesr2e146d72014-09-29 21:12:44267}
268#endif
269
jamesr2e146d72014-09-29 21:12:44270} // namespace base