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