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