1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/message_loop/message_loop_task_runner.h" 6 7 #include <string> 8 #include <utility> 9 10 #include "base/bind_helpers.h" 11 #include "base/callback.h" 12 #include "base/debug/task_annotator.h" 13 #include "base/macros.h" 14 #include "base/memory/scoped_refptr.h" 15 #include "base/message_loop/incoming_task_queue.h" 16 #include "base/message_loop/message_loop.h" 17 #include "base/message_loop/message_loop_task_runner.h" 18 #include "base/message_loop/message_pump.h" 19 #include "base/run_loop.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/time/time.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 #include "testing/perf/perf_test.h" 24 25 namespace base { 26 27 namespace { 28 29 // Tests below will post tasks in a loop until |kPostTaskPerfTestDuration| has 30 // elapsed. 31 constexpr TimeDelta kPostTaskPerfTestDuration = 32 base::TimeDelta::FromSeconds(30); 33 34 } // namespace 35 36 class FakeObserver : public internal::IncomingTaskQueue::Observer { 37 public: 38 // IncomingTaskQueue::Observer 39 void WillQueueTask(PendingTask* task) override {} 40 void DidQueueTask(bool was_empty) override {} 41 42 virtual void RunTask(PendingTask* task) { std::move(task->task).Run(); } 43 }; 44 45 // Exercises MessageLoopTaskRunner's multi-threaded queue in isolation. 46 class BasicPostTaskPerfTest : public testing::Test { 47 public: 48 void Run(int batch_size, 49 int tasks_per_reload, 50 std::unique_ptr<FakeObserver> task_source_observer) { 51 base::TimeTicks start = base::TimeTicks::Now(); 52 base::TimeTicks now; 53 FakeObserver* task_source_observer_raw = task_source_observer.get(); 54 scoped_refptr<internal::IncomingTaskQueue> queue( 55 base::MakeRefCounted<internal::IncomingTaskQueue>( 56 std::move(task_source_observer))); 57 scoped_refptr<SingleThreadTaskRunner> task_runner( 58 base::MakeRefCounted<internal::MessageLoopTaskRunner>(queue)); 59 uint32_t num_posted = 0; 60 do { 61 for (int i = 0; i < batch_size; ++i) { 62 for (int j = 0; j < tasks_per_reload; ++j) { 63 task_runner->PostTask(FROM_HERE, DoNothing()); 64 num_posted++; 65 } 66 TaskQueue loop_local_queue; 67 queue->ReloadWorkQueue(&loop_local_queue); 68 while (!loop_local_queue.empty()) { 69 PendingTask t = std::move(loop_local_queue.front()); 70 loop_local_queue.pop(); 71 task_source_observer_raw->RunTask(&t); 72 } 73 } 74 75 now = base::TimeTicks::Now(); 76 } while (now - start < kPostTaskPerfTestDuration); 77 std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload); 78 perf_test::PrintResult( 79 "task", "", trace, 80 (now - start).InMicroseconds() / static_cast<double>(num_posted), 81 "us/task", true); 82 } 83 }; 84 85 TEST_F(BasicPostTaskPerfTest, OneTaskPerReload) { 86 Run(10000, 1, std::make_unique<FakeObserver>()); 87 } 88 89 TEST_F(BasicPostTaskPerfTest, TenTasksPerReload) { 90 Run(10000, 10, std::make_unique<FakeObserver>()); 91 } 92 93 TEST_F(BasicPostTaskPerfTest, OneHundredTasksPerReload) { 94 Run(1000, 100, std::make_unique<FakeObserver>()); 95 } 96 97 class StubMessagePump : public MessagePump { 98 public: 99 StubMessagePump() = default; 100 ~StubMessagePump() override = default; 101 102 // MessagePump: 103 void Run(Delegate* delegate) override {} 104 void Quit() override {} 105 void ScheduleWork() override {} 106 void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override {} 107 }; 108 109 // Simulates the overhead of hooking TaskAnnotator and ScheduleWork() to the 110 // post task machinery. 111 class FakeObserverSimulatingOverhead : public FakeObserver { 112 public: 113 FakeObserverSimulatingOverhead() = default; 114 115 // FakeObserver: 116 void WillQueueTask(PendingTask* task) final { 117 task_annotator_.WillQueueTask("MessageLoop::PostTask", task); 118 } 119 120 void DidQueueTask(bool was_empty) final { 121 AutoLock scoped_lock(message_loop_lock_); 122 pump_->ScheduleWork(); 123 } 124 125 void RunTask(PendingTask* task) final { 126 task_annotator_.RunTask("MessageLoop::PostTask", task); 127 } 128 129 private: 130 // Simulates overhead from ScheduleWork() and TaskAnnotator calls involved in 131 // a real PostTask (stores the StubMessagePump in a pointer to force a virtual 132 // dispatch for ScheduleWork() and be closer to reality). 133 Lock message_loop_lock_; 134 std::unique_ptr<MessagePump> pump_{std::make_unique<StubMessagePump>()}; 135 debug::TaskAnnotator task_annotator_; 136 137 DISALLOW_COPY_AND_ASSIGN(FakeObserverSimulatingOverhead); 138 }; 139 140 TEST_F(BasicPostTaskPerfTest, OneTaskPerReloadWithOverhead) { 141 Run(10000, 1, std::make_unique<FakeObserverSimulatingOverhead>()); 142 } 143 144 TEST_F(BasicPostTaskPerfTest, TenTasksPerReloadWithOverhead) { 145 Run(10000, 10, std::make_unique<FakeObserverSimulatingOverhead>()); 146 } 147 148 TEST_F(BasicPostTaskPerfTest, OneHundredTasksPerReloadWithOverhead) { 149 Run(1000, 100, std::make_unique<FakeObserverSimulatingOverhead>()); 150 } 151 152 // Exercises the full MessageLoop/RunLoop machinery. 153 class IntegratedPostTaskPerfTest : public testing::Test { 154 public: 155 void Run(int batch_size, int tasks_per_reload) { 156 base::TimeTicks start = base::TimeTicks::Now(); 157 base::TimeTicks now; 158 MessageLoop loop; 159 uint32_t num_posted = 0; 160 do { 161 for (int i = 0; i < batch_size; ++i) { 162 for (int j = 0; j < tasks_per_reload; ++j) { 163 loop->task_runner()->PostTask(FROM_HERE, DoNothing()); 164 num_posted++; 165 } 166 RunLoop().RunUntilIdle(); 167 } 168 169 now = base::TimeTicks::Now(); 170 } while (now - start < kPostTaskPerfTestDuration); 171 std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload); 172 perf_test::PrintResult( 173 "task", "", trace, 174 (now - start).InMicroseconds() / static_cast<double>(num_posted), 175 "us/task", true); 176 } 177 }; 178 179 TEST_F(IntegratedPostTaskPerfTest, OneTaskPerReload) { 180 Run(10000, 1); 181 } 182 183 TEST_F(IntegratedPostTaskPerfTest, TenTasksPerReload) { 184 Run(10000, 10); 185 } 186 187 TEST_F(IntegratedPostTaskPerfTest, OneHundredTasksPerReload) { 188 Run(1000, 100); 189 } 190 191 } // namespace base 192