xref: /aosp_15_r20/external/cronet/base/threading/counter_perftest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2021 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 <atomic>
6*6777b538SAndroid Build Coastguard Worker #include <string>
7*6777b538SAndroid Build Coastguard Worker 
8*6777b538SAndroid Build Coastguard Worker #include "base/barrier_closure.h"
9*6777b538SAndroid Build Coastguard Worker #include "base/functional/callback.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ptr.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/synchronization/lock.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/synchronization/waitable_event.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/threading/simple_thread.h"
14*6777b538SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
15*6777b538SAndroid Build Coastguard Worker #include "testing/perf/perf_result_reporter.h"
16*6777b538SAndroid Build Coastguard Worker 
17*6777b538SAndroid Build Coastguard Worker // This file contains tests to measure the cost of incrementing:
18*6777b538SAndroid Build Coastguard Worker // - A non-atomic variable, no lock.
19*6777b538SAndroid Build Coastguard Worker // - A non-atomic variable, with lock.
20*6777b538SAndroid Build Coastguard Worker // - An atomic variable, no memory barriers.
21*6777b538SAndroid Build Coastguard Worker // - An atomic variable, acquire-release barriers.
22*6777b538SAndroid Build Coastguard Worker // The goal is to provide data to guide counter implementation choices.
23*6777b538SAndroid Build Coastguard Worker 
24*6777b538SAndroid Build Coastguard Worker namespace base {
25*6777b538SAndroid Build Coastguard Worker 
26*6777b538SAndroid Build Coastguard Worker namespace {
27*6777b538SAndroid Build Coastguard Worker 
28*6777b538SAndroid Build Coastguard Worker constexpr char kMetricPrefixCounter[] = "Counter.";
29*6777b538SAndroid Build Coastguard Worker constexpr char kMetricOperationThroughput[] = "operation_throughput";
30*6777b538SAndroid Build Coastguard Worker constexpr uint64_t kNumIterations = 100000000;
31*6777b538SAndroid Build Coastguard Worker 
SetUpReporter(const std::string & story_name)32*6777b538SAndroid Build Coastguard Worker perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
33*6777b538SAndroid Build Coastguard Worker   perf_test::PerfResultReporter reporter(kMetricPrefixCounter, story_name);
34*6777b538SAndroid Build Coastguard Worker   reporter.RegisterImportantMetric(kMetricOperationThroughput, "operations/ms");
35*6777b538SAndroid Build Coastguard Worker   return reporter;
36*6777b538SAndroid Build Coastguard Worker }
37*6777b538SAndroid Build Coastguard Worker 
38*6777b538SAndroid Build Coastguard Worker class Uint64_NoLock {
39*6777b538SAndroid Build Coastguard Worker  public:
40*6777b538SAndroid Build Coastguard Worker   Uint64_NoLock() = default;
Increment()41*6777b538SAndroid Build Coastguard Worker   void Increment() { counter_ = counter_ + 1; }
value() const42*6777b538SAndroid Build Coastguard Worker   uint64_t value() const { return counter_; }
43*6777b538SAndroid Build Coastguard Worker 
44*6777b538SAndroid Build Coastguard Worker  private:
45*6777b538SAndroid Build Coastguard Worker   // Volatile to prevent the compiler from over-optimizing the increment.
46*6777b538SAndroid Build Coastguard Worker   volatile uint64_t counter_ = 0;
47*6777b538SAndroid Build Coastguard Worker };
48*6777b538SAndroid Build Coastguard Worker 
49*6777b538SAndroid Build Coastguard Worker class Uint64_Lock {
50*6777b538SAndroid Build Coastguard Worker  public:
51*6777b538SAndroid Build Coastguard Worker   Uint64_Lock() = default;
Increment()52*6777b538SAndroid Build Coastguard Worker   void Increment() {
53*6777b538SAndroid Build Coastguard Worker     AutoLock auto_lock(lock_);
54*6777b538SAndroid Build Coastguard Worker     ++counter_;
55*6777b538SAndroid Build Coastguard Worker   }
value() const56*6777b538SAndroid Build Coastguard Worker   uint64_t value() const {
57*6777b538SAndroid Build Coastguard Worker     AutoLock auto_lock(lock_);
58*6777b538SAndroid Build Coastguard Worker     return counter_;
59*6777b538SAndroid Build Coastguard Worker   }
60*6777b538SAndroid Build Coastguard Worker 
61*6777b538SAndroid Build Coastguard Worker  private:
62*6777b538SAndroid Build Coastguard Worker   mutable Lock lock_;
63*6777b538SAndroid Build Coastguard Worker   uint64_t counter_ GUARDED_BY(lock_) = 0;
64*6777b538SAndroid Build Coastguard Worker };
65*6777b538SAndroid Build Coastguard Worker 
66*6777b538SAndroid Build Coastguard Worker class AtomicUint64_NoBarrier {
67*6777b538SAndroid Build Coastguard Worker  public:
68*6777b538SAndroid Build Coastguard Worker   AtomicUint64_NoBarrier() = default;
Increment()69*6777b538SAndroid Build Coastguard Worker   void Increment() { counter_.fetch_add(1, std::memory_order_relaxed); }
value() const70*6777b538SAndroid Build Coastguard Worker   uint64_t value() const { return counter_; }
71*6777b538SAndroid Build Coastguard Worker 
72*6777b538SAndroid Build Coastguard Worker  private:
73*6777b538SAndroid Build Coastguard Worker   std::atomic<uint64_t> counter_{0};
74*6777b538SAndroid Build Coastguard Worker };
75*6777b538SAndroid Build Coastguard Worker 
76*6777b538SAndroid Build Coastguard Worker class AtomicUint64_Barrier {
77*6777b538SAndroid Build Coastguard Worker  public:
78*6777b538SAndroid Build Coastguard Worker   AtomicUint64_Barrier() = default;
Increment()79*6777b538SAndroid Build Coastguard Worker   void Increment() { counter_.fetch_add(1, std::memory_order_acq_rel); }
value() const80*6777b538SAndroid Build Coastguard Worker   uint64_t value() const { return counter_; }
81*6777b538SAndroid Build Coastguard Worker 
82*6777b538SAndroid Build Coastguard Worker  private:
83*6777b538SAndroid Build Coastguard Worker   std::atomic<uint64_t> counter_{0};
84*6777b538SAndroid Build Coastguard Worker };
85*6777b538SAndroid Build Coastguard Worker 
86*6777b538SAndroid Build Coastguard Worker template <typename CounterType>
87*6777b538SAndroid Build Coastguard Worker class IncrementThread : public SimpleThread {
88*6777b538SAndroid Build Coastguard Worker  public:
89*6777b538SAndroid Build Coastguard Worker   // Upon entering its main function, the thread waits for |start_event| to be
90*6777b538SAndroid Build Coastguard Worker   // signaled. Then, it increments |counter| |kNumIterations| times.
91*6777b538SAndroid Build Coastguard Worker   // Finally, it invokes |done_closure|.
IncrementThread(WaitableEvent * start_event,CounterType * counter,OnceClosure done_closure)92*6777b538SAndroid Build Coastguard Worker   explicit IncrementThread(WaitableEvent* start_event,
93*6777b538SAndroid Build Coastguard Worker                            CounterType* counter,
94*6777b538SAndroid Build Coastguard Worker                            OnceClosure done_closure)
95*6777b538SAndroid Build Coastguard Worker       : SimpleThread("IncrementThread"),
96*6777b538SAndroid Build Coastguard Worker         start_event_(start_event),
97*6777b538SAndroid Build Coastguard Worker         counter_(counter),
98*6777b538SAndroid Build Coastguard Worker         done_closure_(std::move(done_closure)) {}
99*6777b538SAndroid Build Coastguard Worker 
100*6777b538SAndroid Build Coastguard Worker   // SimpleThread:
Run()101*6777b538SAndroid Build Coastguard Worker   void Run() override {
102*6777b538SAndroid Build Coastguard Worker     start_event_->Wait();
103*6777b538SAndroid Build Coastguard Worker     for (uint64_t i = 0; i < kNumIterations; ++i)
104*6777b538SAndroid Build Coastguard Worker       counter_->Increment();
105*6777b538SAndroid Build Coastguard Worker     std::move(done_closure_).Run();
106*6777b538SAndroid Build Coastguard Worker   }
107*6777b538SAndroid Build Coastguard Worker 
108*6777b538SAndroid Build Coastguard Worker  private:
109*6777b538SAndroid Build Coastguard Worker   const raw_ptr<WaitableEvent> start_event_;
110*6777b538SAndroid Build Coastguard Worker   const raw_ptr<CounterType> counter_;
111*6777b538SAndroid Build Coastguard Worker   OnceClosure done_closure_;
112*6777b538SAndroid Build Coastguard Worker };
113*6777b538SAndroid Build Coastguard Worker 
114*6777b538SAndroid Build Coastguard Worker template <typename CounterType>
RunIncrementPerfTest(const std::string & story_name,int num_threads)115*6777b538SAndroid Build Coastguard Worker void RunIncrementPerfTest(const std::string& story_name, int num_threads) {
116*6777b538SAndroid Build Coastguard Worker   WaitableEvent start_event;
117*6777b538SAndroid Build Coastguard Worker   WaitableEvent end_event;
118*6777b538SAndroid Build Coastguard Worker   CounterType counter;
119*6777b538SAndroid Build Coastguard Worker   RepeatingClosure done_closure = BarrierClosure(
120*6777b538SAndroid Build Coastguard Worker       num_threads, BindOnce(&WaitableEvent::Signal, Unretained(&end_event)));
121*6777b538SAndroid Build Coastguard Worker 
122*6777b538SAndroid Build Coastguard Worker   std::vector<std::unique_ptr<IncrementThread<CounterType>>> threads;
123*6777b538SAndroid Build Coastguard Worker   for (int i = 0; i < num_threads; ++i) {
124*6777b538SAndroid Build Coastguard Worker     threads.push_back(std::make_unique<IncrementThread<CounterType>>(
125*6777b538SAndroid Build Coastguard Worker         &start_event, &counter, done_closure));
126*6777b538SAndroid Build Coastguard Worker     threads.back()->Start();
127*6777b538SAndroid Build Coastguard Worker   }
128*6777b538SAndroid Build Coastguard Worker 
129*6777b538SAndroid Build Coastguard Worker   TimeTicks start_time = TimeTicks::Now();
130*6777b538SAndroid Build Coastguard Worker   start_event.Signal();
131*6777b538SAndroid Build Coastguard Worker   end_event.Wait();
132*6777b538SAndroid Build Coastguard Worker   TimeTicks end_time = TimeTicks::Now();
133*6777b538SAndroid Build Coastguard Worker 
134*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ(num_threads * kNumIterations, counter.value());
135*6777b538SAndroid Build Coastguard Worker 
136*6777b538SAndroid Build Coastguard Worker   auto reporter = SetUpReporter(story_name);
137*6777b538SAndroid Build Coastguard Worker   reporter.AddResult(
138*6777b538SAndroid Build Coastguard Worker       kMetricOperationThroughput,
139*6777b538SAndroid Build Coastguard Worker       kNumIterations / (end_time - start_time).InMillisecondsF());
140*6777b538SAndroid Build Coastguard Worker 
141*6777b538SAndroid Build Coastguard Worker   for (auto& thread : threads)
142*6777b538SAndroid Build Coastguard Worker     thread->Join();
143*6777b538SAndroid Build Coastguard Worker }
144*6777b538SAndroid Build Coastguard Worker 
145*6777b538SAndroid Build Coastguard Worker }  // namespace
146*6777b538SAndroid Build Coastguard Worker 
TEST(CounterPerfTest,Uint64_NoLock_1Thread)147*6777b538SAndroid Build Coastguard Worker TEST(CounterPerfTest, Uint64_NoLock_1Thread) {
148*6777b538SAndroid Build Coastguard Worker   RunIncrementPerfTest<Uint64_NoLock>("Uint64_NoLock_1Thread", 1);
149*6777b538SAndroid Build Coastguard Worker }
150*6777b538SAndroid Build Coastguard Worker 
151*6777b538SAndroid Build Coastguard Worker // No Uint64_NoLock_4Threads test because it would cause data races.
152*6777b538SAndroid Build Coastguard Worker 
TEST(CounterPerfTest,Uint64_Lock_1Thread)153*6777b538SAndroid Build Coastguard Worker TEST(CounterPerfTest, Uint64_Lock_1Thread) {
154*6777b538SAndroid Build Coastguard Worker   RunIncrementPerfTest<Uint64_Lock>("Uint64_Lock_1Thread", 1);
155*6777b538SAndroid Build Coastguard Worker }
156*6777b538SAndroid Build Coastguard Worker 
TEST(CounterPerfTest,Uint64_Lock_4Threads)157*6777b538SAndroid Build Coastguard Worker TEST(CounterPerfTest, Uint64_Lock_4Threads) {
158*6777b538SAndroid Build Coastguard Worker   RunIncrementPerfTest<Uint64_Lock>("Uint64_Lock_4Threads", 4);
159*6777b538SAndroid Build Coastguard Worker }
160*6777b538SAndroid Build Coastguard Worker 
TEST(CounterPerfTest,AtomicUint64_NoBarrier_1Thread)161*6777b538SAndroid Build Coastguard Worker TEST(CounterPerfTest, AtomicUint64_NoBarrier_1Thread) {
162*6777b538SAndroid Build Coastguard Worker   RunIncrementPerfTest<AtomicUint64_NoBarrier>("AtomicUint64_NoBarrier_1Thread",
163*6777b538SAndroid Build Coastguard Worker                                                1);
164*6777b538SAndroid Build Coastguard Worker }
165*6777b538SAndroid Build Coastguard Worker 
TEST(CounterPerfTest,AtomicUint64_NoBarrier_4Threads)166*6777b538SAndroid Build Coastguard Worker TEST(CounterPerfTest, AtomicUint64_NoBarrier_4Threads) {
167*6777b538SAndroid Build Coastguard Worker   RunIncrementPerfTest<AtomicUint64_NoBarrier>(
168*6777b538SAndroid Build Coastguard Worker       "AtomicUint64_NoBarrier_4Threads", 4);
169*6777b538SAndroid Build Coastguard Worker }
170*6777b538SAndroid Build Coastguard Worker 
TEST(CounterPerfTest,AtomicUint64_Barrier_1Thread)171*6777b538SAndroid Build Coastguard Worker TEST(CounterPerfTest, AtomicUint64_Barrier_1Thread) {
172*6777b538SAndroid Build Coastguard Worker   RunIncrementPerfTest<AtomicUint64_Barrier>("AtomicUint64_Barrier_1Thread", 1);
173*6777b538SAndroid Build Coastguard Worker }
174*6777b538SAndroid Build Coastguard Worker 
TEST(CounterPerfTest,AtomicUint64_Barrier_4Threads)175*6777b538SAndroid Build Coastguard Worker TEST(CounterPerfTest, AtomicUint64_Barrier_4Threads) {
176*6777b538SAndroid Build Coastguard Worker   RunIncrementPerfTest<AtomicUint64_Barrier>("AtomicUint64_Barrier_4Threads",
177*6777b538SAndroid Build Coastguard Worker                                              4);
178*6777b538SAndroid Build Coastguard Worker }
179*6777b538SAndroid Build Coastguard Worker 
180*6777b538SAndroid Build Coastguard Worker }  // namespace base
181