• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, base::BindLambdaForTesting([&] {
120                     volatile intptr_t total = 0;
121                     for (size_t i = 0; i < num_operation; ++i)
122                       total = total + read();
123                   }),
124                   num_operation, num_threads);
125 
126     BenchmarkImpl(kMetricBaseWrite, story_name, base::BindLambdaForTesting([&] {
127                     for (size_t i = 0; i < num_operation; ++i)
128                       write(i);
129                   }),
130                   num_operation, num_threads);
131 
132     BenchmarkImpl(kMetricBaseReadWrite, story_name,
133                   base::BindLambdaForTesting([&] {
134                     for (size_t i = 0; i < num_operation; ++i)
135                       write(read() + 1);
136                   }),
137                   num_operation, num_threads);
138   }
139 
BenchmarkImpl(const std::string & metric_base,const std::string & story_name,base::RepeatingClosure action,size_t num_operation,size_t num_threads)140   void BenchmarkImpl(const std::string& metric_base,
141                      const std::string& story_name,
142                      base::RepeatingClosure action,
143                      size_t num_operation,
144                      size_t num_threads) {
145     WaitableEvent start_thread;
146     WaitableEvent complete_thread;
147 
148     base::RepeatingClosure done = BarrierClosure(
149         num_threads,
150         base::BindLambdaForTesting([&] { complete_thread.Signal(); }));
151 
152     std::vector<std::unique_ptr<TLSThread>> threads;
153     for (size_t i = 0; i < num_threads; ++i) {
154       threads.emplace_back(
155           std::make_unique<TLSThread>(&start_thread, action, done));
156     }
157 
158     TimeTicks operation_start = TimeTicks::Now();
159     start_thread.Signal();
160     complete_thread.Wait();
161     TimeDelta operation_duration = TimeTicks::Now() - operation_start;
162 
163     for (auto& thread : threads)
164       thread->Join();
165 
166     auto reporter = SetUpReporter(story_name);
167     reporter.AddResult(metric_base + kMetricSuffixThroughput,
168                        num_operation / operation_duration.InSecondsF());
169     size_t nanos_per_operation =
170         operation_duration.InNanoseconds() / num_operation;
171     reporter.AddResult(metric_base + kMetricSuffixOperationTime,
172                        nanos_per_operation);
173   }
174 };
175 
176 }  // namespace
177 
TEST_F(ThreadLocalStoragePerfTest,ThreadLocalStorage)178 TEST_F(ThreadLocalStoragePerfTest, ThreadLocalStorage) {
179   ThreadLocalStorage::Slot tls;
180   auto read = [&] { return reinterpret_cast<intptr_t>(tls.Get()); };
181   auto write = [&](intptr_t value) { tls.Set(reinterpret_cast<void*>(value)); };
182 
183   Benchmark(kStoryBaseTLS, read, write, 10000000, 1);
184   Benchmark(std::string(kStoryBaseTLS) + kStorySuffixFourThreads, read, write,
185             kCount, 4);
186 }
187 
188 #if BUILDFLAG(IS_WIN)
189 
destroy(void *)190 void WINAPI destroy(void*) {}
191 
TEST_F(ThreadLocalStoragePerfTest,PlatformFls)192 TEST_F(ThreadLocalStoragePerfTest, PlatformFls) {
193   DWORD key = FlsAlloc(destroy);
194   ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
195 
196   auto read = [&] { return reinterpret_cast<intptr_t>(FlsGetValue(key)); };
197   auto write = [&](intptr_t value) {
198     FlsSetValue(key, reinterpret_cast<void*>(value));
199   };
200 
201   Benchmark(kStoryBasePlatformFLS, read, write, 10000000, 1);
202   Benchmark(std::string(kStoryBasePlatformFLS) + kStorySuffixFourThreads, read,
203             write, kCount, 4);
204 }
205 
TEST_F(ThreadLocalStoragePerfTest,PlatformTls)206 TEST_F(ThreadLocalStoragePerfTest, PlatformTls) {
207   DWORD key = TlsAlloc();
208   ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
209 
210   auto read = [&] { return reinterpret_cast<intptr_t>(TlsGetValue(key)); };
211   auto write = [&](intptr_t value) {
212     TlsSetValue(key, reinterpret_cast<void*>(value));
213   };
214 
215   Benchmark(kStoryBasePlatformTLS, read, write, 10000000, 1);
216   Benchmark(std::string(kStoryBasePlatformTLS) + kStorySuffixFourThreads, read,
217             write, kCount, 4);
218 }
219 
220 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
221 
TEST_F(ThreadLocalStoragePerfTest,PlatformTls)222 TEST_F(ThreadLocalStoragePerfTest, PlatformTls) {
223   pthread_key_t key;
224   ASSERT_FALSE(pthread_key_create(&key, [](void*) {}));
225   ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
226 
227   auto read = [&] {
228     return reinterpret_cast<intptr_t>(pthread_getspecific(key));
229   };
230   auto write = [&](intptr_t value) {
231     pthread_setspecific(key, reinterpret_cast<void*>(value));
232   };
233 
234   Benchmark(kStoryBasePlatformTLS, read, write, 10000000, 1);
235   Benchmark(std::string(kStoryBasePlatformTLS) + kStorySuffixFourThreads, read,
236             write, kCount, 4);
237 }
238 
239 #endif
240 
TEST_F(ThreadLocalStoragePerfTest,Cpp11Tls)241 TEST_F(ThreadLocalStoragePerfTest, Cpp11Tls) {
242   thread_local intptr_t thread_local_variable;
243 
244   auto read = [&] { return thread_local_variable; };
245   auto write = [&](intptr_t value) {
246     reinterpret_cast<volatile intptr_t*>(&thread_local_variable)[0] = value;
247   };
248 
249   Benchmark(kStoryBaseCPPTLS, read, write, 10000000, 1);
250   Benchmark(std::string(kStoryBaseCPPTLS) + kStorySuffixFourThreads, read,
251             write, kCount, 4);
252 }
253 
254 }  // namespace internal
255 }  // namespace base
256