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