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