xref: /aosp_15_r20/external/cronet/base/synchronization/lock_perftest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2020 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/compiler_specific.h"
6 #include "base/memory/raw_ptr.h"
7 #include "base/synchronization/lock.h"
8 #include "base/threading/platform_thread.h"
9 #include "base/time/time.h"
10 #include "base/timer/lap_timer.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "testing/perf/perf_result_reporter.h"
13 
14 namespace base {
15 namespace {
16 
17 constexpr int kWarmupRuns = 1;
18 constexpr TimeDelta kTimeLimit = Seconds(1);
19 constexpr int kTimeCheckInterval = 100000;
20 
21 constexpr char kMetricPrefixLock[] = "Lock.";
22 constexpr char kMetricLockUnlockThroughput[] = "lock_unlock_throughput";
23 constexpr char kStoryBaseline[] = "baseline_story";
24 constexpr char kStoryWithCompetingThread[] = "with_competing_thread";
25 
SetUpReporter(const std::string & story_name)26 perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
27   perf_test::PerfResultReporter reporter(kMetricPrefixLock, story_name);
28   reporter.RegisterImportantMetric(kMetricLockUnlockThroughput, "runs/s");
29   return reporter;
30 }
31 
32 class Spin : public PlatformThread::Delegate {
33  public:
Spin(Lock * lock,uint32_t * data)34   Spin(Lock* lock, uint32_t* data)
35       : lock_(lock), data_(data), should_stop_(false) {}
36   ~Spin() override = default;
37 
ThreadMain()38   void ThreadMain() override {
39     // Local variable to avoid "cache line ping-pong" from influencing the
40     // results.
41     uint32_t count = 0;
42     while (!should_stop_.load(std::memory_order_relaxed)) {
43       lock_->Acquire();
44       count++;
45       lock_->Release();
46     }
47 
48     lock_->Acquire();
49     (*data_) += count;
50     lock_->Release();
51   }
52 
53   // Called from another thread to stop the loop.
Stop()54   void Stop() { should_stop_ = true; }
55 
56  private:
57   raw_ptr<Lock> lock_;
58   raw_ptr<uint32_t> data_ GUARDED_BY(lock_);
59   std::atomic<bool> should_stop_;
60 };
61 
62 }  // namespace
63 
TEST(LockPerfTest,Simple)64 TEST(LockPerfTest, Simple) {
65   LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
66   [[maybe_unused]] uint32_t data = 0;
67 
68   Lock lock;
69 
70   do {
71     lock.Acquire();
72     data += 1;
73     lock.Release();
74     timer.NextLap();
75   } while (!timer.HasTimeLimitExpired());
76 
77   auto reporter = SetUpReporter(kStoryBaseline);
78   reporter.AddResult(kMetricLockUnlockThroughput, timer.LapsPerSecond());
79 }
80 
TEST(LockPerfTest,WithCompetingThread)81 TEST(LockPerfTest, WithCompetingThread) {
82   LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
83   uint32_t data = 0;
84 
85   Lock lock;
86 
87   // Starts a competing thread executing the same loop as this thread.
88   Spin thread_main(&lock, &data);
89   PlatformThreadHandle thread_handle;
90   ASSERT_TRUE(PlatformThread::Create(0, &thread_main, &thread_handle));
91 
92   do {
93     lock.Acquire();
94     data += 1;
95     lock.Release();
96     timer.NextLap();
97   } while (!timer.HasTimeLimitExpired());
98 
99   thread_main.Stop();
100   PlatformThread::Join(thread_handle);
101 
102   auto reporter = SetUpReporter(kStoryWithCompetingThread);
103   reporter.AddResult(kMetricLockUnlockThroughput, timer.LapsPerSecond());
104 }
105 }  // namespace base
106