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