• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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/synchronization/waitable_event.h"
6 
7 #include <numeric>
8 
9 #include "base/threading/simple_thread.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "testing/perf/perf_test.h"
12 
13 namespace base {
14 
15 namespace {
16 
17 class TraceWaitableEvent {
18  public:
TraceWaitableEvent(size_t samples)19   TraceWaitableEvent(size_t samples)
20       : event_(WaitableEvent::ResetPolicy::AUTOMATIC,
21                WaitableEvent::InitialState::NOT_SIGNALED),
22         samples_(samples) {
23     signal_times_.reserve(samples);
24     wait_times_.reserve(samples);
25   }
26 
27   ~TraceWaitableEvent() = default;
28 
Signal()29   void Signal() {
30     TimeTicks start = TimeTicks::Now();
31     event_.Signal();
32     signal_times_.push_back(TimeTicks::Now() - start);
33   }
34 
Wait()35   void Wait() {
36     TimeTicks start = TimeTicks::Now();
37     event_.Wait();
38     wait_times_.push_back(TimeTicks::Now() - start);
39   }
40 
TimedWaitUntil(const TimeTicks & end_time)41   bool TimedWaitUntil(const TimeTicks& end_time) {
42     TimeTicks start = TimeTicks::Now();
43     bool signaled = event_.TimedWaitUntil(end_time);
44     wait_times_.push_back(TimeTicks::Now() - start);
45     return signaled;
46   }
47 
IsSignaled()48   bool IsSignaled() { return event_.IsSignaled(); }
49 
signal_times() const50   const std::vector<TimeDelta>& signal_times() const { return signal_times_; }
wait_times() const51   const std::vector<TimeDelta>& wait_times() const { return wait_times_; }
samples() const52   size_t samples() const { return samples_; }
53 
54  private:
55   WaitableEvent event_;
56 
57   std::vector<TimeDelta> signal_times_;
58   std::vector<TimeDelta> wait_times_;
59 
60   const size_t samples_;
61 
62   DISALLOW_COPY_AND_ASSIGN(TraceWaitableEvent);
63 };
64 
65 class SignalerThread : public SimpleThread {
66  public:
SignalerThread(TraceWaitableEvent * waiter,TraceWaitableEvent * signaler)67   SignalerThread(TraceWaitableEvent* waiter, TraceWaitableEvent* signaler)
68       : SimpleThread("WaitableEventPerfTest signaler"),
69         waiter_(waiter),
70         signaler_(signaler) {}
71 
72   ~SignalerThread() override = default;
73 
Run()74   void Run() override {
75     while (!stop_event_.IsSignaled()) {
76       if (waiter_)
77         waiter_->Wait();
78       if (signaler_)
79         signaler_->Signal();
80     }
81   }
82 
83   // Signals the thread to stop on the next iteration of its loop (which
84   // will happen immediately if no |waiter_| is present or is signaled.
RequestStop()85   void RequestStop() { stop_event_.Signal(); }
86 
87  private:
88   WaitableEvent stop_event_{WaitableEvent::ResetPolicy::MANUAL,
89                             WaitableEvent::InitialState::NOT_SIGNALED};
90   TraceWaitableEvent* waiter_;
91   TraceWaitableEvent* signaler_;
92   DISALLOW_COPY_AND_ASSIGN(SignalerThread);
93 };
94 
PrintPerfWaitableEvent(const TraceWaitableEvent * event,const std::string & modifier,const std::string & trace)95 void PrintPerfWaitableEvent(const TraceWaitableEvent* event,
96                             const std::string& modifier,
97                             const std::string& trace) {
98   TimeDelta signal_time = std::accumulate(
99       event->signal_times().begin(), event->signal_times().end(), TimeDelta());
100   TimeDelta wait_time = std::accumulate(event->wait_times().begin(),
101                                         event->wait_times().end(), TimeDelta());
102   perf_test::PrintResult(
103       "signal_time", modifier, trace,
104       static_cast<size_t>(signal_time.InNanoseconds()) / event->samples(),
105       "ns/sample", true);
106   perf_test::PrintResult(
107       "wait_time", modifier, trace,
108       static_cast<size_t>(wait_time.InNanoseconds()) / event->samples(),
109       "ns/sample", true);
110 }
111 
112 }  // namespace
113 
TEST(WaitableEventPerfTest,SingleThread)114 TEST(WaitableEventPerfTest, SingleThread) {
115   const size_t kSamples = 1000;
116 
117   TraceWaitableEvent event(kSamples);
118 
119   for (size_t i = 0; i < kSamples; ++i) {
120     event.Signal();
121     event.Wait();
122   }
123 
124   PrintPerfWaitableEvent(&event, "", "singlethread-1000-samples");
125 }
126 
TEST(WaitableEventPerfTest,MultipleThreads)127 TEST(WaitableEventPerfTest, MultipleThreads) {
128   const size_t kSamples = 1000;
129 
130   TraceWaitableEvent waiter(kSamples);
131   TraceWaitableEvent signaler(kSamples);
132 
133   // The other thread will wait and signal on the respective opposite events.
134   SignalerThread thread(&signaler, &waiter);
135   thread.Start();
136 
137   for (size_t i = 0; i < kSamples; ++i) {
138     signaler.Signal();
139     waiter.Wait();
140   }
141 
142   // Signal the stop event and then make sure the signaler event it is
143   // waiting on is also signaled.
144   thread.RequestStop();
145   signaler.Signal();
146 
147   thread.Join();
148 
149   PrintPerfWaitableEvent(&waiter, "_waiter", "multithread-1000-samples");
150   PrintPerfWaitableEvent(&signaler, "_signaler", "multithread-1000-samples");
151 }
152 
TEST(WaitableEventPerfTest,Throughput)153 TEST(WaitableEventPerfTest, Throughput) {
154   // Reserve a lot of sample space.
155   const size_t kCapacity = 500000;
156   TraceWaitableEvent event(kCapacity);
157 
158   SignalerThread thread(nullptr, &event);
159   thread.Start();
160 
161   TimeTicks end_time = TimeTicks::Now() + TimeDelta::FromSeconds(1);
162   size_t count = 0;
163   while (event.TimedWaitUntil(end_time)) {
164     ++count;
165   }
166 
167   thread.RequestStop();
168   thread.Join();
169 
170   perf_test::PrintResult("counts", "", "throughput", count, "signals", true);
171   PrintPerfWaitableEvent(&event, "", "throughput");
172 
173   // Make sure that allocation didn't happen during the test.
174   EXPECT_LE(event.signal_times().capacity(), kCapacity);
175   EXPECT_LE(event.wait_times().capacity(), kCapacity);
176 }
177 
178 }  // namespace base
179