Skip to content

Commit 2f4b9f7

Browse files
haowu14facebook-github-bot
authored andcommitted
Allow configuring the number of allocators per
Summary: To avoid the mutex of region allocator being the single point of contention, introduce a config to create multiple region allocators. Reviewed By: pbhandar2 Differential Revision: D73201432 fbshipit-source-id: f9395e23737d6b01fe93b7c4c124e5ee295ff359
1 parent db28359 commit 2f4b9f7

10 files changed

Lines changed: 109 additions & 47 deletions

File tree

cachelib/allocator/nvmcache/NavyConfig.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,16 @@ class BlockCacheConfig {
273273
// {1, 2, 3} gives the 1/6th of the items in the first segment (P0
274274
// least important), 2/6th of the items in the second segment
275275
// (P1), and finally 3/6th of the items in the third segment (P2).
276+
// @param Number of allocators for each priority. If not set, each priority
277+
// would have one allocator.
276278
BlockCacheConfig& enableSegmentedFifo(
277-
std::vector<unsigned int> sFifoSegmentRatio) noexcept {
279+
std::vector<unsigned int> sFifoSegmentRatio,
280+
std::vector<uint32_t> allocatorCounts = {}) noexcept {
278281
sFifoSegmentRatio_ = std::move(sFifoSegmentRatio);
282+
if (allocatorCounts.size() > 0) {
283+
XDCHECK_EQ(sFifoSegmentRatio_.size(), allocatorCounts.size());
284+
allocatorsPerPriority_ = std::move(allocatorCounts);
285+
}
279286
lru_ = false;
280287
return *this;
281288
}
@@ -342,6 +349,11 @@ class BlockCacheConfig {
342349
return *this;
343350
}
344351

352+
BlockCacheConfig& setAllocatorCount(uint32_t numAllocators) noexcept {
353+
allocatorsPerPriority_ = {numAllocators};
354+
return *this;
355+
}
356+
345357
bool isLruEnabled() const { return lru_; }
346358

347359
const std::vector<unsigned int>& getSFifoSegmentRatio() const {
@@ -368,6 +380,10 @@ class BlockCacheConfig {
368380

369381
bool isPreciseRemove() const { return preciseRemove_; }
370382

383+
const std::vector<uint32_t>& getNumAllocatorsPerPriority() const {
384+
return allocatorsPerPriority_;
385+
}
386+
371387
private:
372388
// Whether Navy BlockCache will use region-based LRU eviction policy.
373389
bool lru_{true};
@@ -400,6 +416,11 @@ class BlockCacheConfig {
400416
// Whether the region manager workers flushes asynchronously.
401417
bool regionManagerFlushAsync_{false};
402418

419+
// Number of allocators per priority.
420+
// Do not set this directly. This should be configured by setAllocatorCount
421+
// for FIFO and LRU, and enableSegmentedFifio for segmented FIFO.
422+
std::vector<uint32_t> allocatorsPerPriority_{1};
423+
403424
friend class NavyConfig;
404425
};
405426

cachelib/allocator/nvmcache/NavySetup.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ uint64_t setupBlockCache(const navy::BlockCacheConfig& blockCacheConfig,
174174
blockCacheConfig.getCleanRegionThreads());
175175
blockCache->setRegionManagerFlushAsync(
176176
blockCacheConfig.isRegionManagerFlushAsync());
177+
blockCache->setNumAllocatorsPerPriority(
178+
blockCacheConfig.getNumAllocatorsPerPriority());
177179

178180
blockCache->setReinsertionConfig(blockCacheConfig.getReinsertionConfig());
179181

cachelib/navy/Factory.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,15 @@ class BlockCacheProtoImpl final : public BlockCacheProto {
8080
if (config_.evictionPolicy) {
8181
throw std::invalid_argument("There's already an eviction policy set");
8282
}
83-
config_.numPriorities = static_cast<uint16_t>(segmentRatio.size());
8483
config_.evictionPolicy =
8584
std::make_unique<SegmentedFifoPolicy>(std::move(segmentRatio));
8685
}
8786

87+
void setNumAllocatorsPerPriority(
88+
std::vector<uint32_t> allocatorsPerPriority) override {
89+
config_.allocatorsPerPriority = std::move(allocatorsPerPriority);
90+
}
91+
8892
void setReadBufferSize(uint32_t size) override {
8993
config_.readBufferSize = size;
9094
}

cachelib/navy/Factory.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ class BlockCacheProto {
8989

9090
// (Optional) Set if the region manager worker flush should be async.
9191
virtual void setRegionManagerFlushAsync(bool flushAsync) = 0;
92+
93+
// Set number of allocators per priority.
94+
virtual void setNumAllocatorsPerPriority(
95+
std::vector<uint32_t> numAllocatorsPerPriority) = 0;
9296
};
9397

9498
// BigHash engine proto. BigHash is used to cache small objects (under 2KB)

cachelib/navy/block_cache/Allocator.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,27 @@ void RegionAllocator::setAllocationRegion(RegionId rid) {
3232

3333
void RegionAllocator::reset() { rid_ = RegionId{}; }
3434

35-
Allocator::Allocator(RegionManager& regionManager, uint16_t numPriorities)
35+
Allocator::Allocator(RegionManager& regionManager,
36+
const std::vector<uint32_t>& allocatorsPerPriority)
3637
: regionManager_{regionManager} {
38+
size_t numPriorities = allocatorsPerPriority.size();
3739
XLOGF(INFO,
3840
"Enable priority-based allocation for Allocator. Number of "
3941
"priorities: {}",
4042
numPriorities);
4143
for (uint16_t i = 0; i < numPriorities; i++) {
42-
allocators_.emplace_back(i /* priority */);
44+
allocators_.emplace_back();
45+
for (uint32_t j = 0; j < allocatorsPerPriority[i]; j++) {
46+
allocators_.back().emplace_back(i /* priority */);
47+
}
4348
}
4449
}
4550

4651
std::tuple<RegionDescriptor, uint32_t, RelAddress> Allocator::allocate(
47-
uint32_t size, uint16_t priority, bool canWait) {
52+
uint32_t size, uint16_t priority, bool canWait, uint64_t keyHash) {
4853
XDCHECK_LT(priority, allocators_.size());
49-
RegionAllocator* ra = &allocators_[priority];
54+
RegionAllocator* ra =
55+
&allocators_[priority][keyHash % allocators_[priority].size()];
5056
if (size == 0 || size > regionManager_.regionSize()) {
5157
return std::make_tuple(RegionDescriptor{OpenStatus::Error}, size,
5258
RelAddress());
@@ -125,18 +131,22 @@ void Allocator::flushAndReleaseRegionFromRALocked(RegionAllocator& ra,
125131
}
126132

127133
void Allocator::flush() {
128-
for (auto& ra : allocators_) {
129-
std::lock_guard<TimedMutex> lock{ra.getLock()};
130-
flushAndReleaseRegionFromRALocked(ra, false /* async */);
134+
for (auto& ras : allocators_) {
135+
for (auto& ra : ras) {
136+
std::lock_guard<TimedMutex> lock{ra.getLock()};
137+
flushAndReleaseRegionFromRALocked(ra, false /* async */);
138+
}
131139
}
132140
}
133141

134142
void Allocator::reset() {
135143
allocRetryWaits_.set(0);
136144
regionManager_.reset();
137-
for (auto& ra : allocators_) {
138-
std::lock_guard<TimedMutex> lock{ra.getLock()};
139-
ra.reset();
145+
for (auto& ras : allocators_) {
146+
for (auto& ra : ras) {
147+
std::lock_guard<TimedMutex> lock{ra.getLock()};
148+
ra.reset();
149+
}
140150
}
141151
}
142152

cachelib/navy/block_cache/Allocator.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,19 @@ class Allocator {
7777
public:
7878
// Constructs an allocator.
7979
//
80-
// @param regionManager Used to get eviction information and for
81-
// locking regions
82-
// @param numPriorities Specifies how many priorities this allocator
83-
// supports
84-
// Throws std::exception if invalid arguments
85-
explicit Allocator(RegionManager& regionManager, uint16_t numPriorities);
80+
// @param regionManager Used to get eviction information and for
81+
// locking regions
82+
// @param allocatorsPerPriority Number of allocators per priority
83+
explicit Allocator(RegionManager& regionManager,
84+
const std::vector<uint32_t>& allocatorsPerPriority);
8685

8786
// Allocates and opens for writing.
8887
//
8988
// @param size Allocation size
9089
// @param priority Specifies how important this allocation is
9190
// @param canWait If true, wait until allocation can be retried
91+
// @param keyHash Hash of the key to allocate for. Used for deciding the
92+
// allocator if there's more than one for the priority.
9293
//
9394
// Returns a tuple containing region descriptor, allocated slotSize and
9495
// allocated address
@@ -102,7 +103,8 @@ class Allocator {
102103
// this allocator.
103104
std::tuple<RegionDescriptor, uint32_t, RelAddress> allocate(uint32_t size,
104105
uint16_t priority,
105-
bool canWait);
106+
bool canWait,
107+
uint64_t keyHash);
106108

107109
// Closes the region.
108110
void close(RegionDescriptor&& rid);
@@ -132,7 +134,7 @@ class Allocator {
132134

133135
RegionManager& regionManager_;
134136
// Multiple allocators when we use priority-based allocation
135-
std::vector<RegionAllocator> allocators_;
137+
std::vector<std::vector<RegionAllocator>> allocators_;
136138

137139
mutable AtomicCounter allocRetryWaits_;
138140
};

cachelib/navy/block_cache/BlockCache.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,14 @@ BlockCache::Config& BlockCache::Config::validate() {
6464
if (numInMemBuffers == 0) {
6565
throw std::invalid_argument("there must be at least one in-mem buffers");
6666
}
67-
if (numPriorities == 0) {
68-
throw std::invalid_argument("allocator must have at least one priority");
67+
if (allocatorsPerPriority.size() == 0) {
68+
throw std::invalid_argument("Allocators must have at least one priority");
69+
}
70+
for (const auto& ac : allocatorsPerPriority) {
71+
if (ac == 0) {
72+
throw std::invalid_argument(
73+
"Each priority must have at least one allocator");
74+
}
6975
}
7076

7177
reinsertionConfig.validate();
@@ -115,7 +121,8 @@ BlockCache::BlockCache(Config&& config)
115121

116122
BlockCache::BlockCache(Config&& config, ValidConfigTag)
117123
: config_{serializeConfig(config)},
118-
numPriorities_{config.numPriorities},
124+
numPriorities_{
125+
static_cast<uint16_t>(config.allocatorsPerPriority.size())},
119126
checkExpired_{std::move(config.checkExpired)},
120127
destructorCb_{std::move(config.destructorCb)},
121128
checksumData_{config.checksum},
@@ -141,10 +148,10 @@ BlockCache::BlockCache(Config&& config, ValidConfigTag)
141148
bindThis(&BlockCache::onRegionCleanup, *this),
142149
std::move(config.evictionPolicy),
143150
config.numInMemBuffers,
144-
config.numPriorities,
151+
static_cast<uint16_t>(config.allocatorsPerPriority.size()),
145152
config.inMemBufFlushRetryLimit,
146153
config.regionManagerFlushAsync},
147-
allocator_{regionManager_, config.numPriorities},
154+
allocator_{regionManager_, config.allocatorsPerPriority},
148155
reinsertionPolicy_{makeReinsertionPolicy(config.reinsertionConfig)} {
149156
validate(config);
150157
XLOG(INFO, "Block cache created");
@@ -181,8 +188,8 @@ Status BlockCache::insert(HashedKey hk, BufferView value) {
181188
}
182189

183190
// All newly inserted items are assigned with the lowest priority
184-
auto [desc, slotSize, addr] =
185-
allocator_.allocate(size, kDefaultItemPriority, true /* canWait */);
191+
auto [desc, slotSize, addr] = allocator_.allocate(
192+
size, kDefaultItemPriority, true /* canWait */, hk.keyHash());
186193

187194
switch (desc.status()) {
188195
case OpenStatus::Error:
@@ -634,7 +641,7 @@ BlockCache::ReinsertionRes BlockCache::reinsertOrRemoveItem(
634641

635642
uint32_t size = serializedSize(hk.key().size(), value.size());
636643
auto [desc, slotSize, addr] =
637-
allocator_.allocate(size, priority, false /* canWait */);
644+
allocator_.allocate(size, priority, false /* canWait */, hk.keyHash());
638645

639646
switch (desc.status()) {
640647
case OpenStatus::Ready:

cachelib/navy/block_cache/BlockCache.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,12 @@ class BlockCache final : public Engine {
8181
// directly fail it.
8282
uint16_t inMemBufFlushRetryLimit{10};
8383

84-
// Number of priorities. Items of the same priority will be put into
85-
// the same reigon. The effect of priorities will be up to the particular
84+
// The effect of priorities will be up to the particular
8685
// eviction policy. There must be at least one priority.
87-
uint16_t numPriorities{1};
86+
// This vector is the number of allocators for each priority. Under the same
87+
// priority, allocators will be picked by key hash and written into one of
88+
// the regions.
89+
std::vector<uint32_t> allocatorsPerPriority{1};
8890

8991
// whether to remove an item by checking the full key.
9092
bool preciseRemove{false};

0 commit comments

Comments
 (0)