diff --git a/cpp/arcticdb/CMakeLists.txt b/cpp/arcticdb/CMakeLists.txt index 6149a0709d..f71aec6a16 100644 --- a/cpp/arcticdb/CMakeLists.txt +++ b/cpp/arcticdb/CMakeLists.txt @@ -360,6 +360,7 @@ set(arcticdb_srcs util/sparse_utils.hpp util/storage_lock.hpp util/string_utils.hpp + util/thread_cached_int.hpp util/timeouts.hpp util/timer.hpp util/trace.hpp diff --git a/cpp/arcticdb/util/allocator.cpp b/cpp/arcticdb/util/allocator.cpp index 32be101776..9fa09c2ded 100644 --- a/cpp/arcticdb/util/allocator.cpp +++ b/cpp/arcticdb/util/allocator.cpp @@ -12,8 +12,8 @@ #include #include #include +#include #include -#include #include @@ -152,7 +152,7 @@ namespace arcticdb { namespace { template auto& free_count_of(){ - static folly::ThreadCachedInt free_count; + static ThreadCachedInt free_count; return free_count; }; } diff --git a/cpp/arcticdb/util/thread_cached_int.hpp b/cpp/arcticdb/util/thread_cached_int.hpp new file mode 100644 index 0000000000..6c5675b727 --- /dev/null +++ b/cpp/arcticdb/util/thread_cached_int.hpp @@ -0,0 +1,78 @@ +/* Copyright 2023 Man Group Operations Limited + * + * Use of this software is governed by the Business Source License 1.1 included in the file licenses/BSL.txt. + * + * As of the Change Date specified in that file, in accordance with the Business Source License, use of this software will be governed by the Apache License, version 2.0. + * + * The code in this file is derived from https://github.com/facebook/folly/blob/main/folly/ThreadCachedInt.h under the Apache 2.0 license which is available in full at https://github.com/facebook/folly/blob/main/LICENSE. + */ + +#pragma once + +#include +#include + +namespace arcticdb { + + template + class ThreadCachedInt { + public: + explicit ThreadCachedInt(IntT initialVal = 0, uint32_t cacheSize = 1000) + : target_(initialVal), cacheSize_(cacheSize) {} + + ThreadCachedInt(const ThreadCachedInt&) = delete; + ThreadCachedInt& operator=(const ThreadCachedInt&) = delete; + + void increment(IntT inc) { + auto cache = cache_.get(); + if (cache == nullptr) { + cache = new IntCache(*this); + cache_.reset(cache); + } + cache->increment(inc); + } + + // Quickly grabs the current value which may not include some cached increments. + IntT readFast() const { + return target_.load(std::memory_order_relaxed); + } + + // Quickly reads and resets current value (doesn't reset cached increments). + IntT readFastAndReset() { + return target_.exchange(0, std::memory_order_release); + } + + private: + struct IntCache; + + std::atomic target_; + std::atomic cacheSize_; + boost::thread_specific_ptr cache_; // Must be last for dtor ordering + + struct IntCache { + ThreadCachedInt* parent_; + mutable IntT val_; + mutable uint32_t numUpdates_; + + explicit IntCache(ThreadCachedInt& parent) + : parent_(&parent), val_(0), numUpdates_(0) {} + + void increment(IntT inc) { + val_ += inc; + ++numUpdates_; + if (numUpdates_ > parent_->cacheSize_.load(std::memory_order_acquire)) { + flush(); + } + } + + void flush() const { + parent_->target_.fetch_add(val_, std::memory_order_release); + val_ = 0; + numUpdates_ = 0; + } + + ~IntCache() { flush(); } + }; + }; + +} //namespace arcticdb