Home | History | Annotate | Download | only in message_loop
      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