thread_local_storage_perftest.cc 8.5 KB

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