blob: 198775c9fa282d1380357b20b18f57f1efa01100 [file] [log] [blame]
// 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/bind_helpers.h"
#include "base/callback.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/bind_test_util.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_test.h"
#if defined(OS_WIN)
#include <windows.h>
#include "base/win/windows_types.h"
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
#include <pthread.h>
#endif
namespace base {
namespace internal {
namespace {
// 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();
}
void Run() override {
start_event_->Wait();
std::move(action_).Run();
std::move(completion_).Run();
}
private:
WaitableEvent* const start_event_;
base::OnceClosure action_;
base::OnceClosure completion_;
DISALLOW_COPY_AND_ASSIGN(TLSThread);
};
class ThreadLocalStoragePerfTest : public testing::Test {
public:
protected:
ThreadLocalStoragePerfTest() = default;
~ThreadLocalStoragePerfTest() override = default;
template <class Read, class Write>
void Benchmark(const std::string& trace,
Read read,
Write write,
size_t num_operation,
size_t num_threads) {
write(2);
BenchmarkImpl("TLS read throughput", trace,
base::BindLambdaForTesting([&]() {
volatile intptr_t total = 0;
for (size_t i = 0; i < num_operation; ++i)
total += read();
}),
num_operation, num_threads);
BenchmarkImpl("TLS write throughput", trace,
base::BindLambdaForTesting([&]() {
for (size_t i = 0; i < num_operation; ++i)
write(i);
}),
num_operation, num_threads);
BenchmarkImpl("TLS read-write throughput", trace,
base::BindLambdaForTesting([&]() {
for (size_t i = 0; i < num_operation; ++i)
write(read() + 1);
}),
num_operation, num_threads);
}
void BenchmarkImpl(const std::string& measurment,
const std::string& trace,
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();
perf_test::PrintResult(
measurment, "", trace,
num_operation /
static_cast<double>(operation_duration.InMilliseconds()),
"operations/ms", true);
}
private:
DISALLOW_COPY_AND_ASSIGN(ThreadLocalStoragePerfTest);
};
} // 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("ThreadLocalStorage", read, write, 10000000, 1);
Benchmark("ThreadLocalStorage 4 threads", read, write, 10000000, 4);
}
#if defined(OS_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("PlatformFls", read, write, 10000000, 1);
Benchmark("PlatformFls 4 threads", read, write, 10000000, 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("PlatformTls", read, write, 10000000, 1);
Benchmark("PlatformTls 4 threads", read, write, 10000000, 4);
}
#elif defined(OS_POSIX) || defined(OS_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("PlatformTls", read, write, 10000000, 1);
Benchmark("PlatformTls 4 threads", read, write, 10000000, 4);
}
#endif
} // namespace internal
} // namespace base