Skip to content

Commit 771f1e1

Browse files
rlyerlymeta-codesync[bot]
authored andcommitted
Back the hash table with huge pages on create and attach
Summary: Backs the access-container hash-table segments (kShmHashTableName, kShmChainedItemHashTableName) with HugeTLB pages when the cache is configured via enableHugePages(). To avoid a footgun where a per-AccessConfig page size could silently diverge from the cache-wide enableHugePages() setting, pageSize is removed entirely from AccessConfig (ChainedHashTable::Config). Persistence is normal-page only, so PersistenceManager now rejects a huge-page config outright. Reviewed By: AlnisM Differential Revision: D113626131 fbshipit-source-id: 32ff4d5fafe6aa9d55c9967cbc822381ca460b59
1 parent 4ab246f commit 771f1e1

5 files changed

Lines changed: 68 additions & 55 deletions

File tree

cachelib/allocator/CacheAllocator.h

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,6 +2255,14 @@ class CacheAllocator : public CacheBase {
22552255
std::chrono::seconds timeout = std::chrono::seconds{0});
22562256

22572257
ShmSegmentOpts createShmCacheOpts();
2258+
2259+
// Builds the shm options for an access-container (hash table) segment,
2260+
ShmSegmentOpts createShmAccessOpts();
2261+
2262+
// Validates and applies a requested huge page size (bytes; 0 == normal) onto
2263+
// opts, throwing if the kernel does not support the size.
2264+
void applyHugePageOpts(ShmSegmentOpts& opts, const PageSize& pageSize) const;
2265+
22582266
std::unique_ptr<MemoryAllocator> createNewMemoryAllocator();
22592267
std::unique_ptr<MemoryAllocator> restoreMemoryAllocator();
22602268
std::unique_ptr<CCacheManager> restoreCCacheManager();
@@ -2805,23 +2813,38 @@ ShmSegmentOpts CacheAllocator<CacheTrait>::createShmCacheOpts() {
28052813
throw std::invalid_argument("CacheLib only supports a single memory tier");
28062814
}
28072815
opts.memBindNumaNodes = config_.memoryTierConfigs[0].getMemBind();
2816+
applyHugePageOpts(opts, config_.hugePageSize);
2817+
return opts;
2818+
}
28082819

2809-
if (config_.hugePageSize.isHugePage()) {
2810-
if (!PageSize::supportedHugePageSizes().contains(
2811-
config_.hugePageSize.getPageSize())) {
2812-
throw std::invalid_argument(fmt::format(
2813-
"Requested huge page size {} is not supported by the kernel",
2814-
config_.hugePageSize.getPageSize()));
2815-
} else if (config_.hugePageMountDir.empty() && config_.usePosixShm) {
2816-
throw std::invalid_argument(
2817-
"Requested huge pages for POSIX shared memory regions but didn't "
2818-
"supply a hugetlbfs mount");
2819-
}
2820-
opts.pageSize = config_.hugePageSize;
2821-
}
2820+
template <typename CacheTrait>
2821+
ShmSegmentOpts CacheAllocator<CacheTrait>::createShmAccessOpts() {
2822+
ShmSegmentOpts opts;
2823+
applyHugePageOpts(opts, config_.hugePageSize);
28222824
return opts;
28232825
}
28242826

2827+
template <typename CacheTrait>
2828+
void CacheAllocator<CacheTrait>::applyHugePageOpts(
2829+
ShmSegmentOpts& opts, const PageSize& pageSize) const {
2830+
if (!pageSize.isHugePage()) {
2831+
return;
2832+
} else if (!PageSize::supportedHugePageSizes().contains(
2833+
pageSize.getPageSize())) {
2834+
throw std::invalid_argument(fmt::format(
2835+
"Requested huge page size {} is not supported by the kernel",
2836+
pageSize.getPageSize()));
2837+
} else if (config_.hugePageMountDir.empty() && config_.usePosixShm) {
2838+
throw std::invalid_argument(
2839+
"Requested huge pages for POSIX shared memory regions but didn't "
2840+
"supply a hugetlbfs mount");
2841+
}
2842+
opts.pageSize = pageSize;
2843+
// The segment is attached at a page-aligned address, so its alignment must be
2844+
// at least the huge page size or the kernel rejects the mapping
2845+
opts.alignment = std::max(opts.alignment, pageSize.getPageSize());
2846+
}
2847+
28252848
template <typename CacheTrait>
28262849
std::unique_ptr<MemoryAllocator>
28272850
CacheAllocator<CacheTrait>::createNewMemoryAllocator() {
@@ -3029,17 +3052,18 @@ CacheAllocator<CacheTrait>::initAccessContainer(InitMemType type,
30293052
name,
30303053
AccessContainer::getRequiredSize(config.getNumBuckets()),
30313054
nullptr,
3032-
ShmSegmentOpts(config.getPageSize()))
3055+
createShmAccessOpts())
30333056
.addr,
30343057
compressor_,
30353058
[this](Item* it) -> WriteHandle { return acquire(it); });
30363059
} else if (type == InitMemType::kMemAttach) {
30373060
return std::make_unique<AccessContainer>(
30383061
deserializer_->deserialize<AccessSerializationType>(),
30393062
config,
3040-
shmManager_->attachShm(name),
3063+
shmManager_->attachShm(name, nullptr, createShmAccessOpts()),
30413064
compressor_,
3042-
[this](Item* it) -> WriteHandle { return acquire(it); });
3065+
[this](Item* it) -> WriteHandle { return acquire(it); },
3066+
config_.hugePageSize);
30433067
}
30443068

30453069
// Invalid type

cachelib/allocator/ChainedHashTable.h

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -239,26 +239,15 @@ class ChainedHashTable {
239239

240240
// @param bucketsPower number of buckets in base 2 logarithm
241241
// @param locksPower number of locks in base 2 logarithm
242-
// @param pageSize page size
243-
Config(unsigned int bucketsPower,
244-
unsigned int locksPower,
245-
PageSize pageSize = PageSize())
246-
: Config(bucketsPower,
247-
locksPower,
248-
std::make_shared<MurmurHash2>(),
249-
std::move(pageSize)) {}
242+
Config(unsigned int bucketsPower, unsigned int locksPower)
243+
: Config(bucketsPower, locksPower, std::make_shared<MurmurHash2>()) {}
250244

251245
// @param bucketsPower number of buckets in base 2 logarithm
252246
// @param locksPower number of locks in base 2 logarithm
253247
// @param hasher the key hash function
254-
// @param pageSize page size
255-
Config(unsigned int bucketsPower,
256-
unsigned int locksPower,
257-
Hasher hasher,
258-
PageSize pageSize = PageSize())
248+
Config(unsigned int bucketsPower, unsigned int locksPower, Hasher hasher)
259249
: bucketsPower_(bucketsPower),
260250
locksPower_(locksPower),
261-
pageSize_(std::move(pageSize)),
262251
hasher_(std::move(hasher)) {
263252
if (bucketsPower_ > kMaxBucketPower || locksPower_ > kMaxLockPower ||
264253
locksPower_ > bucketsPower_) {
@@ -314,8 +303,6 @@ class ChainedHashTable {
314303
return configMap;
315304
}
316305

317-
const PageSize& getPageSize() const { return pageSize_; }
318-
319306
private:
320307
// 4 billion buckets should be good enough for everyone.
321308
static constexpr unsigned int kMaxBucketPower = 32;
@@ -330,8 +317,6 @@ class ChainedHashTable {
330317
// total number of locks for the hashtable expressed as a power of two.
331318
unsigned int locksPower_{5};
332319

333-
PageSize pageSize_{};
334-
335320
Hasher hasher_ = std::make_shared<MurmurHash2>();
336321
};
337322

@@ -392,6 +377,8 @@ class ChainedHashTable {
392377
// @param memSegment shared memory segment for the hash table
393378
// @param compressor object used to compress/decompress node pointers
394379
// @param hm the functor that creates a Handle from T*
380+
// @param hugePageSize page size the segment was backed with (bytes), used
381+
// to validate the mapped segment size.
395382
//
396383
// @throw std::invalid argument if the bucket power in new config does not
397384
// match the previous state or the size of the memSegment does not
@@ -400,7 +387,8 @@ class ChainedHashTable {
400387
const Config& newConfig,
401388
ShmAddr memSegment,
402389
const PtrCompressor& compressor,
403-
HandleMaker hm = kDefaultHandleMaker);
390+
HandleMaker hm = kDefaultHandleMaker,
391+
PageSize hugePageSize = PageSize());
404392

405393
// restore hash table from previous state. This only works when the
406394
// hash table memory is managed by the user.
@@ -411,6 +399,8 @@ class ChainedHashTable {
411399
// @param nBytes size of memory allocation pointed to by memStart
412400
// @param compressor object used to compress/decompress node pointers
413401
// @param hm the functor that creates a Handle from T*
402+
// @param hugePageSize page size the segment was backed with (bytes), used
403+
// to validate the mapped segment size.
414404
//
415405
// @throw std::invalid argument if the bucket power in new config does not
416406
// match the previous state or the size of the memSegment does not
@@ -420,7 +410,8 @@ class ChainedHashTable {
420410
void* memStart,
421411
size_t nBytes,
422412
const PtrCompressor& compressor,
423-
HandleMaker hm = kDefaultHandleMaker);
413+
HandleMaker hm = kDefaultHandleMaker,
414+
PageSize hugePageSize = PageSize());
424415

425416
Container(const Container&) = delete;
426417
Container& operator=(const Container&) = delete;
@@ -1024,13 +1015,15 @@ ChainedHashTable::Container<T, HookPtr, LockT>::Container(
10241015
const Config& config,
10251016
ShmAddr memSegment,
10261017
const PtrCompressor& compressor,
1027-
HandleMaker hm)
1018+
HandleMaker hm,
1019+
PageSize hugePageSize)
10281020
: Container(object,
10291021
config,
10301022
memSegment.addr,
10311023
memSegment.size,
10321024
compressor,
1033-
std::move(hm)) {}
1025+
std::move(hm),
1026+
hugePageSize) {}
10341027

10351028
template <typename T,
10361029
typename ChainedHashTable::Hook<T> T::* HookPtr,
@@ -1041,7 +1034,8 @@ ChainedHashTable::Container<T, HookPtr, LockT>::Container(
10411034
void* memStart,
10421035
size_t nBytes,
10431036
const PtrCompressor& compressor,
1044-
HandleMaker hm)
1037+
HandleMaker hm,
1038+
PageSize hugePageSize)
10451039
: config_{config},
10461040
handleMaker_(std::move(hm)),
10471041
ht_{config_.getNumBuckets(), memStart, compressor, config_.getHasher(),
@@ -1058,8 +1052,7 @@ ChainedHashTable::Container<T, HookPtr, LockT>::Container(
10581052

10591053
// Take page alignment into consideration when comparing the size of the
10601054
// shared memory and the size of the hashtable.
1061-
if (nBytes !=
1062-
util::getAlignedSize(ht_.size(), config_.getPageSize().getPageSize())) {
1055+
if (nBytes != util::getAlignedSize(ht_.size(), hugePageSize.getPageSize())) {
10631056
throw std::invalid_argument(
10641057
fmt::format("Hashtable size not compatible. old = {}, new = {}",
10651058
ht_.size(),

cachelib/persistence/PersistenceManager.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ class PersistenceStreamWriter {
9393
* - isNvmCacheEncryption
9494
* - isNvmCacheTruncateAllocSize
9595
* - accessConfig.numBuckets
96-
* - accessConfig.pageSize
9796
* - chainedItemAccessConfig.numBuckets
9897
* - nvmConfig.navyConfig.FileName
9998
* - nvmConfig.navyConfig.RaidPaths
@@ -117,9 +116,8 @@ class PersistenceManager {
117116
// requires plumbing page sizes: saveShm() would need to attach the source
118117
// at its real page size, and restoreCache() would need to create segments
119118
// at the destination's configured page size (and hugetlbfs mount).
120-
CACHELIB_CHECK_THROW(config.accessConfig.getPageSize().getPageSize() ==
121-
PageSize::systemPageSize(),
122-
"Only default PageSize is supported to persist");
119+
CACHELIB_CHECK_THROW(!config.hugePageSize.isHugePage(),
120+
"Huge pages are not supported to persist");
123121

124122
if (config.nvmConfig.has_value()) {
125123
const auto& navyConfig = config.nvmConfig->navyConfig;

cachelib/persistence/tests/PersistenceManagerTest.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,11 @@ TEST_F(PersistenceManagerTest, testConfigChange) {
8686
{
8787
// change cache config
8888
auto config = cache_.config_;
89-
// non-default page size is not allowed
90-
config.setAccessConfig(
91-
Cache::AccessConfig{10, 10, PageSize(PageSize::kHugePageSize2MB)});
89+
// huge pages are not allowed
90+
config.enableHugePages(PageSize(PageSize::kHugePageSize2MB));
9291
ASSERT_THROW_WITH_MSG(PersistenceManager manager(config),
9392
std::invalid_argument,
94-
"Only default PageSize is supported to persist");
93+
"Huge pages are not supported to persist");
9594
}
9695
}
9796

website/docs/Cache_Library_User_Guides/Cross_Host_Cache_Persistence.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ shared infra, but this is not a persistent storage that serves like databases.
1616
1. Create a persistence cache with [Cache Persistence](Cache_persistence) user guide.
1717
2. Shutdown cache successfully.
1818
3. Only POSIX is supported.
19-
4. Only default PageSize is supported.
19+
4. Huge pages are not supported.
2020

2121
## Implement Stream Reader and Writer APIs
2222

@@ -152,11 +152,10 @@ incompatible.
152152
5. isNvmCacheEncryption
153153
6. isNvmCacheTruncateAllocSize
154154
7. accessConfig.numBuckets
155-
8. accessConfig.pageSize
156-
9. chainedItemAccessConfig.numBuckets
157-
10. nvmConfig.navyConfig.getFileName
158-
11. nvmConfig.navyConfig.getRaidPaths
159-
12. nvmConfig.navyConfig.getFileSize
155+
8. chainedItemAccessConfig.numBuckets
156+
9. nvmConfig.navyConfig.getFileName
157+
10. nvmConfig.navyConfig.getRaidPaths
158+
11. nvmConfig.navyConfig.getFileSize
160159
161160
## Simple Example
162161

0 commit comments

Comments
 (0)