Merge pull request #60823 from azat/thread-fuzzer

Faster (almost 2x) mutexes (was slower due to ThreadFuzzer)
This commit is contained in:
Alexey Milovidov 2024-03-06 00:07:50 +03:00 committed by GitHub
commit 456157939c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 79 additions and 11 deletions

View File

@ -174,6 +174,8 @@ endif ()
add_library(clickhouse_common_io ${clickhouse_common_io_headers} ${clickhouse_common_io_sources})
set_source_files_properties(Common/ThreadFuzzer.cpp PROPERTIES COMPILE_FLAGS "-fomit-frame-pointer -momit-leaf-frame-pointer")
add_library (clickhouse_malloc OBJECT Common/malloc.cpp)
set_source_files_properties(Common/malloc.cpp PROPERTIES COMPILE_FLAGS "-fno-builtin")

View File

@ -51,7 +51,11 @@ ThreadFuzzer::ThreadFuzzer()
{
initConfiguration();
if (!isEffective())
{
/// It has no effect - disable it
stop();
return;
}
setup();
}
@ -172,6 +176,8 @@ void ThreadFuzzer::stop()
void ThreadFuzzer::start()
{
if (!instance().isEffective())
return;
started.store(true, std::memory_order_relaxed);
}
@ -180,11 +186,11 @@ bool ThreadFuzzer::isStarted()
return started.load(std::memory_order_relaxed);
}
static void injection(
static void injectionImpl(
double yield_probability,
double migrate_probability,
double sleep_probability,
double sleep_time_us [[maybe_unused]])
double sleep_time_us)
{
DENY_ALLOCATIONS_IN_SCOPE;
if (!ThreadFuzzer::isStarted())
@ -222,6 +228,19 @@ static void injection(
}
}
static ALWAYS_INLINE void injection(
double yield_probability,
double migrate_probability,
double sleep_probability,
double sleep_time_us)
{
DENY_ALLOCATIONS_IN_SCOPE;
if (!ThreadFuzzer::isStarted())
return;
injectionImpl(yield_probability, migrate_probability, sleep_probability, sleep_time_us);
}
void ThreadFuzzer::maybeInjectSleep()
{
auto & fuzzer = ThreadFuzzer::instance();
@ -286,13 +305,13 @@ void ThreadFuzzer::setup() const
#if THREAD_FUZZER_WRAP_PTHREAD
#define INJECTION_BEFORE(NAME) \
injection( \
injectionImpl( \
NAME##_before_yield_probability.load(std::memory_order_relaxed), \
NAME##_before_migrate_probability.load(std::memory_order_relaxed), \
NAME##_before_sleep_probability.load(std::memory_order_relaxed), \
NAME##_before_sleep_time_us.load(std::memory_order_relaxed));
#define INJECTION_AFTER(NAME) \
injection( \
injectionImpl( \
NAME##_after_yield_probability.load(std::memory_order_relaxed), \
NAME##_after_migrate_probability.load(std::memory_order_relaxed), \
NAME##_after_sleep_probability.load(std::memory_order_relaxed), \
@ -383,13 +402,16 @@ static void * getFunctionAddress(const char * name)
static constinit RET(*real_##NAME)(__VA_ARGS__) = nullptr; \
extern "C" RET NAME(__VA_ARGS__) \
{ \
INJECTION_BEFORE(NAME); \
bool thread_fuzzer_enabled = ThreadFuzzer::isStarted(); \
if (thread_fuzzer_enabled) \
INJECTION_BEFORE(NAME); \
if (unlikely(!real_##NAME)) { \
real_##NAME = \
reinterpret_cast<RET(*)(__VA_ARGS__)>(getFunctionAddress(#NAME)); \
} \
auto && ret{real_##NAME(arg)}; \
INJECTION_AFTER(NAME); \
if (thread_fuzzer_enabled) \
INJECTION_AFTER(NAME); \
return ret; \
}
FOR_EACH_WRAPPED_FUNCTION(MAKE_WRAPPER_USING_DLSYM)
@ -399,10 +421,17 @@ FOR_EACH_WRAPPED_FUNCTION(MAKE_WRAPPER_USING_DLSYM)
extern "C" RET __##NAME(__VA_ARGS__); \
extern "C" RET NAME(__VA_ARGS__) \
{ \
INJECTION_BEFORE(NAME); \
auto && ret{__##NAME(arg)}; \
INJECTION_AFTER(NAME); \
return ret; \
if (!ThreadFuzzer::isStarted()) \
{ \
return __##NAME(arg); \
} \
else \
{ \
INJECTION_BEFORE(NAME); \
auto && ret{__##NAME(arg)}; \
INJECTION_AFTER(NAME); \
return ret; \
} \
}
FOR_EACH_WRAPPED_FUNCTION(MAKE_WRAPPER_USING_INTERNAL_SYMBOLS)
#undef MAKE_WRAPPER_USING_INTERNAL_SYMBOLS

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <atomic>
#include <base/defines.h>
namespace DB
{
@ -56,7 +57,7 @@ public:
static void stop();
static void start();
static bool isStarted();
static bool ALWAYS_INLINE isStarted();
static void maybeInjectSleep();
static void maybeInjectMemoryLimitException();

View File

@ -0,0 +1,36 @@
#include <optional>
#include <thread>
#include <vector>
#include <gtest/gtest.h>
#include <Common/ThreadFuzzer.h>
#include <Common/Stopwatch.h>
TEST(ThreadFuzzer, mutex)
{
/// Initialize ThreadFuzzer::started
DB::ThreadFuzzer::instance().isEffective();
std::mutex mutex;
std::atomic<size_t> elapsed_ns = 0;
auto func = [&]()
{
Stopwatch watch;
for (size_t i = 0; i < 1e6; ++i)
{
mutex.lock();
mutex.unlock();
}
elapsed_ns += watch.elapsedNanoseconds();
};
std::vector<std::optional<std::thread>> threads(10);
for (auto & thread : threads)
thread.emplace(func);
for (auto & thread : threads)
thread->join();
std::cout << "elapsed: " << elapsed_ns/1e9 << "\n";
}