Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / base / threading / thread_perftest.cc
1 // Copyright 2014 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/base_switches.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/memory/scoped_vector.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/synchronization/condition_variable.h"
11 #include "base/synchronization/lock.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/threading/thread.h"
14 #include "base/time/time.h"
15 #include "build/build_config.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "testing/perf/perf_test.h"
18
19 #if defined(OS_POSIX)
20 #include <pthread.h>
21 #endif
22
23 namespace base {
24
25 namespace {
26
27 const int kNumRuns = 100000;
28
29 // Base class for a threading perf-test. This sets up some threads for the
30 // test and measures the clock-time in addition to time spent on each thread.
31 class ThreadPerfTest : public testing::Test {
32  public:
33   ThreadPerfTest()
34       : done_(false, false) {
35     // Disable the task profiler as it adds significant cost!
36     CommandLine::Init(0, NULL);
37     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
38         switches::kProfilerTiming,
39         switches::kProfilerTimingDisabledValue);
40   }
41
42   // To be implemented by each test. Subclass must uses threads_ such that
43   // their cpu-time can be measured. Test must return from PingPong() _and_
44   // call FinishMeasurement from any thread to complete the test.
45   virtual void Init() {}
46   virtual void PingPong(int hops) = 0;
47   virtual void Reset() {}
48
49   void TimeOnThread(base::TimeTicks* ticks, base::WaitableEvent* done) {
50     *ticks = base::TimeTicks::ThreadNow();
51     done->Signal();
52   }
53
54   base::TimeTicks ThreadNow(base::Thread* thread) {
55     base::WaitableEvent done(false, false);
56     base::TimeTicks ticks;
57     thread->message_loop_proxy()->PostTask(
58         FROM_HERE,
59         base::Bind(&ThreadPerfTest::TimeOnThread,
60                    base::Unretained(this),
61                    &ticks,
62                    &done));
63     done.Wait();
64     return ticks;
65   }
66
67   void RunPingPongTest(const std::string& name, unsigned num_threads) {
68     // Create threads and collect starting cpu-time for each thread.
69     std::vector<base::TimeTicks> thread_starts;
70     while (threads_.size() < num_threads) {
71       threads_.push_back(new base::Thread("PingPonger"));
72       threads_.back()->Start();
73       if (base::TimeTicks::IsThreadNowSupported())
74         thread_starts.push_back(ThreadNow(threads_.back()));
75     }
76
77     Init();
78
79     base::TimeTicks start = base::TimeTicks::HighResNow();
80     PingPong(kNumRuns);
81     done_.Wait();
82     base::TimeTicks end = base::TimeTicks::HighResNow();
83
84     // Gather the cpu-time spent on each thread. This does one extra tasks,
85     // but that should be in the noise given enough runs.
86     base::TimeDelta thread_time;
87     while (threads_.size()) {
88       if (base::TimeTicks::IsThreadNowSupported()) {
89         thread_time += ThreadNow(threads_.back()) - thread_starts.back();
90         thread_starts.pop_back();
91       }
92       threads_.pop_back();
93     }
94
95     Reset();
96
97     double num_runs = static_cast<double>(kNumRuns);
98     double us_per_task_clock = (end - start).InMicroseconds() / num_runs;
99     double us_per_task_cpu = thread_time.InMicroseconds() / num_runs;
100
101     // Clock time per task.
102     perf_test::PrintResult(
103         "task", "", name + "_time ", us_per_task_clock, "us/hop", true);
104
105     // Total utilization across threads if available (likely higher).
106     if (base::TimeTicks::IsThreadNowSupported()) {
107       perf_test::PrintResult(
108           "task", "", name + "_cpu ", us_per_task_cpu, "us/hop", true);
109     }
110   }
111
112  protected:
113   void FinishMeasurement() { done_.Signal(); }
114   ScopedVector<base::Thread> threads_;
115
116  private:
117   base::WaitableEvent done_;
118 };
119
120 // Class to test task performance by posting empty tasks back and forth.
121 class TaskPerfTest : public ThreadPerfTest {
122   base::Thread* NextThread(int count) {
123     return threads_[count % threads_.size()];
124   }
125
126   void PingPong(int hops) override {
127     if (!hops) {
128       FinishMeasurement();
129       return;
130     }
131     NextThread(hops)->message_loop_proxy()->PostTask(
132         FROM_HERE,
133         base::Bind(
134             &ThreadPerfTest::PingPong, base::Unretained(this), hops - 1));
135   }
136 };
137
138 // This tries to test the 'best-case' as well as the 'worst-case' task posting
139 // performance. The best-case keeps one thread alive such that it never yeilds,
140 // while the worse-case forces a context switch for every task. Four threads are
141 // used to ensure the threads do yeild (with just two it might be possible for
142 // both threads to stay awake if they can signal each other fast enough).
143 TEST_F(TaskPerfTest, TaskPingPong) {
144   RunPingPongTest("1_Task_Threads", 1);
145   RunPingPongTest("4_Task_Threads", 4);
146 }
147
148
149 // Same as above, but add observers to test their perf impact.
150 class MessageLoopObserver : public base::MessageLoop::TaskObserver {
151  public:
152   void WillProcessTask(const base::PendingTask& pending_task) override {}
153   void DidProcessTask(const base::PendingTask& pending_task) override {}
154 };
155 MessageLoopObserver message_loop_observer;
156
157 class TaskObserverPerfTest : public TaskPerfTest {
158  public:
159   void Init() override {
160     TaskPerfTest::Init();
161     for (size_t i = 0; i < threads_.size(); i++) {
162       threads_[i]->message_loop()->AddTaskObserver(&message_loop_observer);
163     }
164   }
165 };
166
167 TEST_F(TaskObserverPerfTest, TaskPingPong) {
168   RunPingPongTest("1_Task_Threads_With_Observer", 1);
169   RunPingPongTest("4_Task_Threads_With_Observer", 4);
170 }
171
172 // Class to test our WaitableEvent performance by signaling back and fort.
173 // WaitableEvent is templated so we can also compare with other versions.
174 template <typename WaitableEventType>
175 class EventPerfTest : public ThreadPerfTest {
176  public:
177   virtual void Init() override {
178     for (size_t i = 0; i < threads_.size(); i++)
179       events_.push_back(new WaitableEventType(false, false));
180   }
181
182   virtual void Reset() override { events_.clear(); }
183
184   void WaitAndSignalOnThread(size_t event) {
185     size_t next_event = (event + 1) % events_.size();
186     int my_hops = 0;
187     do {
188       events_[event]->Wait();
189       my_hops = --remaining_hops_;  // We own 'hops' between Wait and Signal.
190       events_[next_event]->Signal();
191     } while (my_hops > 0);
192     // Once we are done, all threads will signal as hops passes zero.
193     // We only signal completion once, on the thread that reaches zero.
194     if (!my_hops)
195       FinishMeasurement();
196   }
197
198   virtual void PingPong(int hops) override {
199     remaining_hops_ = hops;
200     for (size_t i = 0; i < threads_.size(); i++) {
201       threads_[i]->message_loop_proxy()->PostTask(
202           FROM_HERE,
203           base::Bind(&EventPerfTest::WaitAndSignalOnThread,
204                      base::Unretained(this),
205                      i));
206     }
207
208     // Kick off the Signal ping-ponging.
209     events_.front()->Signal();
210   }
211
212   int remaining_hops_;
213   ScopedVector<WaitableEventType> events_;
214 };
215
216 // Similar to the task posting test, this just tests similar functionality
217 // using WaitableEvents. We only test four threads (worst-case), but we
218 // might want to craft a way to test the best-case (where the thread doesn't
219 // end up blocking because the event is already signalled).
220 typedef EventPerfTest<base::WaitableEvent> WaitableEventPerfTest;
221 TEST_F(WaitableEventPerfTest, EventPingPong) {
222   RunPingPongTest("4_WaitableEvent_Threads", 4);
223 }
224
225 // Build a minimal event using ConditionVariable.
226 class ConditionVariableEvent {
227  public:
228   ConditionVariableEvent(bool manual_reset, bool initially_signaled)
229       : cond_(&lock_), signaled_(false) {
230     DCHECK(!manual_reset);
231     DCHECK(!initially_signaled);
232   }
233
234   void Signal() {
235     {
236       base::AutoLock scoped_lock(lock_);
237       signaled_ = true;
238     }
239     cond_.Signal();
240   }
241
242   void Wait() {
243     base::AutoLock scoped_lock(lock_);
244     while (!signaled_)
245       cond_.Wait();
246     signaled_ = false;
247   }
248
249  private:
250   base::Lock lock_;
251   base::ConditionVariable cond_;
252   bool signaled_;
253 };
254
255 // This is meant to test the absolute minimal context switching time
256 // using our own base synchronization code.
257 typedef EventPerfTest<ConditionVariableEvent> ConditionVariablePerfTest;
258 TEST_F(ConditionVariablePerfTest, EventPingPong) {
259   RunPingPongTest("4_ConditionVariable_Threads", 4);
260 }
261 #if defined(OS_POSIX)
262
263 // Absolutely 100% minimal posix waitable event. If there is a better/faster
264 // way to force a context switch, we should use that instead.
265 class PthreadEvent {
266  public:
267   PthreadEvent(bool manual_reset, bool initially_signaled) {
268     DCHECK(!manual_reset);
269     DCHECK(!initially_signaled);
270     pthread_mutex_init(&mutex_, 0);
271     pthread_cond_init(&cond_, 0);
272     signaled_ = false;
273   }
274
275   ~PthreadEvent() {
276     pthread_cond_destroy(&cond_);
277     pthread_mutex_destroy(&mutex_);
278   }
279
280   void Signal() {
281     pthread_mutex_lock(&mutex_);
282     signaled_ = true;
283     pthread_mutex_unlock(&mutex_);
284     pthread_cond_signal(&cond_);
285   }
286
287   void Wait() {
288     pthread_mutex_lock(&mutex_);
289     while (!signaled_)
290       pthread_cond_wait(&cond_, &mutex_);
291     signaled_ = false;
292     pthread_mutex_unlock(&mutex_);
293   }
294
295  private:
296   bool signaled_;
297   pthread_mutex_t mutex_;
298   pthread_cond_t cond_;
299 };
300
301 // This is meant to test the absolute minimal context switching time.
302 // If there is any faster way to do this we should substitute it in.
303 typedef EventPerfTest<PthreadEvent> PthreadEventPerfTest;
304 TEST_F(PthreadEventPerfTest, EventPingPong) {
305   RunPingPongTest("4_PthreadCondVar_Threads", 4);
306 }
307
308 #endif
309
310 }  // namespace
311
312 }  // namespace base