1 // Copyright 2017 The Chromium Authors
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/memory/raw_ptr.h"
6 #include "base/synchronization/waitable_event.h"
7
8 #include <string>
9
10 #include "base/threading/simple_thread.h"
11 #include "base/time/time.h"
12 #include "base/timer/elapsed_timer.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "testing/perf/perf_result_reporter.h"
15
16 namespace base {
17
18 namespace {
19
20 constexpr char kMetricPrefixWaitableEvent[] = "WaitableEvent.";
21 constexpr char kMetricWaitTime[] = "wait_time_per_sample";
22 constexpr char kMetricSignalTime[] = "signal_time_per_sample";
23 constexpr char kMetricElapsedCycles[] = "elapsed_cycles";
24 constexpr char kStorySingleThread[] = "single_thread_1000_samples";
25 constexpr char kStoryMultiThreadWaiter[] = "multi_thread_1000_samples_waiter";
26 constexpr char kStoryMultiThreadSignaler[] =
27 "multi_thread_1000_samples_signaler";
28 constexpr char kStoryTimedThroughput[] = "timed_throughput";
29
SetUpReporter(const std::string & story_name)30 perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
31 perf_test::PerfResultReporter reporter(kMetricPrefixWaitableEvent,
32 story_name);
33 reporter.RegisterImportantMetric(kMetricWaitTime, "ns");
34 reporter.RegisterImportantMetric(kMetricSignalTime, "ns");
35 reporter.RegisterImportantMetric(kMetricElapsedCycles, "count");
36 return reporter;
37 }
38
39 class TraceWaitableEvent {
40 public:
41 TraceWaitableEvent() = default;
42
43 TraceWaitableEvent(const TraceWaitableEvent&) = delete;
44 TraceWaitableEvent& operator=(const TraceWaitableEvent&) = delete;
45
46 ~TraceWaitableEvent() = default;
47
Signal()48 void Signal() {
49 ElapsedTimer timer;
50 event_.Signal();
51 total_signal_time_ += timer.Elapsed();
52 ++signal_samples_;
53 }
54
Wait()55 void Wait() {
56 ElapsedTimer timer;
57 event_.Wait();
58 total_wait_time_ += timer.Elapsed();
59 ++wait_samples_;
60 }
61
TimedWaitUntil(const TimeTicks & end_time)62 bool TimedWaitUntil(const TimeTicks& end_time) {
63 ElapsedTimer timer;
64 const bool signaled = event_.TimedWait(end_time - timer.start_time());
65 total_wait_time_ += timer.Elapsed();
66 ++wait_samples_;
67 return signaled;
68 }
69
IsSignaled()70 bool IsSignaled() { return event_.IsSignaled(); }
71
total_signal_time() const72 TimeDelta total_signal_time() const { return total_signal_time_; }
total_wait_time() const73 TimeDelta total_wait_time() const { return total_wait_time_; }
signal_samples() const74 size_t signal_samples() const { return signal_samples_; }
wait_samples() const75 size_t wait_samples() const { return wait_samples_; }
76
77 private:
78 WaitableEvent event_{WaitableEvent::ResetPolicy::AUTOMATIC};
79
80 TimeDelta total_signal_time_;
81 TimeDelta total_wait_time_;
82
83 size_t signal_samples_ = 0U;
84 size_t wait_samples_ = 0U;
85 };
86
87 class SignalerThread : public SimpleThread {
88 public:
SignalerThread(TraceWaitableEvent * waiter,TraceWaitableEvent * signaler)89 SignalerThread(TraceWaitableEvent* waiter, TraceWaitableEvent* signaler)
90 : SimpleThread("WaitableEventPerfTest signaler"),
91 waiter_(waiter),
92 signaler_(signaler) {}
93
94 SignalerThread(const SignalerThread&) = delete;
95 SignalerThread& operator=(const SignalerThread&) = delete;
96
97 ~SignalerThread() override = default;
98
Run()99 void Run() override {
100 while (!stop_event_.IsSignaled()) {
101 if (waiter_)
102 waiter_->Wait();
103 if (signaler_)
104 signaler_->Signal();
105 }
106 }
107
108 // Signals the thread to stop on the next iteration of its loop (which
109 // will happen immediately if no |waiter_| is present or is signaled.
RequestStop()110 void RequestStop() { stop_event_.Signal(); }
111
112 private:
113 WaitableEvent stop_event_;
114 raw_ptr<TraceWaitableEvent> waiter_;
115 raw_ptr<TraceWaitableEvent> signaler_;
116 };
117
PrintPerfWaitableEvent(const TraceWaitableEvent * event,const std::string & story_name,size_t * elapsed_cycles=nullptr)118 void PrintPerfWaitableEvent(const TraceWaitableEvent* event,
119 const std::string& story_name,
120 size_t* elapsed_cycles = nullptr) {
121 auto reporter = SetUpReporter(story_name);
122 reporter.AddResult(
123 kMetricSignalTime,
124 static_cast<size_t>(event->total_signal_time().InNanoseconds()) /
125 event->signal_samples());
126 reporter.AddResult(
127 kMetricWaitTime,
128 static_cast<size_t>(event->total_wait_time().InNanoseconds()) /
129 event->wait_samples());
130 if (elapsed_cycles) {
131 reporter.AddResult(kMetricElapsedCycles, *elapsed_cycles);
132 }
133 }
134
135 } // namespace
136
TEST(WaitableEventPerfTest,SingleThread)137 TEST(WaitableEventPerfTest, SingleThread) {
138 const size_t kSamples = 1000;
139
140 TraceWaitableEvent event;
141
142 for (size_t i = 0; i < kSamples; ++i) {
143 event.Signal();
144 event.Wait();
145 }
146
147 PrintPerfWaitableEvent(&event, kStorySingleThread);
148 }
149
TEST(WaitableEventPerfTest,MultipleThreads)150 TEST(WaitableEventPerfTest, MultipleThreads) {
151 const size_t kSamples = 1000;
152
153 TraceWaitableEvent waiter;
154 TraceWaitableEvent signaler;
155
156 // The other thread will wait and signal on the respective opposite events.
157 SignalerThread thread(&signaler, &waiter);
158 thread.Start();
159
160 for (size_t i = 0; i < kSamples; ++i) {
161 signaler.Signal();
162 waiter.Wait();
163 }
164
165 // Signal the stop event and then make sure the signaler event it is
166 // waiting on is also signaled.
167 thread.RequestStop();
168 signaler.Signal();
169
170 thread.Join();
171
172 PrintPerfWaitableEvent(&waiter, kStoryMultiThreadWaiter);
173 PrintPerfWaitableEvent(&signaler, kStoryMultiThreadSignaler);
174 }
175
TEST(WaitableEventPerfTest,Throughput)176 TEST(WaitableEventPerfTest, Throughput) {
177 TraceWaitableEvent event;
178
179 SignalerThread thread(nullptr, &event);
180 thread.Start();
181
182 const TimeTicks end_time = TimeTicks::Now() + Seconds(1);
183 size_t count = 0;
184 while (event.TimedWaitUntil(end_time)) {
185 ++count;
186 }
187
188 thread.RequestStop();
189 thread.Join();
190
191 PrintPerfWaitableEvent(&event, kStoryTimedThroughput, &count);
192 }
193
194 } // namespace base
195