diff --git a/.clang-format b/.clang-format index dc69733..734a436 100644 --- a/.clang-format +++ b/.clang-format @@ -78,6 +78,6 @@ SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: c++11 +Standard: c++20 TabWidth: 4 -UseTab: Never \ No newline at end of file +UseTab: Never diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 735024e..9b6ae6e 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -45,3 +45,11 @@ jobs: cd builds/build_ubsan chmod +x tests/tests tests/tests + format: + runs-on: ubuntu-latest + steps: + - name: Install clang-format + run: sudo apt install -y clang-format + - uses: actions/checkout@v4 + - name: Check clang-format + run: find ./ -iname '*.h' -o -iname '*.cpp' | xargs clang-format -n --Werror diff --git a/.gitignore b/.gitignore index ee6d40f..7170c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,4 @@ *.out *.app -build/ \ No newline at end of file +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index e7ec613..7f322ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF) message("ASAN: " ${ENABLE_ASAN}) message("MSAN: " ${ENABLE_MSAN}) -message("TSAB: " ${ENABLE_TSAN}) +message("TSAN: " ${ENABLE_TSAN}) message("UBSAN: " ${ENABLE_UBSAN}) if(ENABLE_ASAN) message(STATUS "Enabling AddressSanitizer") @@ -51,4 +51,4 @@ FetchContent_Declare( FetchContent_MakeAvailable(googletest) target_include_directories(ciri PUBLIC src) -add_subdirectory(tests) \ No newline at end of file +add_subdirectory(tests) diff --git a/src/metrics/counter_basic.cpp b/src/metrics/counter_basic.cpp index 16715a4..0427076 100644 --- a/src/metrics/counter_basic.cpp +++ b/src/metrics/counter_basic.cpp @@ -1,8 +1,8 @@ #include "metrics/counter_basic.h" + #include #include namespace ciri { -namespace metrics { -} -} \ No newline at end of file +namespace metrics {} +} // namespace ciri diff --git a/src/metrics/counter_basic.h b/src/metrics/counter_basic.h index fda1fd1..8db679e 100644 --- a/src/metrics/counter_basic.h +++ b/src/metrics/counter_basic.h @@ -1,82 +1,84 @@ #pragma once +#include #include +#include #include -#include -#include #include -#include -#include #include +#include +#include namespace ciri { namespace metrics { -template +template class CounterBasic { -public: - static const size_t alignment = 64; - CounterBasic(size_t capacity) : - fast_module((capacity & (capacity - 1)) == 0) - , capacity_(capacity) { - assert(capacity_ > 0); - counters_ = static_cast*>(std::aligned_alloc(alignment, capacity * sizeof(decltype(counters_[0])))); - assert(counters_ != nullptr); - for (size_t i = 0; i < capacity_; ++i) { - new (counters_ + i) std::atomic(); - } - for (size_t i = 0; i < capacity_; ++i) { - counters_[i].store(0); - } + public: + static const size_t alignment = 64; + CounterBasic(size_t capacity) + : fast_module((capacity & (capacity - 1)) == 0), capacity_(capacity) { + assert(capacity_ > 0); + counters_ = static_cast*>(std::aligned_alloc( + alignment, capacity * sizeof(decltype(counters_[0])))); + assert(counters_ != nullptr); + for (size_t i = 0; i < capacity_; ++i) { + new (counters_ + i) std::atomic(); } - void increase(T inc) { - size_t thread_hash = std::hash{}(std::this_thread::get_id()); - counters_[get_index(thread_hash)].fetch_add(inc, std::memory_order_relaxed); - } - void decrease(T dec) { - size_t thread_hash = std::hash{}(std::this_thread::get_id()); - counters_[get_index(thread_hash)].fetch_sub(dec, std::memory_order_relaxed); - } - void set(T value) { - size_t thread_hash = std::hash{}(std::this_thread::get_id()); - counters_[get_index(thread_hash)].store(value, std::memory_order_relaxed); + for (size_t i = 0; i < capacity_; ++i) { + counters_[i].store(0); } + } + void increase(T inc) { + size_t thread_hash = + std::hash{}(std::this_thread::get_id()); + counters_[get_index(thread_hash)].fetch_add(inc, std::memory_order_relaxed); + } + void decrease(T dec) { + size_t thread_hash = + std::hash{}(std::this_thread::get_id()); + counters_[get_index(thread_hash)].fetch_sub(dec, std::memory_order_relaxed); + } + void set(T value) { + size_t thread_hash = + std::hash{}(std::this_thread::get_id()); + counters_[get_index(thread_hash)].store(value, std::memory_order_relaxed); + } - template&> F> - void call(F&& callback) { - size_t thread_hash = std::hash{}(std::this_thread::get_id()); - callback(counters_[get_index(thread_hash)]); - } + template &> F> + void call(F&& callback) { + size_t thread_hash = + std::hash{}(std::this_thread::get_id()); + callback(counters_[get_index(thread_hash)]); + } - inline void get_index() const { - size_t thread_hash = std::hash{}(std::this_thread::get_id()); - return get_index(thread_hash); - } + inline void get_index() const { + size_t thread_hash = + std::hash{}(std::this_thread::get_id()); + return get_index(thread_hash); + } - - template&> F> - void iterate(F&& func) { - for (size_t i = 0; i < capacity_; ++i) { - func(counters_[i]); - } + template &> F> + void iterate(F&& func) { + for (size_t i = 0; i < capacity_; ++i) { + func(counters_[i]); } + } - template&> F> - void iterate(F&& func) const { - for (size_t i = 0; i < capacity_; ++i) { - func(counters_[i]); - } - } - - ~CounterBasic() { - std::free(counters_); + template &> F> + void iterate(F&& func) const { + for (size_t i = 0; i < capacity_; ++i) { + func(counters_[i]); } + } - inline size_t get_index(size_t i) const { - return fast_module ? (i & (capacity_ - 1)) : (i % capacity_); - } - const bool fast_module = false; - const size_t capacity_{0}; - alignas(alignment) std::atomic* counters_{nullptr}; + ~CounterBasic() { std::free(counters_); } + + inline size_t get_index(size_t i) const { + return fast_module ? (i & (capacity_ - 1)) : (i % capacity_); + } + const bool fast_module = false; + const size_t capacity_{0}; + alignas(alignment) std::atomic* counters_{nullptr}; }; -} -} \ No newline at end of file +} // namespace metrics +} // namespace ciri diff --git a/src/metrics/counter_minmax.h b/src/metrics/counter_minmax.h index 6eafc72..f869a22 100644 --- a/src/metrics/counter_minmax.h +++ b/src/metrics/counter_minmax.h @@ -1,64 +1,64 @@ #pragma once #include #include + #include "metrics/counter_basic.h" namespace ciri { namespace metrics { -template +template class CounterMinMax { -public: - enum Mode { - Min, - Max - }; - struct CompareCallback { - Mode mode; - T upd; - void operator()(std::atomic& element) { - while (true) { - T value = element.load(std::memory_order_relaxed); - if ((value <= upd && mode == Mode::Min) || (value >= upd && mode == Mode::Max)) { - break; - } - if (element.compare_exchange_strong(value, upd, std::memory_order_relaxed)) { - break; - } - } + public: + enum Mode { Min, Max }; + struct CompareCallback { + Mode mode; + T upd; + void operator()(std::atomic& element) { + while (true) { + T value = element.load(std::memory_order_relaxed); + if ((value <= upd && mode == Mode::Min) || + (value >= upd && mode == Mode::Max)) { + break; } - }; - CounterMinMax(Mode mode, size_t capacity = 1) : mode_(mode), counter_(capacity) { - + if (element.compare_exchange_strong(value, upd, + std::memory_order_relaxed)) { + break; + } + } } + }; + CounterMinMax(Mode mode, size_t capacity = 1) + : mode_(mode), counter_(capacity) {} - void update(T upd) { - CompareCallback callback{.mode = mode_, .upd = std::move(upd)}; - counter_.call(callback); - } + void update(T upd) { + CompareCallback callback{.mode = mode_, .upd = std::move(upd)}; + counter_.call(callback); + } - T get() const { - T result = T{}; - auto max_func = [&](const std::atomic& element) { - T value = element.load(std::memory_order_relaxed); - result = result > value ? result : value; - }; - auto min_func = [&](const std::atomic& element) { - T value = element.load(std::memory_order_relaxed); - result = result < value ? result : value; - }; - switch (mode_) { - case Mode::Max: - counter_.iterate(max_func); - return result; - case Mode::Min: - counter_.iterate(min_func); - return result; - } - assert(false); + T get() const { + T result = T{}; + auto max_func = [&](const std::atomic& element) { + T value = element.load(std::memory_order_relaxed); + result = result > value ? result : value; + }; + auto min_func = [&](const std::atomic& element) { + T value = element.load(std::memory_order_relaxed); + result = result < value ? result : value; + }; + switch (mode_) { + case Mode::Max: + counter_.iterate(max_func); + return result; + case Mode::Min: + counter_.iterate(min_func); + return result; } -private: - Mode mode_; - CounterBasic counter_; + assert(false); + } + + private: + Mode mode_; + CounterBasic counter_; }; -} -} \ No newline at end of file +} // namespace metrics +} // namespace ciri diff --git a/src/metrics/counter_sum.h b/src/metrics/counter_sum.h index 12e0caf..58ecbf1 100644 --- a/src/metrics/counter_sum.h +++ b/src/metrics/counter_sum.h @@ -1,42 +1,39 @@ #pragma once +#include #include +#include #include -#include -#include #include -#include -#include #include +#include +#include + #include "metrics/counter_basic.h" namespace ciri { namespace metrics { -template +template class CounterSum { -public: - static const size_t alignment = 64; - CounterSum(size_t capacity = 1) : counter_(capacity) { - } - void increase(T inc) { - counter_.increase(inc); - } - void decrease(T dec) { - counter_.decrease(dec); - } - T get() const { - T sum = T{}; - auto f = [&](const std::atomic& element) { - sum += element.load(std::memory_order_relaxed); - }; - counter_.iterate(f); - return sum; - } + public: + static const size_t alignment = 64; + CounterSum(size_t capacity = 1) : counter_(capacity) {} + void increase(T inc) { counter_.increase(inc); } + void decrease(T dec) { counter_.decrease(dec); } + T get() const { + T sum = T{}; + auto f = [&](const std::atomic& element) { + sum += element.load(std::memory_order_relaxed); + }; + counter_.iterate(f); + return sum; + } + + ~CounterSum() = default; - ~CounterSum() = default; -private: - const bool fast_module = false; - ciri::metrics::CounterBasic counter_; + private: + const bool fast_module = false; + ciri::metrics::CounterBasic counter_; }; -} -} \ No newline at end of file +} // namespace metrics +} // namespace ciri diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 864619c..b21e9d3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,4 +14,4 @@ if(ENABLE_ASAN) endif() include(GoogleTest) -gtest_discover_tests(tests) \ No newline at end of file +gtest_discover_tests(tests) diff --git a/tests/metrics.cpp b/tests/metrics.cpp index 91fbe58..c3e7cc1 100644 --- a/tests/metrics.cpp +++ b/tests/metrics.cpp @@ -1,95 +1,94 @@ -#include -#include -#include "metrics/counter_sum.h" -#include "metrics/counter_minmax.h" -#include "metrics/counter_basic.h" #include +#include +#include +#include "metrics/counter_basic.h" +#include "metrics/counter_minmax.h" +#include "metrics/counter_sum.h" using namespace std::chrono_literals; - TEST(MetricsTests, CounterSum) { - ciri::metrics::CounterSum c; - for (size_t i = 0; i < 100; ++i) { - c.increase(10); - } + ciri::metrics::CounterSum c; + for (size_t i = 0; i < 100; ++i) { + c.increase(10); + } - for (size_t i = 0; i < 100; ++i) { - c.decrease(10); - } + for (size_t i = 0; i < 100; ++i) { + c.decrease(10); + } - std::atomic_thread_fence(std::memory_order_acquire); - EXPECT_EQ(c.get(), 0); + std::atomic_thread_fence(std::memory_order_acquire); + EXPECT_EQ(c.get(), 0); } TEST(MetricsTests, CounterMax) { - size_t n = 4; - size_t iterations = 100'000'000; + size_t n = 4; + size_t iterations = 100'000'000; // clang-format off + std::vector threads; + threads.reserve(n); + ciri::metrics::CounterMinMax c(ciri::metrics::CounterMinMax::Mode::Max, 4); + for (size_t i = 0; i < n; ++i) { + threads.emplace_back([&c, &iterations, num = i]() { + for (size_t i = 0; i <= iterations; ++i) { + c.update(num * i); + } + }); + } + for (size_t i = 0; i < n; ++i) { + threads[i].join(); + } + std::atomic_thread_fence(std::memory_order_acquire); + + EXPECT_EQ(c.get(), (n - 1) * iterations); +} + +TEST(MetricsTests, CounterStress) { + size_t n = 4; + size_t iterations = 100'000'000; // clang-format off + { std::vector threads; threads.reserve(n); - ciri::metrics::CounterMinMax c(ciri::metrics::CounterMinMax::Mode::Max, 4); + std::atomic_size_t dummy; + std::cout << "dummy: "; + auto start = std::chrono::system_clock::now(); for (size_t i = 0; i < n; ++i) { - threads.emplace_back([&c, &iterations, num = i]() { - for (size_t i = 0; i <= iterations; ++i) { - c.update(num * i); - } - }); + threads.emplace_back([&]() { + size_t sum = 0; + for (size_t k = 0; k < iterations; ++k) { + dummy.fetch_add(k, std::memory_order_relaxed); + } + }); } + size_t sum = dummy.load(std::memory_order_relaxed); for (size_t i = 0; i < n; ++i) { - threads[i].join(); + threads[i].join(); } - std::atomic_thread_fence(std::memory_order_acquire); - - EXPECT_EQ(c.get(), (n - 1) * iterations); -} - -TEST(MetricsTests, CounterStress) { - size_t n = 4; - size_t iterations = 100'000'000; - { - std::vector threads; - threads.reserve(n); - std::atomic_size_t dummy; - std::cout << "dummy: "; - auto start = std::chrono::system_clock::now(); - for (size_t i = 0; i < n; ++i) { - threads.emplace_back([&]() { - size_t sum = 0; - for (size_t k = 0; k < iterations; ++k) { - dummy.fetch_add(k, std::memory_order_relaxed); - } - }); - } - size_t sum = dummy.load(std::memory_order_relaxed); - for (size_t i = 0; i < n; ++i) { - threads[i].join(); + threads.clear(); + auto finish = std::chrono::system_clock::now(); + auto duration = finish - start; + std::cout << std::chrono::duration_cast(duration).count() << "ms\n"; + } + { + std::vector threads; + threads.reserve(n); + ciri::metrics::CounterSum c(1); + std::cout << "ciri: "; + auto start = std::chrono::system_clock::now(); + for (size_t i = 0; i < n; ++i) { + threads.emplace_back([&]() { + size_t sum = 0; + for (size_t k = 0; k < iterations; ++k) { + c.increase(k); } - threads.clear(); - auto finish = std::chrono::system_clock::now(); - auto duration = finish - start; - std::cout << std::chrono::duration_cast(duration).count() << "ms\n"; + }); } - { - std::vector threads; - threads.reserve(n); - ciri::metrics::CounterSum c(1); - std::cout << "ciri: "; - auto start = std::chrono::system_clock::now(); - for (size_t i = 0; i < n; ++i) { - threads.emplace_back([&]() { - size_t sum = 0; - for (size_t k = 0; k < iterations; ++k) { - c.increase(k); - } - }); - } - size_t sum = c.get(); - for (size_t i = 0; i < n; ++i) { - threads[i].join(); - } - auto finish = std::chrono::system_clock::now(); - auto duration = finish - start; - std::cout << std::chrono::duration_cast(duration).count() << "ms\n"; + size_t sum = c.get(); + for (size_t i = 0; i < n; ++i) { + threads[i].join(); } + auto finish = std::chrono::system_clock::now(); + auto duration = finish - start; + std::cout << std::chrono::duration_cast(duration).count() << "ms\n"; + } }