| // Copyright 2018 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 "base/message_loop/message_loop_task_runner.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/debug/task_annotator.h" |
| #include "base/macros.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/message_loop/message_loop_task_runner.h" |
| #include "base/message_loop/message_pump.h" |
| #include "base/message_loop/sequenced_task_source.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/time/time.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/perf/perf_test.h" |
| |
| namespace base { |
| |
| namespace { |
| |
| // Tests below will post tasks in a loop until |kPostTaskPerfTestDuration| has |
| // elapsed. |
| constexpr TimeDelta kPostTaskPerfTestDuration = |
| base::TimeDelta::FromSeconds(30); |
| |
| } // namespace |
| |
| class FakeObserver : public SequencedTaskSource::Observer { |
| public: |
| // SequencedTaskSource::Observer |
| void WillQueueTask(PendingTask* task) override {} |
| void DidQueueTask(bool was_empty) override {} |
| |
| virtual void RunTask(PendingTask* task) { std::move(task->task).Run(); } |
| }; |
| |
| // Exercises MessageLoopTaskRunner's multi-threaded queue in isolation. |
| class BasicPostTaskPerfTest : public testing::Test { |
| public: |
| void Run(int batch_size, |
| int tasks_per_reload, |
| std::unique_ptr<FakeObserver> task_source_observer) { |
| base::TimeTicks start = base::TimeTicks::Now(); |
| base::TimeTicks now; |
| FakeObserver* task_source_observer_raw = task_source_observer.get(); |
| auto message_loop_task_runner = |
| MakeRefCounted<internal::MessageLoopTaskRunner>( |
| std::move(task_source_observer)); |
| uint32_t num_posted = 0; |
| do { |
| for (int i = 0; i < batch_size; ++i) { |
| for (int j = 0; j < tasks_per_reload; ++j) { |
| message_loop_task_runner->PostTask(FROM_HERE, DoNothing()); |
| num_posted++; |
| } |
| // The outgoing queue will only be reloaded when first entering this |
| // loop. |
| while (message_loop_task_runner->HasTasks()) { |
| auto task = message_loop_task_runner->TakeTask(); |
| task_source_observer_raw->RunTask(&task); |
| } |
| } |
| |
| now = base::TimeTicks::Now(); |
| } while (now - start < kPostTaskPerfTestDuration); |
| std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload); |
| perf_test::PrintResult( |
| "task", "", trace, |
| (now - start).InMicroseconds() / static_cast<double>(num_posted), |
| "us/task", true); |
| } |
| }; |
| |
| TEST_F(BasicPostTaskPerfTest, OneTaskPerReload) { |
| Run(10000, 1, std::make_unique<FakeObserver>()); |
| } |
| |
| TEST_F(BasicPostTaskPerfTest, TenTasksPerReload) { |
| Run(10000, 10, std::make_unique<FakeObserver>()); |
| } |
| |
| TEST_F(BasicPostTaskPerfTest, OneHundredTasksPerReload) { |
| Run(1000, 100, std::make_unique<FakeObserver>()); |
| } |
| |
| class StubMessagePump : public MessagePump { |
| public: |
| StubMessagePump() = default; |
| ~StubMessagePump() override = default; |
| |
| // MessagePump: |
| void Run(Delegate* delegate) override {} |
| void Quit() override {} |
| void ScheduleWork() override {} |
| void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override {} |
| }; |
| |
| // Simulates the overhead of hooking TaskAnnotator and ScheduleWork() to the |
| // post task machinery. |
| class FakeObserverSimulatingOverhead : public FakeObserver { |
| public: |
| FakeObserverSimulatingOverhead() = default; |
| |
| // FakeObserver: |
| void WillQueueTask(PendingTask* task) final { |
| task_annotator_.WillQueueTask("MessageLoop::PostTask", task); |
| } |
| |
| void DidQueueTask(bool was_empty) final { |
| AutoLock scoped_lock(message_loop_lock_); |
| pump_->ScheduleWork(); |
| } |
| |
| void RunTask(PendingTask* task) final { |
| task_annotator_.RunTask("MessageLoop::PostTask", task); |
| } |
| |
| private: |
| // Simulates overhead from ScheduleWork() and TaskAnnotator calls involved in |
| // a real PostTask (stores the StubMessagePump in a pointer to force a virtual |
| // dispatch for ScheduleWork() and be closer to reality). |
| Lock message_loop_lock_; |
| std::unique_ptr<MessagePump> pump_{std::make_unique<StubMessagePump>()}; |
| debug::TaskAnnotator task_annotator_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeObserverSimulatingOverhead); |
| }; |
| |
| TEST_F(BasicPostTaskPerfTest, OneTaskPerReloadWithOverhead) { |
| Run(10000, 1, std::make_unique<FakeObserverSimulatingOverhead>()); |
| } |
| |
| TEST_F(BasicPostTaskPerfTest, TenTasksPerReloadWithOverhead) { |
| Run(10000, 10, std::make_unique<FakeObserverSimulatingOverhead>()); |
| } |
| |
| TEST_F(BasicPostTaskPerfTest, OneHundredTasksPerReloadWithOverhead) { |
| Run(1000, 100, std::make_unique<FakeObserverSimulatingOverhead>()); |
| } |
| |
| // Exercises the full MessageLoop/RunLoop machinery. |
| class IntegratedPostTaskPerfTest : public testing::Test { |
| public: |
| void Run(int batch_size, int tasks_per_reload) { |
| base::TimeTicks start = base::TimeTicks::Now(); |
| base::TimeTicks now; |
| MessageLoop loop; |
| uint32_t num_posted = 0; |
| do { |
| for (int i = 0; i < batch_size; ++i) { |
| for (int j = 0; j < tasks_per_reload; ++j) { |
| loop->task_runner()->PostTask(FROM_HERE, DoNothing()); |
| num_posted++; |
| } |
| RunLoop().RunUntilIdle(); |
| } |
| |
| now = base::TimeTicks::Now(); |
| } while (now - start < kPostTaskPerfTestDuration); |
| std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload); |
| perf_test::PrintResult( |
| "task", "", trace, |
| (now - start).InMicroseconds() / static_cast<double>(num_posted), |
| "us/task", true); |
| } |
| }; |
| |
| TEST_F(IntegratedPostTaskPerfTest, OneTaskPerReload) { |
| Run(10000, 1); |
| } |
| |
| TEST_F(IntegratedPostTaskPerfTest, TenTasksPerReload) { |
| Run(10000, 10); |
| } |
| |
| TEST_F(IntegratedPostTaskPerfTest, OneHundredTasksPerReload) { |
| Run(1000, 100); |
| } |
| |
| } // namespace base |