Skip to content

Commit

Permalink
Make kShards configurable in NvmCache
Browse files Browse the repository at this point in the history
Summary: During T2 testing, to avoid lock contention in NvmCache, we had to increase this from 8K to 64-128K. Make it configurable and default to 8K

Reviewed By: therealgymmy

Differential Revision: D69208409

fbshipit-source-id: a82f054eb788a1eec619a312e2a2b4960713f439
  • Loading branch information
Stuart Clark authored and facebook-github-bot committed Mar 2, 2025
1 parent 155ea14 commit 6d1a739
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 20 deletions.
4 changes: 4 additions & 0 deletions cachelib/allocator/nvmcache/NavyConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ class NavyConfig {
uint32_t getMaxConcurrentInserts() const { return maxConcurrentInserts_; }
uint64_t getMaxParcelMemoryMB() const { return maxParcelMemoryMB_; }
bool getUseEstimatedWriteSize() const { return useEstimatedWriteSize_; }
size_t getNumShards() const { return numShards_; }

// Setters:
// Enable "dynamic_random" admission policy.
Expand Down Expand Up @@ -678,6 +679,7 @@ class NavyConfig {
void setUseEstimatedWriteSize(bool useEstimatedWriteSize) noexcept {
useEstimatedWriteSize_ = useEstimatedWriteSize;
}
void setNumShards(size_t numShards) noexcept { numShards_ = numShards; }

const std::vector<EnginesConfig>& enginesConfigs() const {
return enginesConfigs_;
Expand Down Expand Up @@ -762,6 +764,8 @@ class NavyConfig {
// Whether Navy support the NVMe FDP data placement(TP4146) directives or not.
// Reference: https://nvmexpress.org/nvmeflexible-data-placement-fdp-blog/
bool enableFDP_{false};
// Number of nvm lock shards
size_t numShards_{8192};
};
} // namespace navy
} // namespace cachelib
Expand Down
50 changes: 33 additions & 17 deletions cachelib/allocator/nvmcache/NvmCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,13 @@ class NvmCache {
using FillMap =
folly::F14ValueMap<folly::StringPiece, std::unique_ptr<GetCtx>>;

static size_t getShardForKey(HashedKey hk) { return hk.keyHash() % kShards; }
size_t getShardForKey(HashedKey hk) const {
return hk.keyHash() % numShards_;
}

size_t getNumShards() const { return numShards_; }

static size_t getShardForKey(folly::StringPiece key) {
size_t getShardForKey(folly::StringPiece key) const {
return getShardForKey(HashedKey{key});
}

Expand Down Expand Up @@ -489,42 +493,45 @@ class NvmCache {
C& cache_; //< cache allocator
std::atomic<bool> navyEnabled_{true}; //< switch to turn off/on navy

static constexpr size_t kShards = 8192;
const size_t numShards_;

// a function to check if an item is expired
const navy::ExpiredCheck checkExpired_;

// a map of all pending fills to prevent thundering herds
struct {
struct FillStruct {
alignas(folly::hardware_destructive_interference_size) FillMap fills_;
} fills_[kShards];
};
std::vector<FillStruct> fills_;

// a map of fill locks for each shard
struct {
struct FillLockStruct {
alignas(folly::hardware_destructive_interference_size) TimedMutex fillLock_;
} fillLock_[kShards];
};
std::vector<FillLockStruct> fillLock_;

// currently queued put operations to navy.
std::array<PutContexts, kShards> putContexts_;
std::vector<PutContexts> putContexts_;

// currently queued delete operations to navy.
std::array<DelContexts, kShards> delContexts_;
std::vector<DelContexts> delContexts_;

// co-ordination between in-flight evictions from cache that are not queued
// to navy and in-flight gets into nvmcache that are not yet queued.
std::array<InFlightPuts, kShards> inflightPuts_;
std::array<TombStones, kShards> tombstones_;
std::vector<InFlightPuts> inflightPuts_;
std::vector<TombStones> tombstones_;

const ItemDestructor itemDestructor_;

mutable std::array<TimedMutex, kShards> itemDestructorMutex_{TimedMutex()};
mutable std::vector<TimedMutex> itemDestructorMutex_{numShards_,
TimedMutex()};
// Used to track the keys of items present in NVM that should be excluded for
// executing Destructor upon eviction from NVM, if the item is not present in
// DRAM. The ownership of item destructor is already managed elsewhere for
// these keys. This data struct is updated prior to issueing NvmCache::remove
// to handle any racy eviction from NVM before the NvmCache::remove is
// finished.
std::array<folly::F14FastSet<std::string>, kShards> itemRemoved_;
std::vector<folly::F14FastSet<std::string>> itemRemoved_;

std::unique_ptr<cachelib::navy::AbstractCache> navyCache_;

Expand Down Expand Up @@ -588,7 +595,7 @@ template <typename C>
typename NvmCache<C>::DeleteTombStoneGuard NvmCache<C>::createDeleteTombStone(
HashedKey hk) {
// lower bits for shard and higher bits for key.
const auto shard = hk.keyHash() % kShards;
const auto shard = hk.keyHash() % numShards_;
auto guard = tombstones_[shard].add(hk.key());

// need to synchronize tombstone creations with fill lock to serialize
Expand All @@ -609,7 +616,7 @@ typename NvmCache<C>::DeleteTombStoneGuard NvmCache<C>::createDeleteTombStone(
template <typename C>
bool NvmCache<C>::hasTombStone(HashedKey hk) {
// lower bits for shard and higher bits for key.
const auto shard = hk.keyHash() % kShards;
const auto shard = hk.keyHash() % numShards_;
return tombstones_[shard].isPresent(hk.key());
}

Expand Down Expand Up @@ -947,11 +954,20 @@ NvmCache<C>::NvmCache(C& c,
const ItemDestructor& itemDestructor)
: config_(config.validateAndSetDefaults()),
cache_(c),
numShards_{config_.navyConfig.getNumShards()},
checkExpired_([](navy::BufferView v) -> bool {
const auto& nvmItem = *reinterpret_cast<const NvmItem*>(v.data());
return nvmItem.isExpired();
}),
itemDestructor_(itemDestructor) {
fills_(numShards_),
fillLock_(numShards_),
putContexts_(numShards_),
delContexts_(numShards_),
inflightPuts_(numShards_),
tombstones_(numShards_),
itemDestructor_(itemDestructor),
itemDestructorMutex_(numShards_),
itemRemoved_(numShards_) {
navyCache_ = createNavyCache(
config_.navyConfig,
checkExpired_,
Expand Down Expand Up @@ -1509,7 +1525,7 @@ bool NvmCache<C>::checkAndUnmarkItemRemovedLocked(HashedKey hk) {
template <typename C>
uint64_t NvmCache<C>::getNvmItemRemovedSize() const {
uint64_t size = 0;
for (size_t i = 0; i < kShards; ++i) {
for (size_t i = 0; i < numShards_; ++i) {
auto lock = std::unique_lock<TimedMutex>{itemDestructorMutex_[i]};
size += itemRemoved_[i].size();
}
Expand Down
7 changes: 4 additions & 3 deletions cachelib/allocator/nvmcache/tests/NvmTestBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,10 @@ class NvmCacheTest : public testing::Test {
protected:
// Helper for ShardHashIsNotFillMapHash because we're the friend of NvmCache.
std::pair<size_t, size_t> getNvmShardAndHashForKey(folly::StringPiece key) {
auto shard = NvmCacheT::getShardForKey(key);
auto hash =
typename NvmCacheT::FillMap{}.hash_function()(key) % NvmCacheT::kShards;
auto nvm = getNvmCache();
auto shard = nvm->getShardForKey(key);
auto hash = typename NvmCacheT::FillMap{}.hash_function()(key) %
nvm->getNumShards();
return std::make_pair(shard, hash);
}

Expand Down

0 comments on commit 6d1a739

Please sign in to comment.