mirror of
https://github.com/facebook/rocksdb.git
synced 2025-06-02 18:41:22 +08:00
Calculate table size of FastLRUCache more accurately (#10235)
Summary: Calculate the required size of the hash table in FastLRUCache more accurately. Pull Request resolved: https://github.com/facebook/rocksdb/pull/10235 Test Plan: ``make -j24 check`` Reviewed By: gitbw95 Differential Revision: D37460546 Pulled By: guidotag fbshipit-source-id: 7945128d6f002832f8ed922ef0151919f4350854
This commit is contained in:
parent
a1eb02f089
commit
c6055cba30
24
cache/fast_lru_cache.cc
vendored
24
cache/fast_lru_cache.cc
vendored
@ -192,8 +192,7 @@ LRUCacheShard::LRUCacheShard(size_t capacity, size_t estimated_value_size,
|
|||||||
: capacity_(capacity),
|
: capacity_(capacity),
|
||||||
strict_capacity_limit_(strict_capacity_limit),
|
strict_capacity_limit_(strict_capacity_limit),
|
||||||
table_(
|
table_(
|
||||||
CalcHashBits(capacity, estimated_value_size, metadata_charge_policy) +
|
CalcHashBits(capacity, estimated_value_size, metadata_charge_policy)),
|
||||||
static_cast<uint8_t>(ceil(log2(1.0 / kLoadFactor)))),
|
|
||||||
usage_(0),
|
usage_(0),
|
||||||
lru_usage_(0) {
|
lru_usage_(0) {
|
||||||
set_metadata_charge_policy(metadata_charge_policy);
|
set_metadata_charge_policy(metadata_charge_policy);
|
||||||
@ -295,16 +294,29 @@ void LRUCacheShard::EvictFromLRU(size_t charge,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t LRUCacheShard::CalcHashBits(
|
size_t LRUCacheShard::CalcEstimatedHandleCharge(
|
||||||
size_t capacity, size_t estimated_value_size,
|
size_t estimated_value_size,
|
||||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||||
LRUHandle h;
|
LRUHandle h;
|
||||||
h.CalcTotalCharge(estimated_value_size, metadata_charge_policy);
|
h.CalcTotalCharge(estimated_value_size, metadata_charge_policy);
|
||||||
size_t num_entries = capacity / h.total_charge;
|
return h.total_charge;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t LRUCacheShard::CalcHashBits(
|
||||||
|
size_t capacity, size_t estimated_value_size,
|
||||||
|
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||||
|
size_t handle_charge =
|
||||||
|
CalcEstimatedHandleCharge(estimated_value_size, metadata_charge_policy);
|
||||||
|
size_t num_entries =
|
||||||
|
static_cast<size_t>(capacity / (kLoadFactor * handle_charge));
|
||||||
|
|
||||||
|
// Compute the ceiling of log2(num_entries). If num_entries == 0, return 0.
|
||||||
uint8_t num_hash_bits = 0;
|
uint8_t num_hash_bits = 0;
|
||||||
while (num_entries >>= 1) {
|
size_t num_entries_copy = num_entries;
|
||||||
|
while (num_entries_copy >>= 1) {
|
||||||
++num_hash_bits;
|
++num_hash_bits;
|
||||||
}
|
}
|
||||||
|
num_hash_bits += size_t{1} << num_hash_bits < num_entries ? 1 : 0;
|
||||||
return num_hash_bits;
|
return num_hash_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
cache/fast_lru_cache.h
vendored
10
cache/fast_lru_cache.h
vendored
@ -25,6 +25,9 @@ namespace ROCKSDB_NAMESPACE {
|
|||||||
|
|
||||||
namespace fast_lru_cache {
|
namespace fast_lru_cache {
|
||||||
|
|
||||||
|
// Forward declaration of friend class.
|
||||||
|
class FastLRUCacheTest;
|
||||||
|
|
||||||
// LRU cache implementation using an open-address hash table.
|
// LRU cache implementation using an open-address hash table.
|
||||||
//
|
//
|
||||||
// Every slot in the hash table is an LRUHandle. Because handles can be
|
// Every slot in the hash table is an LRUHandle. Because handles can be
|
||||||
@ -365,6 +368,8 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
friend class LRUCache;
|
friend class LRUCache;
|
||||||
|
friend class FastLRUCacheTest;
|
||||||
|
|
||||||
void LRU_Remove(LRUHandle* e);
|
void LRU_Remove(LRUHandle* e);
|
||||||
void LRU_Insert(LRUHandle* e);
|
void LRU_Insert(LRUHandle* e);
|
||||||
|
|
||||||
@ -374,6 +379,11 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
|||||||
// holding the mutex_.
|
// holding the mutex_.
|
||||||
void EvictFromLRU(size_t charge, autovector<LRUHandle>* deleted);
|
void EvictFromLRU(size_t charge, autovector<LRUHandle>* deleted);
|
||||||
|
|
||||||
|
// Returns the charge of a single handle.
|
||||||
|
static size_t CalcEstimatedHandleCharge(
|
||||||
|
size_t estimated_value_size,
|
||||||
|
CacheMetadataChargePolicy metadata_charge_policy);
|
||||||
|
|
||||||
// Returns the number of bits used to hash an element in the hash
|
// Returns the number of bits used to hash an element in the hash
|
||||||
// table.
|
// table.
|
||||||
static uint8_t CalcHashBits(size_t capacity, size_t estimated_value_size,
|
static uint8_t CalcHashBits(size_t capacity, size_t estimated_value_size,
|
||||||
|
89
cache/lru_cache_test.cc
vendored
89
cache/lru_cache_test.cc
vendored
@ -206,6 +206,7 @@ TEST_F(LRUCacheTest, EntriesWithPriority) {
|
|||||||
ValidateLRUList({"e", "f", "g", "Z", "d"}, 2);
|
ValidateLRUList({"e", "f", "g", "Z", "d"}, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace fast_lru_cache {
|
||||||
// TODO(guido) Consolidate the following FastLRUCache tests with
|
// TODO(guido) Consolidate the following FastLRUCache tests with
|
||||||
// that of LRUCache.
|
// that of LRUCache.
|
||||||
class FastLRUCacheTest : public testing::Test {
|
class FastLRUCacheTest : public testing::Test {
|
||||||
@ -238,6 +239,38 @@ class FastLRUCacheTest : public testing::Test {
|
|||||||
|
|
||||||
Status Insert(char key, size_t len) { return Insert(std::string(len, key)); }
|
Status Insert(char key, size_t len) { return Insert(std::string(len, key)); }
|
||||||
|
|
||||||
|
size_t CalcEstimatedHandleChargeWrapper(
|
||||||
|
size_t estimated_value_size,
|
||||||
|
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||||
|
return fast_lru_cache::LRUCacheShard::CalcEstimatedHandleCharge(
|
||||||
|
estimated_value_size, metadata_charge_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CalcHashBitsWrapper(
|
||||||
|
size_t capacity, size_t estimated_value_size,
|
||||||
|
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||||
|
return fast_lru_cache::LRUCacheShard::CalcHashBits(
|
||||||
|
capacity, estimated_value_size, metadata_charge_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum number of items that a shard can hold.
|
||||||
|
double CalcMaxOccupancy(size_t capacity, size_t estimated_value_size,
|
||||||
|
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||||
|
size_t handle_charge =
|
||||||
|
fast_lru_cache::LRUCacheShard::CalcEstimatedHandleCharge(
|
||||||
|
estimated_value_size, metadata_charge_policy);
|
||||||
|
return capacity / (fast_lru_cache::kLoadFactor * handle_charge);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TableSizeIsAppropriate(uint8_t hash_bits, double max_occupancy) {
|
||||||
|
if (hash_bits == 0) {
|
||||||
|
return max_occupancy <= 1;
|
||||||
|
} else {
|
||||||
|
return (1 << hash_bits >= max_occupancy) &&
|
||||||
|
(1 << (hash_bits - 1) <= max_occupancy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
fast_lru_cache::LRUCacheShard* cache_ = nullptr;
|
fast_lru_cache::LRUCacheShard* cache_ = nullptr;
|
||||||
};
|
};
|
||||||
@ -253,6 +286,62 @@ TEST_F(FastLRUCacheTest, ValidateKeySize) {
|
|||||||
EXPECT_NOK(Insert('f', 0));
|
EXPECT_NOK(Insert('f', 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FastLRUCacheTest, CalcHashBitsTest) {
|
||||||
|
size_t capacity = 1024;
|
||||||
|
size_t estimated_value_size = 1;
|
||||||
|
CacheMetadataChargePolicy metadata_charge_policy = kDontChargeCacheMetadata;
|
||||||
|
double max_occupancy =
|
||||||
|
CalcMaxOccupancy(capacity, estimated_value_size, metadata_charge_policy);
|
||||||
|
uint8_t hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||||
|
metadata_charge_policy);
|
||||||
|
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, max_occupancy));
|
||||||
|
|
||||||
|
capacity = 1024;
|
||||||
|
estimated_value_size = 1;
|
||||||
|
metadata_charge_policy = kFullChargeCacheMetadata;
|
||||||
|
max_occupancy =
|
||||||
|
CalcMaxOccupancy(capacity, estimated_value_size, metadata_charge_policy);
|
||||||
|
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||||
|
metadata_charge_policy);
|
||||||
|
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, max_occupancy));
|
||||||
|
|
||||||
|
// No elements fit in cache.
|
||||||
|
capacity = 0;
|
||||||
|
estimated_value_size = 1;
|
||||||
|
metadata_charge_policy = kDontChargeCacheMetadata;
|
||||||
|
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||||
|
metadata_charge_policy);
|
||||||
|
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, 0 /* max_occupancy */));
|
||||||
|
|
||||||
|
// Set the capacity just below a single handle. Because the load factor is <
|
||||||
|
// 100% at least one handle will fit in the table.
|
||||||
|
estimated_value_size = 1;
|
||||||
|
size_t handle_charge = CalcEstimatedHandleChargeWrapper(
|
||||||
|
8192 /* estimated_value_size */, kDontChargeCacheMetadata);
|
||||||
|
capacity = handle_charge - 1;
|
||||||
|
// The load factor should be bounded away from 100%.
|
||||||
|
assert(static_cast<size_t>(capacity / fast_lru_cache::kLoadFactor) >
|
||||||
|
handle_charge);
|
||||||
|
metadata_charge_policy = kDontChargeCacheMetadata;
|
||||||
|
max_occupancy =
|
||||||
|
CalcMaxOccupancy(capacity, estimated_value_size, metadata_charge_policy);
|
||||||
|
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||||
|
metadata_charge_policy);
|
||||||
|
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, max_occupancy));
|
||||||
|
|
||||||
|
// Large capacity.
|
||||||
|
capacity = 31924172;
|
||||||
|
estimated_value_size = 321;
|
||||||
|
metadata_charge_policy = kFullChargeCacheMetadata;
|
||||||
|
max_occupancy =
|
||||||
|
CalcMaxOccupancy(capacity, estimated_value_size, metadata_charge_policy);
|
||||||
|
hash_bits = CalcHashBitsWrapper(capacity, estimated_value_size,
|
||||||
|
metadata_charge_policy);
|
||||||
|
EXPECT_TRUE(TableSizeIsAppropriate(hash_bits, max_occupancy));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fast_lru_cache
|
||||||
|
|
||||||
class TestSecondaryCache : public SecondaryCache {
|
class TestSecondaryCache : public SecondaryCache {
|
||||||
public:
|
public:
|
||||||
// Specifies what action to take on a lookup for a particular key
|
// Specifies what action to take on a lookup for a particular key
|
||||||
|
Loading…
x
Reference in New Issue
Block a user