• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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/task/sequence_manager/sequence_manager.h"
6 
7 #include <stddef.h>
8 #include <memory>
9 
10 #include "base/bind.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/task/sequence_manager/task_queue_impl.h"
16 #include "base/task/sequence_manager/test/mock_time_domain.h"
17 #include "base/task/sequence_manager/test/sequence_manager_for_test.h"
18 #include "base/task/sequence_manager/test/test_task_queue.h"
19 #include "base/task/sequence_manager/test/test_task_time_observer.h"
20 #include "base/threading/thread.h"
21 #include "base/threading/thread_task_runner_handle.h"
22 #include "base/time/default_tick_clock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "testing/perf/perf_test.h"
25 
26 namespace base {
27 namespace sequence_manager {
28 
29 // To reduce noise related to the OS timer, we use a mock time domain to
30 // fast forward the timers.
31 class PerfTestTimeDomain : public MockTimeDomain {
32  public:
PerfTestTimeDomain()33   PerfTestTimeDomain() : MockTimeDomain(TimeTicks::Now()) {}
34   ~PerfTestTimeDomain() override = default;
35 
DelayTillNextTask(LazyNow * lazy_now)36   Optional<TimeDelta> DelayTillNextTask(LazyNow* lazy_now) override {
37     Optional<TimeTicks> run_time = NextScheduledRunTime();
38     if (!run_time)
39       return nullopt;
40     SetNowTicks(*run_time);
41     // Makes SequenceManager to continue immediately.
42     return TimeDelta();
43   }
44 
SetNextDelayedDoWork(LazyNow * lazy_now,TimeTicks run_time)45   void SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) override {
46     // De-dupe DoWorks.
47     if (NumberOfScheduledWakeUps() == 1u)
48       RequestDoWork();
49   }
50 
51  private:
52   DISALLOW_COPY_AND_ASSIGN(PerfTestTimeDomain);
53 };
54 
55 class SequenceManagerPerfTest : public testing::Test {
56  public:
SequenceManagerPerfTest()57   SequenceManagerPerfTest()
58       : num_queues_(0),
59         max_tasks_in_flight_(0),
60         num_tasks_in_flight_(0),
61         num_tasks_to_post_(0),
62         num_tasks_to_run_(0) {}
63 
SetUp()64   void SetUp() override {
65     if (ThreadTicks::IsSupported())
66       ThreadTicks::WaitUntilInitialized();
67   }
68 
TearDown()69   void TearDown() override {
70     queues_.clear();
71     manager_->UnregisterTimeDomain(time_domain_.get());
72     manager_.reset();
73   }
74 
Initialize(size_t num_queues)75   void Initialize(size_t num_queues) {
76     num_queues_ = num_queues;
77     message_loop_.reset(new MessageLoop());
78     manager_ = SequenceManagerForTest::Create(message_loop_.get(),
79                                               message_loop_->task_runner(),
80                                               DefaultTickClock::GetInstance());
81     manager_->AddTaskTimeObserver(&test_task_time_observer_);
82 
83     time_domain_.reset(new PerfTestTimeDomain());
84     manager_->RegisterTimeDomain(time_domain_.get());
85 
86     for (size_t i = 0; i < num_queues; i++) {
87       queues_.push_back(manager_->CreateTaskQueue<TestTaskQueue>(
88           TaskQueue::Spec("test").SetTimeDomain(time_domain_.get())));
89     }
90 
91     delayed_task_closure_ = BindRepeating(
92         &SequenceManagerPerfTest::TestDelayedTask, Unretained(this));
93 
94     immediate_task_closure_ = BindRepeating(
95         &SequenceManagerPerfTest::TestImmediateTask, Unretained(this));
96   }
97 
TestDelayedTask()98   void TestDelayedTask() {
99     if (--num_tasks_to_run_ == 0) {
100       run_loop_->QuitWhenIdle();
101       return;
102     }
103 
104     num_tasks_in_flight_--;
105     // NOTE there are only up to max_tasks_in_flight_ pending delayed tasks at
106     // any one time.  Thanks to the lower_num_tasks_to_post going to zero if
107     // there are a lot of tasks in flight, the total number of task in flight at
108     // any one time is very variable.
109     unsigned int lower_num_tasks_to_post =
110         num_tasks_in_flight_ < (max_tasks_in_flight_ / 2) ? 1 : 0;
111     unsigned int max_tasks_to_post =
112         num_tasks_to_post_ % 2 ? lower_num_tasks_to_post : 10;
113     for (unsigned int i = 0;
114          i < max_tasks_to_post && num_tasks_in_flight_ < max_tasks_in_flight_ &&
115          num_tasks_to_post_ > 0;
116          i++) {
117       // Choose a queue weighted towards queue 0.
118       unsigned int queue = num_tasks_to_post_ % (num_queues_ + 1);
119       if (queue == num_queues_) {
120         queue = 0;
121       }
122       // Simulate a mix of short and longer delays.
123       unsigned int delay =
124           num_tasks_to_post_ % 2 ? 1 : (10 + num_tasks_to_post_ % 10);
125       queues_[queue]->PostDelayedTask(FROM_HERE, delayed_task_closure_,
126                                       TimeDelta::FromMilliseconds(delay));
127       num_tasks_in_flight_++;
128       num_tasks_to_post_--;
129     }
130   }
131 
TestImmediateTask()132   void TestImmediateTask() {
133     if (--num_tasks_to_run_ == 0) {
134       run_loop_->QuitWhenIdle();
135       return;
136     }
137 
138     num_tasks_in_flight_--;
139     // NOTE there are only up to max_tasks_in_flight_ pending delayed tasks at
140     // any one time.  Thanks to the lower_num_tasks_to_post going to zero if
141     // there are a lot of tasks in flight, the total number of task in flight at
142     // any one time is very variable.
143     unsigned int lower_num_tasks_to_post =
144         num_tasks_in_flight_ < (max_tasks_in_flight_ / 2) ? 1 : 0;
145     unsigned int max_tasks_to_post =
146         num_tasks_to_post_ % 2 ? lower_num_tasks_to_post : 10;
147     for (unsigned int i = 0;
148          i < max_tasks_to_post && num_tasks_in_flight_ < max_tasks_in_flight_ &&
149          num_tasks_to_post_ > 0;
150          i++) {
151       // Choose a queue weighted towards queue 0.
152       unsigned int queue = num_tasks_to_post_ % (num_queues_ + 1);
153       if (queue == num_queues_) {
154         queue = 0;
155       }
156       queues_[queue]->PostTask(FROM_HERE, immediate_task_closure_);
157       num_tasks_in_flight_++;
158       num_tasks_to_post_--;
159     }
160   }
161 
ResetAndCallTestDelayedTask(unsigned int num_tasks_to_run)162   void ResetAndCallTestDelayedTask(unsigned int num_tasks_to_run) {
163     num_tasks_in_flight_ = 1;
164     num_tasks_to_post_ = num_tasks_to_run;
165     num_tasks_to_run_ = num_tasks_to_run;
166     TestDelayedTask();
167   }
168 
ResetAndCallTestImmediateTask(unsigned int num_tasks_to_run)169   void ResetAndCallTestImmediateTask(unsigned int num_tasks_to_run) {
170     num_tasks_in_flight_ = 1;
171     num_tasks_to_post_ = num_tasks_to_run;
172     num_tasks_to_run_ = num_tasks_to_run;
173     TestImmediateTask();
174   }
175 
Benchmark(const std::string & trace,const RepeatingClosure & test_task)176   void Benchmark(const std::string& trace, const RepeatingClosure& test_task) {
177     ThreadTicks start = ThreadTicks::Now();
178     ThreadTicks now;
179     unsigned long long num_iterations = 0;
180     do {
181       test_task.Run();
182       run_loop_.reset(new RunLoop());
183       run_loop_->Run();
184       now = ThreadTicks::Now();
185       num_iterations++;
186     } while (now - start < TimeDelta::FromSeconds(5));
187     perf_test::PrintResult(
188         "task", "", trace,
189         (now - start).InMicroseconds() / static_cast<double>(num_iterations),
190         "us/run", true);
191   }
192 
193   size_t num_queues_;
194   unsigned int max_tasks_in_flight_;
195   unsigned int num_tasks_in_flight_;
196   unsigned int num_tasks_to_post_;
197   unsigned int num_tasks_to_run_;
198   std::unique_ptr<MessageLoop> message_loop_;
199   std::unique_ptr<SequenceManager> manager_;
200   std::unique_ptr<RunLoop> run_loop_;
201   std::unique_ptr<TimeDomain> time_domain_;
202   std::vector<scoped_refptr<SingleThreadTaskRunner>> queues_;
203   RepeatingClosure delayed_task_closure_;
204   RepeatingClosure immediate_task_closure_;
205   // TODO(alexclarke): parameterize so we can measure with and without a
206   // TaskTimeObserver.
207   TestTaskTimeObserver test_task_time_observer_;
208 };
209 
TEST_F(SequenceManagerPerfTest,RunTenThousandDelayedTasks_OneQueue)210 TEST_F(SequenceManagerPerfTest, RunTenThousandDelayedTasks_OneQueue) {
211   if (!ThreadTicks::IsSupported())
212     return;
213   Initialize(1u);
214 
215   max_tasks_in_flight_ = 200;
216   Benchmark("run 10000 delayed tasks with one queue",
217             BindRepeating(&SequenceManagerPerfTest::ResetAndCallTestDelayedTask,
218                           Unretained(this), 10000));
219 }
220 
TEST_F(SequenceManagerPerfTest,RunTenThousandDelayedTasks_FourQueues)221 TEST_F(SequenceManagerPerfTest, RunTenThousandDelayedTasks_FourQueues) {
222   if (!ThreadTicks::IsSupported())
223     return;
224   Initialize(4u);
225 
226   max_tasks_in_flight_ = 200;
227   Benchmark("run 10000 delayed tasks with four queues",
228             BindRepeating(&SequenceManagerPerfTest::ResetAndCallTestDelayedTask,
229                           Unretained(this), 10000));
230 }
231 
TEST_F(SequenceManagerPerfTest,RunTenThousandDelayedTasks_EightQueues)232 TEST_F(SequenceManagerPerfTest, RunTenThousandDelayedTasks_EightQueues) {
233   if (!ThreadTicks::IsSupported())
234     return;
235   Initialize(8u);
236 
237   max_tasks_in_flight_ = 200;
238   Benchmark("run 10000 delayed tasks with eight queues",
239             BindRepeating(&SequenceManagerPerfTest::ResetAndCallTestDelayedTask,
240                           Unretained(this), 10000));
241 }
242 
TEST_F(SequenceManagerPerfTest,RunTenThousandDelayedTasks_ThirtyTwoQueues)243 TEST_F(SequenceManagerPerfTest, RunTenThousandDelayedTasks_ThirtyTwoQueues) {
244   if (!ThreadTicks::IsSupported())
245     return;
246   Initialize(32u);
247 
248   max_tasks_in_flight_ = 200;
249   Benchmark("run 10000 delayed tasks with thirty two queues",
250             BindRepeating(&SequenceManagerPerfTest::ResetAndCallTestDelayedTask,
251                           Unretained(this), 10000));
252 }
253 
TEST_F(SequenceManagerPerfTest,RunTenThousandImmediateTasks_OneQueue)254 TEST_F(SequenceManagerPerfTest, RunTenThousandImmediateTasks_OneQueue) {
255   if (!ThreadTicks::IsSupported())
256     return;
257   Initialize(1u);
258 
259   max_tasks_in_flight_ = 200;
260   Benchmark(
261       "run 10000 immediate tasks with one queue",
262       BindRepeating(&SequenceManagerPerfTest::ResetAndCallTestImmediateTask,
263                     Unretained(this), 10000));
264 }
265 
TEST_F(SequenceManagerPerfTest,RunTenThousandImmediateTasks_FourQueues)266 TEST_F(SequenceManagerPerfTest, RunTenThousandImmediateTasks_FourQueues) {
267   if (!ThreadTicks::IsSupported())
268     return;
269   Initialize(4u);
270 
271   max_tasks_in_flight_ = 200;
272   Benchmark(
273       "run 10000 immediate tasks with four queues",
274       BindRepeating(&SequenceManagerPerfTest::ResetAndCallTestImmediateTask,
275                     Unretained(this), 10000));
276 }
277 
TEST_F(SequenceManagerPerfTest,RunTenThousandImmediateTasks_EightQueues)278 TEST_F(SequenceManagerPerfTest, RunTenThousandImmediateTasks_EightQueues) {
279   if (!ThreadTicks::IsSupported())
280     return;
281   Initialize(8u);
282 
283   max_tasks_in_flight_ = 200;
284   Benchmark(
285       "run 10000 immediate tasks with eight queues",
286       BindRepeating(&SequenceManagerPerfTest::ResetAndCallTestImmediateTask,
287                     Unretained(this), 10000));
288 }
289 
TEST_F(SequenceManagerPerfTest,RunTenThousandImmediateTasks_ThirtyTwoQueues)290 TEST_F(SequenceManagerPerfTest, RunTenThousandImmediateTasks_ThirtyTwoQueues) {
291   if (!ThreadTicks::IsSupported())
292     return;
293   Initialize(32u);
294 
295   max_tasks_in_flight_ = 200;
296   Benchmark(
297       "run 10000 immediate tasks with thirty two queues",
298       BindRepeating(&SequenceManagerPerfTest::ResetAndCallTestImmediateTask,
299                     Unretained(this), 10000));
300 }
301 
302 // TODO(alexclarke): Add additional tests with different mixes of non-delayed vs
303 // delayed tasks.
304 
305 }  // namespace sequence_manager
306 }  // namespace base
307