| // 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 |