summaryrefslogtreecommitdiff
path: root/base/message_loop/message_pump_perftest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'base/message_loop/message_pump_perftest.cc')
-rw-r--r--base/message_loop/message_pump_perftest.cc243
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