xref: /aosp_15_r20/external/cronet/base/synchronization/waitable_event_perftest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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