mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 00:22:29 +00:00
Merge pull request #60823 from azat/thread-fuzzer
Faster (almost 2x) mutexes (was slower due to ThreadFuzzer)
This commit is contained in:
commit
456157939c
@ -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")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
36
src/Common/tests/gtest_thread_fuzzer.cpp
Normal file
36
src/Common/tests/gtest_thread_fuzzer.cpp
Normal 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";
|
||||
}
|
Loading…
Reference in New Issue
Block a user