xref: /aosp_15_r20/external/cronet/base/threading/thread_local_storage_perftest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 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 <stddef.h>
6 #include <memory>
7 #include <vector>
8 
9 #include "base/barrier_closure.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/test/bind.h"
16 #include "base/threading/simple_thread.h"
17 #include "base/threading/thread_local_storage.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "testing/perf/perf_result_reporter.h"
22 
23 #if BUILDFLAG(IS_WIN)
24 #include <windows.h>
25 
26 #include "base/win/windows_types.h"
27 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
28 #include <pthread.h>
29 #endif
30 
31 namespace base {
32 namespace internal {
33 
34 namespace {
35 
36 constexpr size_t kCount = 5000000;
37 
38 constexpr char kMetricPrefixThreadLocalStorage[] = "ThreadLocalStorage.";
39 constexpr char kMetricBaseRead[] = "read";
40 constexpr char kMetricBaseWrite[] = "write";
41 constexpr char kMetricBaseReadWrite[] = "read_write";
42 constexpr char kMetricSuffixThroughput[] = "_throughput";
43 constexpr char kMetricSuffixOperationTime[] = "_operation_time";
44 constexpr char kStoryBaseTLS[] = "thread_local_storage";
45 #if BUILDFLAG(IS_WIN)
46 constexpr char kStoryBasePlatformFLS[] = "platform_fiber_local_storage";
47 #endif  // BUILDFLAG(IS_WIN)
48 constexpr char kStoryBasePlatformTLS[] = "platform_thread_local_storage";
49 constexpr char kStoryBaseCPPTLS[] = "c++_platform_thread_local_storage";
50 constexpr char kStorySuffixFourThreads[] = "_4_threads";
51 
SetUpReporter(const std::string & story_name)52 perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
53   perf_test::PerfResultReporter reporter(kMetricPrefixThreadLocalStorage,
54                                          story_name);
55   reporter.RegisterImportantMetric(
56       std::string(kMetricBaseRead) + kMetricSuffixThroughput, "runs/s");
57   reporter.RegisterImportantMetric(
58       std::string(kMetricBaseRead) + kMetricSuffixOperationTime, "ns");
59   reporter.RegisterImportantMetric(
60       std::string(kMetricBaseWrite) + kMetricSuffixThroughput, "runs/s");
61   reporter.RegisterImportantMetric(
62       std::string(kMetricBaseWrite) + kMetricSuffixOperationTime, "ns");
63   reporter.RegisterImportantMetric(
64       std::string(kMetricBaseReadWrite) + kMetricSuffixThroughput, "runs/s");
65   reporter.RegisterImportantMetric(
66       std::string(kMetricBaseReadWrite) + kMetricSuffixOperationTime, "ns");
67   return reporter;
68 }
69 
70 // A thread that waits for the caller to signal an event before proceeding to
71 // call action.Run().
72 class TLSThread : public SimpleThread {
73  public:
74   // Creates a PostingThread that waits on |start_event| before calling
75   // action.Run().
TLSThread(WaitableEvent * start_event,base::OnceClosure action,base::OnceClosure completion)76   TLSThread(WaitableEvent* start_event,
77             base::OnceClosure action,
78             base::OnceClosure completion)
79       : SimpleThread("TLSThread"),
80         start_event_(start_event),
81         action_(std::move(action)),
82         completion_(std::move(completion)) {
83     Start();
84   }
85 
86   TLSThread(const TLSThread&) = delete;
87   TLSThread& operator=(const TLSThread&) = delete;
88 
Run()89   void Run() override {
90     start_event_->Wait();
91     std::move(action_).Run();
92     std::move(completion_).Run();
93   }
94 
95  private:
96   const raw_ptr<WaitableEvent> start_event_;
97   base::OnceClosure action_;
98   base::OnceClosure completion_;
99 };
100 
101 class ThreadLocalStoragePerfTest : public testing::Test {
102  public:
103   ThreadLocalStoragePerfTest(const ThreadLocalStoragePerfTest&) = delete;
104   ThreadLocalStoragePerfTest& operator=(const ThreadLocalStoragePerfTest&) =
105       delete;
106 
107  protected:
108   ThreadLocalStoragePerfTest() = default;
109   ~ThreadLocalStoragePerfTest() override = default;
110 
111   template <class Read, class Write>
Benchmark(const std::string & story_name,Read read,Write write,size_t num_operation,size_t num_threads)112   void Benchmark(const std::string& story_name,
113                  Read read,
114                  Write write,
115                  size_t num_operation,
116                  size_t num_threads) {
117     write(2);
118 
119     BenchmarkImpl(kMetricBaseRead, story_name,
120                   base::BindLambdaForTesting([&]() {
121                     volatile intptr_t total = 0;
122                     for (size_t i = 0; i < num_operation; ++i)
123                       total = total + read();
124                   }),
125                   num_operation, num_threads);
126 
127     BenchmarkImpl(kMetricBaseWrite, story_name,
128                   base::BindLambdaForTesting([&]() {
129                     for (size_t i = 0; i < num_operation; ++i)
130                       write(i);
131                   }),
132                   num_operation, num_threads);
133 
134     BenchmarkImpl(kMetricBaseReadWrite, story_name,
135                   base::BindLambdaForTesting([&]() {
136                     for (size_t i = 0; i < num_operation; ++i)
137                       write(read() + 1);
138                   }),
139                   num_operation, num_threads);
140   }
141 
BenchmarkImpl(const std::string & metric_base,const std::string & story_name,base::RepeatingClosure action,size_t num_operation,size_t num_threads)142   void BenchmarkImpl(const std::string& metric_base,
143                      const std::string& story_name,
144                      base::RepeatingClosure action,
145                      size_t num_operation,
146                      size_t num_threads) {
147     WaitableEvent start_thread;
148     WaitableEvent complete_thread;
149 
150     base::RepeatingClosure done = BarrierClosure(
151         num_threads,
152         base::BindLambdaForTesting([&]() { complete_thread.Signal(); }));
153 
154     std::vector<std::unique_ptr<TLSThread>> threads;
155     for (size_t i = 0; i < num_threads; ++i) {
156       threads.emplace_back(
157           std::make_unique<TLSThread>(&start_thread, action, done));
158     }
159 
160     TimeTicks operation_start = TimeTicks::Now();
161     start_thread.Signal();
162     complete_thread.Wait();
163     TimeDelta operation_duration = TimeTicks::Now() - operation_start;
164 
165     for (auto& thread : threads)
166       thread->Join();
167 
168     auto reporter = SetUpReporter(story_name);
169     reporter.AddResult(metric_base + kMetricSuffixThroughput,
170                        num_operation / operation_duration.InSecondsF());
171     size_t nanos_per_operation =
172         operation_duration.InNanoseconds() / num_operation;
173     reporter.AddResult(metric_base + kMetricSuffixOperationTime,
174                        nanos_per_operation);
175   }
176 };
177 
178 }  // namespace
179 
TEST_F(ThreadLocalStoragePerfTest,ThreadLocalStorage)180 TEST_F(ThreadLocalStoragePerfTest, ThreadLocalStorage) {
181   ThreadLocalStorage::Slot tls;
182   auto read = [&]() { return reinterpret_cast<intptr_t>(tls.Get()); };
183   auto write = [&](intptr_t value) { tls.Set(reinterpret_cast<void*>(value)); };
184 
185   Benchmark(kStoryBaseTLS, read, write, 10000000, 1);
186   Benchmark(std::string(kStoryBaseTLS) + kStorySuffixFourThreads, read, write,
187             kCount, 4);
188 }
189 
190 #if BUILDFLAG(IS_WIN)
191 
destroy(void *)192 void WINAPI destroy(void*) {}
193 
TEST_F(ThreadLocalStoragePerfTest,PlatformFls)194 TEST_F(ThreadLocalStoragePerfTest, PlatformFls) {
195   DWORD key = FlsAlloc(destroy);
196   ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
197 
198   auto read = [&]() { return reinterpret_cast<intptr_t>(FlsGetValue(key)); };
199   auto write = [&](intptr_t value) {
200     FlsSetValue(key, reinterpret_cast<void*>(value));
201   };
202 
203   Benchmark(kStoryBasePlatformFLS, read, write, 10000000, 1);
204   Benchmark(std::string(kStoryBasePlatformFLS) + kStorySuffixFourThreads, read,
205             write, kCount, 4);
206 }
207 
TEST_F(ThreadLocalStoragePerfTest,PlatformTls)208 TEST_F(ThreadLocalStoragePerfTest, PlatformTls) {
209   DWORD key = TlsAlloc();
210   ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
211 
212   auto read = [&]() { return reinterpret_cast<intptr_t>(TlsGetValue(key)); };
213   auto write = [&](intptr_t value) {
214     TlsSetValue(key, reinterpret_cast<void*>(value));
215   };
216 
217   Benchmark(kStoryBasePlatformTLS, read, write, 10000000, 1);
218   Benchmark(std::string(kStoryBasePlatformTLS) + kStorySuffixFourThreads, read,
219             write, kCount, 4);
220 }
221 
222 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
223 
TEST_F(ThreadLocalStoragePerfTest,PlatformTls)224 TEST_F(ThreadLocalStoragePerfTest, PlatformTls) {
225   pthread_key_t key;
226   ASSERT_FALSE(pthread_key_create(&key, [](void*) {}));
227   ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
228 
229   auto read = [&]() {
230     return reinterpret_cast<intptr_t>(pthread_getspecific(key));
231   };
232   auto write = [&](intptr_t value) {
233     pthread_setspecific(key, reinterpret_cast<void*>(value));
234   };
235 
236   Benchmark(kStoryBasePlatformTLS, read, write, 10000000, 1);
237   Benchmark(std::string(kStoryBasePlatformTLS) + kStorySuffixFourThreads, read,
238             write, kCount, 4);
239 }
240 
241 #endif
242 
TEST_F(ThreadLocalStoragePerfTest,Cpp11Tls)243 TEST_F(ThreadLocalStoragePerfTest, Cpp11Tls) {
244   thread_local intptr_t thread_local_variable;
245 
246   auto read = [&]() { return thread_local_variable; };
247   auto write = [&](intptr_t value) {
248     reinterpret_cast<volatile intptr_t*>(&thread_local_variable)[0] = value;
249   };
250 
251   Benchmark(kStoryBaseCPPTLS, read, write, 10000000, 1);
252   Benchmark(std::string(kStoryBaseCPPTLS) + kStorySuffixFourThreads, read,
253             write, kCount, 4);
254 }
255 
256 }  // namespace internal
257 }  // namespace base
258