Skip to content

Commit dcca025

Browse files
These changes introduce per-tier cache configuration required to implement features discussed here: #102.
These specific changes enable single DRAM tier configs only which are compatible with the current version of cachelib. Configuration API will be expanded as multi-tier changes in other parts of the library are introduced.
1 parent 325a4e2 commit dcca025

File tree

4 files changed

+338
-1
lines changed

4 files changed

+338
-1
lines changed

cachelib/allocator/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ if (BUILD_TESTS)
119119
add_test (tests/AllocatorHitStatsTypeTest.cpp)
120120
add_test (tests/MultiAllocatorTest.cpp)
121121
add_test (tests/NvmAdmissionPolicyTest.cpp)
122+
add_test (tests/CacheAllocatorConfigTest.cpp)
122123
add_test (nvmcache/tests/NvmItemTests.cpp)
123124
add_test (nvmcache/tests/InFlightPutsTest.cpp)
124125
add_test (nvmcache/tests/TombStoneTests.cpp)

cachelib/allocator/CacheAllocatorConfig.h

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "cachelib/allocator/Cache.h"
2828
#include "cachelib/allocator/MM2Q.h"
2929
#include "cachelib/allocator/MemoryMonitor.h"
30+
#include "cachelib/allocator/MemoryTierCacheConfig.h"
3031
#include "cachelib/allocator/NvmAdmissionPolicy.h"
3132
#include "cachelib/allocator/PoolOptimizeStrategy.h"
3233
#include "cachelib/allocator/RebalanceStrategy.h"
@@ -50,6 +51,7 @@ class CacheAllocatorConfig {
5051
using NvmCacheDeviceEncryptor = typename CacheT::NvmCacheT::DeviceEncryptor;
5152
using MoveCb = typename CacheT::MoveCb;
5253
using NvmCacheConfig = typename CacheT::NvmCacheT::Config;
54+
using MemoryTierConfigs = std::vector<MemoryTierCacheConfig>;
5355
using Key = typename CacheT::Key;
5456
using EventTrackerSharedPtr = std::shared_ptr<typename CacheT::EventTracker>;
5557
using Item = typename CacheT::Item;
@@ -199,6 +201,25 @@ class CacheAllocatorConfig {
199201
// cachePersistence()
200202
CacheAllocatorConfig& usePosixForShm();
201203

204+
// Configures cache memory tiers. Accepts vector of MemoryTierCacheConfig.
205+
// Each vector element describes configuration for a single memory cache tier.
206+
// Tiers can be set up as ratios of total cache size or have their sizes
207+
// explicitly specified. If ratios are used, then total cache size must be set
208+
// before this method is called; if sizes are explicitly specified, then
209+
// their sum sizes must match the total cache size if it's previously set; if
210+
// the total size has not been set then this method will set it as a sum of
211+
// all tier sizes
212+
CacheAllocatorConfig& configureMemoryTiers(const MemoryTierConfigs& configs);
213+
214+
// Sets total cache size and configures cache memory tiers. This method
215+
// can be used when configuring cache memory tears whose sizes are specified
216+
// via ratios of total cache size.
217+
CacheAllocatorConfig& configureMemoryTiers(size_t totalCacheSize,
218+
const MemoryTierConfigs& configs);
219+
220+
// Return reference to MemoryTierCacheConfigs.
221+
const MemoryTierConfigs& getMemoryTierConfigs();
222+
202223
// This turns on a background worker that periodically scans through the
203224
// access container and look for expired items and remove them.
204225
CacheAllocatorConfig& enableItemReaperInBackground(
@@ -575,6 +596,12 @@ class CacheAllocatorConfig {
575596
friend CacheT;
576597

577598
private:
599+
CacheAllocatorConfig& setCacheSizeImpl(size_t _size);
600+
601+
// Configuration for memory tiers.
602+
MemoryTierConfigs memoryTierConfigs{
603+
{MemoryTierCacheConfig::fromShm().setRatio(1)}};
604+
578605
void mergeWithPrefix(
579606
std::map<std::string, std::string>& configMap,
580607
const std::map<std::string, std::string>& configMapToMerge,
@@ -597,7 +624,8 @@ CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::setCacheName(
597624
}
598625

599626
template <typename T>
600-
CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::setCacheSize(size_t _size) {
627+
CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::setCacheSizeImpl(
628+
size_t _size) {
601629
size = _size;
602630
constexpr size_t maxCacheSizeWithCoredump = 64'424'509'440; // 60GB
603631
if (size <= maxCacheSizeWithCoredump) {
@@ -606,6 +634,18 @@ CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::setCacheSize(size_t _size) {
606634
return *this;
607635
}
608636

637+
template <typename T>
638+
CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::setCacheSize(size_t _size) {
639+
if (memoryTierConfigs.size() == 1) {
640+
memoryTierConfigs[0].setSize(size);
641+
} else {
642+
throw std::invalid_argument(
643+
"Cannot set cache size after configuring memory tiers.");
644+
}
645+
646+
return setCacheSizeImpl(_size);
647+
}
648+
609649
template <typename T>
610650
CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::setDefaultAllocSizes(
611651
std::set<uint32_t> allocSizes) {
@@ -844,6 +884,95 @@ CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::enableItemReaperInBackground(
844884
return *this;
845885
}
846886

887+
template <typename T>
888+
CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::configureMemoryTiers(
889+
size_t totalCacheSize, const MemoryTierConfigs& config) {
890+
setCacheSizeImpl(totalCacheSize);
891+
if (!getCacheSize()) {
892+
throw std::invalid_argument("Total cache size must be greater than 0.");
893+
}
894+
895+
return configureMemoryTiers(config);
896+
}
897+
898+
template <typename T>
899+
CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::configureMemoryTiers(
900+
const MemoryTierConfigs& config) {
901+
if (config.size() != 1) {
902+
throw std::invalid_argument(
903+
"Only single memory tier is currently supported.");
904+
}
905+
memoryTierConfigs = config;
906+
size_t sumRatios = 0;
907+
size_t sumSizes = 0;
908+
909+
for (const auto& tierConfig : memoryTierConfigs) {
910+
auto tierSize = tierConfig.getSize();
911+
auto tierRatio = tierConfig.getRatio();
912+
if ((sumRatios && tierSize) || (sumSizes && tierRatio)) {
913+
throw std::invalid_argument(
914+
"For each memory tier either size or ratio must be set.");
915+
}
916+
sumRatios += tierRatio;
917+
sumSizes += tierSize;
918+
}
919+
920+
if (!getCacheSize()) {
921+
if (sumRatios) {
922+
throw std::invalid_argument(
923+
"Total cache size must be specified when size ratios are "
924+
"used to specify memory tier sizes.");
925+
} else if (sumSizes) {
926+
setCacheSizeImpl(sumSizes);
927+
}
928+
}
929+
930+
if (sumRatios) {
931+
if (!getCacheSize()) {
932+
throw std::invalid_argument(
933+
"Total cache size must be specified when size ratios are "
934+
"used to specify memory tier sizes.");
935+
} else {
936+
if (getCacheSize() < sumRatios) {
937+
throw std::invalid_argument(
938+
"Sum of all tier size ratios is greater than total cache size.");
939+
}
940+
// Convert ratios to sizes
941+
sumSizes = 0;
942+
size_t partitionSize = getCacheSize() / sumRatios;
943+
for (auto& tierConfig : memoryTierConfigs) {
944+
tierConfig.setSize(partitionSize * tierConfig.getRatio());
945+
sumSizes += tierConfig.getSize();
946+
}
947+
if (getCacheSize() != sumSizes) {
948+
// Adjust capacity of the last tier to account for rounding error
949+
memoryTierConfigs.back().setSize(memoryTierConfigs.back().getSize() +
950+
(getCacheSize() - sumSizes));
951+
sumSizes = getCacheSize();
952+
}
953+
}
954+
} else if (sumSizes) {
955+
if (sumSizes != getCacheSize()) {
956+
throw std::invalid_argument(
957+
"Sum of tier sizes doesn't match total cache size. "
958+
"Setting of cache total size is not required when per-tier "
959+
"sizes are specified - it is calculated as sum of tier sizes.");
960+
}
961+
} else {
962+
throw std::invalid_argument(
963+
"Either sum of all memory tiers sizes or sum of all ratios "
964+
"must be greater than 0.");
965+
}
966+
967+
return *this;
968+
}
969+
970+
template <typename T>
971+
const typename CacheAllocatorConfig<T>::MemoryTierConfigs&
972+
CacheAllocatorConfig<T>::getMemoryTierConfigs() {
973+
return memoryTierConfigs;
974+
}
975+
847976
template <typename T>
848977
CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::disableCacheEviction() {
849978
disableEviction = true;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) Intel Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <string>
20+
21+
#include "cachelib/shm/ShmCommon.h"
22+
23+
namespace facebook {
24+
namespace cachelib {
25+
class MemoryTierCacheConfig {
26+
public:
27+
// Creates instance of MemoryTierCacheConfig for Posix/SysV Shared memory.
28+
static MemoryTierCacheConfig fromShm() {
29+
// TODO: expand this method when adding support for file-mapped memory
30+
return MemoryTierCacheConfig();
31+
}
32+
33+
// Specifies size of this memory tier. Sizes of tiers must be specified by
34+
// either setting size explicitly or using ratio, mixing of the two is not
35+
// supported.
36+
MemoryTierCacheConfig& setSize(size_t _size) {
37+
size = _size;
38+
return *this;
39+
}
40+
41+
// Specifies ratio of this memory tier to other tiers. Absolute size
42+
// of each tier can be calculated as:
43+
// cacheSize * tierRatio / Sum of ratios for all tiers; the difference
44+
// between total cache size and sum of all tier sizes resulted from
45+
// round off error is accounted for when calculating the last tier's
46+
// size to make the totals equal.
47+
MemoryTierCacheConfig& setRatio(double _ratio) {
48+
ratio = _ratio;
49+
return *this;
50+
}
51+
52+
size_t getRatio() const noexcept { return ratio; }
53+
54+
size_t getSize() const noexcept { return size; }
55+
56+
// Size of this memory tiers
57+
size_t size{0};
58+
59+
// Ratio is a number of parts of the total cache size to be allocated for this
60+
// tier. E.g. if X is a total cache size, Yi are ratios specified for memory
61+
// tiers, and Y is the sum of all Yi, then size of the i-th tier
62+
// Xi = (X / Y) * Yi. For examle, to configure 2-tier cache where each
63+
// tier is a half of the total cache size, set both tiers' ratios to 1.
64+
size_t ratio{0};
65+
66+
private:
67+
// TODO: introduce a container for tier settings when adding support for
68+
// file-mapped memory
69+
MemoryTierCacheConfig() = default;
70+
};
71+
} // namespace cachelib
72+
} // namespace facebook
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "cachelib/allocator/CacheAllocatorConfig.h"
18+
#include "cachelib/allocator/MemoryTierCacheConfig.h"
19+
#include "cachelib/allocator/tests/TestBase.h"
20+
21+
namespace facebook {
22+
namespace cachelib {
23+
24+
namespace tests {
25+
26+
using AllocatorT = LruAllocator;
27+
using MemoryTierConfigs = CacheAllocatorConfig<AllocatorT>::MemoryTierConfigs;
28+
29+
// TODO: Increase this constant when multi-tier configs are fully enabled
30+
size_t maxNumTiers = 1;
31+
size_t defaultTotalSize = 2 * Slab::kSize * maxNumTiers;
32+
33+
class CacheAllocatorConfigTest : public testing::Test {};
34+
35+
MemoryTierConfigs generateTierConfigs(size_t numTiers,
36+
MemoryTierCacheConfig& config) {
37+
return MemoryTierConfigs(numTiers, config);
38+
}
39+
40+
size_t sumTierSizes(const MemoryTierConfigs& configs) {
41+
size_t sumSizes = 0;
42+
for (const auto& config : configs) {
43+
sumSizes += config.getSize();
44+
}
45+
return sumSizes;
46+
}
47+
48+
TEST_F(CacheAllocatorConfigTest, MultipleTier0Config) {
49+
AllocatorT::Config config1, config2;
50+
// Throws if vector of tier configs is emptry
51+
EXPECT_THROW(config1.configureMemoryTiers(MemoryTierConfigs()),
52+
std::invalid_argument);
53+
EXPECT_THROW(
54+
config2.configureMemoryTiers(defaultTotalSize, MemoryTierConfigs()),
55+
std::invalid_argument);
56+
}
57+
58+
TEST_F(CacheAllocatorConfigTest, MultipleTier1Config) {
59+
AllocatorT::Config config1, config2, config3;
60+
// Accepts single-tier configuration
61+
config1.setCacheSize(defaultTotalSize)
62+
.configureMemoryTiers(
63+
{MemoryTierCacheConfig::fromShm().setSize(defaultTotalSize)});
64+
config2.setCacheSize(0).configureMemoryTiers(
65+
{MemoryTierCacheConfig::fromShm().setSize(defaultTotalSize)});
66+
config3.configureMemoryTiers(defaultTotalSize,
67+
{MemoryTierCacheConfig::fromShm().setRatio(1)});
68+
}
69+
70+
TEST_F(CacheAllocatorConfigTest, MultipleTier2Config) {
71+
AllocatorT::Config config1, config2;
72+
// Throws if vector of tier configs > 1
73+
EXPECT_THROW(config1.configureMemoryTiers(generateTierConfigs(
74+
maxNumTiers + 1,
75+
MemoryTierCacheConfig::fromShm().setSize(defaultTotalSize))),
76+
std::invalid_argument);
77+
EXPECT_THROW(
78+
config2.configureMemoryTiers(
79+
defaultTotalSize,
80+
generateTierConfigs(maxNumTiers + 1,
81+
MemoryTierCacheConfig::fromShm().setRatio(1))),
82+
std::invalid_argument);
83+
}
84+
85+
TEST_F(CacheAllocatorConfigTest, TotalCacheSizeSet0) {
86+
AllocatorT::Config config1, config2;
87+
// If total cache size is set to zero and each individual tier specifies its
88+
// size then total cache size is set to the sum of tier sizes
89+
MemoryTierConfigs tierConfigs = {
90+
MemoryTierCacheConfig::fromShm().setSize(defaultTotalSize)};
91+
config1.setCacheSize(0).configureMemoryTiers(tierConfigs);
92+
ASSERT_EQ(config1.getCacheSize(),
93+
sumTierSizes(config1.getMemoryTierConfigs()));
94+
95+
// Throws if total cache size is set to 0 and ratios are used to specify sizes
96+
EXPECT_THROW(config2.configureMemoryTiers(
97+
0, {MemoryTierCacheConfig::fromShm().setRatio(1)}),
98+
std::invalid_argument);
99+
}
100+
101+
TEST_F(CacheAllocatorConfigTest, TotalCacheSizeLessThanRatios) {
102+
AllocatorT::Config config1, config2;
103+
// Throws if total cache size is set to 0
104+
EXPECT_THROW(
105+
config1.setCacheSize(defaultTotalSize)
106+
.configureMemoryTiers({MemoryTierCacheConfig::fromShm().setRatio(
107+
defaultTotalSize + 1)}),
108+
std::invalid_argument);
109+
EXPECT_THROW(
110+
config2.configureMemoryTiers(
111+
defaultTotalSize,
112+
{MemoryTierCacheConfig::fromShm().setRatio(defaultTotalSize + 1)}),
113+
std::invalid_argument);
114+
}
115+
116+
TEST_F(CacheAllocatorConfigTest, SumTierSizes) {
117+
AllocatorT::Config config1, config2;
118+
MemoryTierConfigs sizeConfigs = generateTierConfigs(
119+
maxNumTiers,
120+
MemoryTierCacheConfig::fromShm().setSize(defaultTotalSize / maxNumTiers));
121+
MemoryTierConfigs ratioConfigs = generateTierConfigs(
122+
maxNumTiers, MemoryTierCacheConfig::fromShm().setRatio(1));
123+
124+
config1.setCacheSize(defaultTotalSize).configureMemoryTiers(sizeConfigs);
125+
ASSERT_EQ(config1.getCacheSize(),
126+
sumTierSizes(config1.getMemoryTierConfigs()));
127+
128+
config2.setCacheSize(defaultTotalSize).configureMemoryTiers(ratioConfigs);
129+
ASSERT_EQ(config2.getCacheSize(),
130+
sumTierSizes(config2.getMemoryTierConfigs()));
131+
}
132+
133+
} // namespace tests
134+
} // namespace cachelib
135+
} // namespace facebook

0 commit comments

Comments
 (0)