123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include <stddef.h>
- #include <memory>
- #include <vector>
- #include "base/barrier_closure.h"
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/callback_helpers.h"
- #include "base/memory/raw_ptr.h"
- #include "base/synchronization/waitable_event.h"
- #include "base/test/bind.h"
- #include "base/threading/simple_thread.h"
- #include "base/threading/thread_local_storage.h"
- #include "base/time/time.h"
- #include "build/build_config.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "testing/perf/perf_result_reporter.h"
- #if BUILDFLAG(IS_WIN)
- #include <windows.h>
- #include "base/win/windows_types.h"
- #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
- #include <pthread.h>
- #endif
- namespace base {
- namespace internal {
- namespace {
- constexpr size_t kCount = 5000000;
- constexpr char kMetricPrefixThreadLocalStorage[] = "ThreadLocalStorage.";
- constexpr char kMetricBaseRead[] = "read";
- constexpr char kMetricBaseWrite[] = "write";
- constexpr char kMetricBaseReadWrite[] = "read_write";
- constexpr char kMetricSuffixThroughput[] = "_throughput";
- constexpr char kMetricSuffixOperationTime[] = "_operation_time";
- constexpr char kStoryBaseTLS[] = "thread_local_storage";
- #if BUILDFLAG(IS_WIN)
- constexpr char kStoryBasePlatformFLS[] = "platform_fiber_local_storage";
- #endif // BUILDFLAG(IS_WIN)
- constexpr char kStoryBasePlatformTLS[] = "platform_thread_local_storage";
- constexpr char kStoryBaseCPPTLS[] = "c++_platform_thread_local_storage";
- constexpr char kStorySuffixFourThreads[] = "_4_threads";
- perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
- perf_test::PerfResultReporter reporter(kMetricPrefixThreadLocalStorage,
- story_name);
- reporter.RegisterImportantMetric(
- std::string(kMetricBaseRead) + kMetricSuffixThroughput, "runs/s");
- reporter.RegisterImportantMetric(
- std::string(kMetricBaseRead) + kMetricSuffixOperationTime, "ns");
- reporter.RegisterImportantMetric(
- std::string(kMetricBaseWrite) + kMetricSuffixThroughput, "runs/s");
- reporter.RegisterImportantMetric(
- std::string(kMetricBaseWrite) + kMetricSuffixOperationTime, "ns");
- reporter.RegisterImportantMetric(
- std::string(kMetricBaseReadWrite) + kMetricSuffixThroughput, "runs/s");
- reporter.RegisterImportantMetric(
- std::string(kMetricBaseReadWrite) + kMetricSuffixOperationTime, "ns");
- return reporter;
- }
- // A thread that waits for the caller to signal an event before proceeding to
- // call action.Run().
- class TLSThread : public SimpleThread {
- public:
- // Creates a PostingThread that waits on |start_event| before calling
- // action.Run().
- TLSThread(WaitableEvent* start_event,
- base::OnceClosure action,
- base::OnceClosure completion)
- : SimpleThread("TLSThread"),
- start_event_(start_event),
- action_(std::move(action)),
- completion_(std::move(completion)) {
- Start();
- }
- TLSThread(const TLSThread&) = delete;
- TLSThread& operator=(const TLSThread&) = delete;
- void Run() override {
- start_event_->Wait();
- std::move(action_).Run();
- std::move(completion_).Run();
- }
- private:
- const raw_ptr<WaitableEvent> start_event_;
- base::OnceClosure action_;
- base::OnceClosure completion_;
- };
- class ThreadLocalStoragePerfTest : public testing::Test {
- public:
- ThreadLocalStoragePerfTest(const ThreadLocalStoragePerfTest&) = delete;
- ThreadLocalStoragePerfTest& operator=(const ThreadLocalStoragePerfTest&) =
- delete;
- protected:
- ThreadLocalStoragePerfTest() = default;
- ~ThreadLocalStoragePerfTest() override = default;
- template <class Read, class Write>
- void Benchmark(const std::string& story_name,
- Read read,
- Write write,
- size_t num_operation,
- size_t num_threads) {
- write(2);
- BenchmarkImpl(kMetricBaseRead, story_name,
- base::BindLambdaForTesting([&]() {
- volatile intptr_t total = 0;
- for (size_t i = 0; i < num_operation; ++i)
- total = total + read();
- }),
- num_operation, num_threads);
- BenchmarkImpl(kMetricBaseWrite, story_name,
- base::BindLambdaForTesting([&]() {
- for (size_t i = 0; i < num_operation; ++i)
- write(i);
- }),
- num_operation, num_threads);
- BenchmarkImpl(kMetricBaseReadWrite, story_name,
- base::BindLambdaForTesting([&]() {
- for (size_t i = 0; i < num_operation; ++i)
- write(read() + 1);
- }),
- num_operation, num_threads);
- }
- void BenchmarkImpl(const std::string& metric_base,
- const std::string& story_name,
- base::RepeatingClosure action,
- size_t num_operation,
- size_t num_threads) {
- WaitableEvent start_thread;
- WaitableEvent complete_thread;
- base::RepeatingClosure done = BarrierClosure(
- num_threads,
- base::BindLambdaForTesting([&]() { complete_thread.Signal(); }));
- std::vector<std::unique_ptr<TLSThread>> threads;
- for (size_t i = 0; i < num_threads; ++i) {
- threads.emplace_back(
- std::make_unique<TLSThread>(&start_thread, action, done));
- }
- TimeTicks operation_start = TimeTicks::Now();
- start_thread.Signal();
- complete_thread.Wait();
- TimeDelta operation_duration = TimeTicks::Now() - operation_start;
- for (auto& thread : threads)
- thread->Join();
- auto reporter = SetUpReporter(story_name);
- reporter.AddResult(metric_base + kMetricSuffixThroughput,
- num_operation / operation_duration.InSecondsF());
- size_t nanos_per_operation =
- operation_duration.InNanoseconds() / num_operation;
- reporter.AddResult(metric_base + kMetricSuffixOperationTime,
- nanos_per_operation);
- }
- };
- } // namespace
- TEST_F(ThreadLocalStoragePerfTest, ThreadLocalStorage) {
- ThreadLocalStorage::Slot tls;
- auto read = [&]() { return reinterpret_cast<intptr_t>(tls.Get()); };
- auto write = [&](intptr_t value) { tls.Set(reinterpret_cast<void*>(value)); };
- Benchmark(kStoryBaseTLS, read, write, 10000000, 1);
- Benchmark(std::string(kStoryBaseTLS) + kStorySuffixFourThreads, read, write,
- kCount, 4);
- }
- #if BUILDFLAG(IS_WIN)
- void WINAPI destroy(void*) {}
- TEST_F(ThreadLocalStoragePerfTest, PlatformFls) {
- DWORD key = FlsAlloc(destroy);
- ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
- auto read = [&]() { return reinterpret_cast<intptr_t>(FlsGetValue(key)); };
- auto write = [&](intptr_t value) {
- FlsSetValue(key, reinterpret_cast<void*>(value));
- };
- Benchmark(kStoryBasePlatformFLS, read, write, 10000000, 1);
- Benchmark(std::string(kStoryBasePlatformFLS) + kStorySuffixFourThreads, read,
- write, kCount, 4);
- }
- TEST_F(ThreadLocalStoragePerfTest, PlatformTls) {
- DWORD key = TlsAlloc();
- ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
- auto read = [&]() { return reinterpret_cast<intptr_t>(TlsGetValue(key)); };
- auto write = [&](intptr_t value) {
- TlsSetValue(key, reinterpret_cast<void*>(value));
- };
- Benchmark(kStoryBasePlatformTLS, read, write, 10000000, 1);
- Benchmark(std::string(kStoryBasePlatformTLS) + kStorySuffixFourThreads, read,
- write, kCount, 4);
- }
- #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
- TEST_F(ThreadLocalStoragePerfTest, PlatformTls) {
- pthread_key_t key;
- ASSERT_FALSE(pthread_key_create(&key, [](void*) {}));
- ASSERT_NE(PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key);
- auto read = [&]() {
- return reinterpret_cast<intptr_t>(pthread_getspecific(key));
- };
- auto write = [&](intptr_t value) {
- pthread_setspecific(key, reinterpret_cast<void*>(value));
- };
- Benchmark(kStoryBasePlatformTLS, read, write, 10000000, 1);
- Benchmark(std::string(kStoryBasePlatformTLS) + kStorySuffixFourThreads, read,
- write, kCount, 4);
- }
- #endif
- TEST_F(ThreadLocalStoragePerfTest, Cpp11Tls) {
- thread_local intptr_t thread_local_variable;
- auto read = [&]() { return thread_local_variable; };
- auto write = [&](intptr_t value) {
- reinterpret_cast<volatile intptr_t*>(&thread_local_variable)[0] = value;
- };
- Benchmark(kStoryBaseCPPTLS, read, write, 10000000, 1);
- Benchmark(std::string(kStoryBaseCPPTLS) + kStorySuffixFourThreads, read,
- write, kCount, 4);
- }
- } // namespace internal
- } // namespace base
|