diff options
Diffstat (limited to 'base/message_loop/message_pump_perftest.cc')
-rw-r--r-- | base/message_loop/message_pump_perftest.cc | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc new file mode 100644 index 0000000000..71ed4912d8 --- /dev/null +++ b/base/message_loop/message_pump_perftest.cc @@ -0,0 +1,243 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> +#include <stdint.h> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/format_macros.h" +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/stringprintf.h" +#include "base/synchronization/condition_variable.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/perf/perf_test.h" + +#if defined(OS_ANDROID) +#include "base/android/java_handler_thread.h" +#endif + +namespace base { + +class ScheduleWorkTest : public testing::Test { + public: + ScheduleWorkTest() : counter_(0) {} + + void SetUp() override { + if (base::ThreadTicks::IsSupported()) + base::ThreadTicks::WaitUntilInitialized(); + } + + void Increment(uint64_t amount) { counter_ += amount; } + + void Schedule(int index) { + base::TimeTicks start = base::TimeTicks::Now(); + base::ThreadTicks thread_start; + if (ThreadTicks::IsSupported()) + thread_start = base::ThreadTicks::Now(); + base::TimeDelta minimum = base::TimeDelta::Max(); + base::TimeDelta maximum = base::TimeDelta(); + base::TimeTicks now, lastnow = start; + uint64_t schedule_calls = 0u; + do { + for (size_t i = 0; i < kBatchSize; ++i) { + target_message_loop()->ScheduleWork(); + schedule_calls++; + } + now = base::TimeTicks::Now(); + base::TimeDelta laptime = now - lastnow; + lastnow = now; + minimum = std::min(minimum, laptime); + maximum = std::max(maximum, laptime); + } while (now - start < base::TimeDelta::FromSeconds(kTargetTimeSec)); + + scheduling_times_[index] = now - start; + if (ThreadTicks::IsSupported()) + scheduling_thread_times_[index] = + base::ThreadTicks::Now() - thread_start; + min_batch_times_[index] = minimum; + max_batch_times_[index] = maximum; + target_message_loop()->task_runner()->PostTask( + FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment, + base::Unretained(this), schedule_calls)); + } + + void ScheduleWork(MessageLoop::Type target_type, int num_scheduling_threads) { +#if defined(OS_ANDROID) + if (target_type == MessageLoop::TYPE_JAVA) { + java_thread_.reset(new android::JavaHandlerThread("target")); + java_thread_->Start(); + } else +#endif + { + target_.reset(new Thread("target")); + target_->StartWithOptions(Thread::Options(target_type, 0u)); + + // Without this, it's possible for the scheduling threads to start and run + // before the target thread. In this case, the scheduling threads will + // call target_message_loop()->ScheduleWork(), which dereferences the + // loop's message pump, which is only created after the target thread has + // finished starting. + target_->WaitUntilThreadStarted(); + } + + std::vector<std::unique_ptr<Thread>> scheduling_threads; + scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]); + scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]); + min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]); + max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]); + + for (int i = 0; i < num_scheduling_threads; ++i) { + scheduling_threads.push_back(std::make_unique<Thread>("posting thread")); + scheduling_threads[i]->Start(); + } + + for (int i = 0; i < num_scheduling_threads; ++i) { + scheduling_threads[i]->task_runner()->PostTask( + FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule, + base::Unretained(this), i)); + } + + for (int i = 0; i < num_scheduling_threads; ++i) { + scheduling_threads[i]->Stop(); + } +#if defined(OS_ANDROID) + if (target_type == MessageLoop::TYPE_JAVA) { + java_thread_->Stop(); + java_thread_.reset(); + } else +#endif + { + target_->Stop(); + target_.reset(); + } + base::TimeDelta total_time; + base::TimeDelta total_thread_time; + base::TimeDelta min_batch_time = base::TimeDelta::Max(); + base::TimeDelta max_batch_time = base::TimeDelta(); + for (int i = 0; i < num_scheduling_threads; ++i) { + total_time += scheduling_times_[i]; + total_thread_time += scheduling_thread_times_[i]; + min_batch_time = std::min(min_batch_time, min_batch_times_[i]); + max_batch_time = std::max(max_batch_time, max_batch_times_[i]); + } + std::string trace = StringPrintf( + "%d_threads_scheduling_to_%s_pump", + num_scheduling_threads, + target_type == MessageLoop::TYPE_IO + ? "io" + : (target_type == MessageLoop::TYPE_UI ? "ui" : "default")); + perf_test::PrintResult( + "task", + "", + trace, + total_time.InMicroseconds() / static_cast<double>(counter_), + "us/task", + true); + perf_test::PrintResult( + "task", + "_min_batch_time", + trace, + min_batch_time.InMicroseconds() / static_cast<double>(kBatchSize), + "us/task", + false); + perf_test::PrintResult( + "task", + "_max_batch_time", + trace, + max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize), + "us/task", + false); + if (ThreadTicks::IsSupported()) { + perf_test::PrintResult( + "task", + "_thread_time", + trace, + total_thread_time.InMicroseconds() / static_cast<double>(counter_), + "us/task", + true); + } + } + + MessageLoop* target_message_loop() { +#if defined(OS_ANDROID) + if (java_thread_) + return java_thread_->message_loop(); +#endif + return target_->message_loop(); + } + + private: + std::unique_ptr<Thread> target_; +#if defined(OS_ANDROID) + std::unique_ptr<android::JavaHandlerThread> java_thread_; +#endif + std::unique_ptr<base::TimeDelta[]> scheduling_times_; + std::unique_ptr<base::TimeDelta[]> scheduling_thread_times_; + std::unique_ptr<base::TimeDelta[]> min_batch_times_; + std::unique_ptr<base::TimeDelta[]> max_batch_times_; + uint64_t counter_; + + static const size_t kTargetTimeSec = 5; + static const size_t kBatchSize = 1000; +}; + +TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) { + ScheduleWork(MessageLoop::TYPE_IO, 1); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) { + ScheduleWork(MessageLoop::TYPE_IO, 2); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) { + ScheduleWork(MessageLoop::TYPE_IO, 4); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) { + ScheduleWork(MessageLoop::TYPE_UI, 1); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) { + ScheduleWork(MessageLoop::TYPE_UI, 2); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) { + ScheduleWork(MessageLoop::TYPE_UI, 4); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) { + ScheduleWork(MessageLoop::TYPE_DEFAULT, 1); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) { + ScheduleWork(MessageLoop::TYPE_DEFAULT, 2); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) { + ScheduleWork(MessageLoop::TYPE_DEFAULT, 4); +} + +#if defined(OS_ANDROID) +TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) { + ScheduleWork(MessageLoop::TYPE_JAVA, 1); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) { + ScheduleWork(MessageLoop::TYPE_JAVA, 2); +} + +TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) { + ScheduleWork(MessageLoop::TYPE_JAVA, 4); +} +#endif + +} // namespace base |