Skip to content

Commit

Permalink
Optimized LRU cache for repeated requests for same slab.
Browse files Browse the repository at this point in the history
This generalizes the previous optimization for the max_slabs = 1 case.
  • Loading branch information
LTLA committed Apr 6, 2024
1 parent c3ebd9c commit e460e6d
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 18 deletions.
34 changes: 17 additions & 17 deletions include/tatami_chunked/LruSlabCache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ class LruSlabCache {
typedef std::pair<Slab_, Id_> Element;
std::list<Element> cache_data;
std::unordered_map<Id_, typename std::list<Element>::iterator> cache_exists;
size_t max_slabs = 0;
size_t max_slabs;
Id_ last_id = 0;
Slab_* last_slab = NULL;

public:
/**
Expand All @@ -38,6 +40,8 @@ class LruSlabCache {

public:
/**
* This method should only be called if `m > 0` in the constructor.
*
* @tparam Cfunction_ Function to create a new `Slab_` object.
* @tparam Pfunction_ Function to populate a `Slab_` object with the contents of a slab.
*
Expand All @@ -55,23 +59,18 @@ class LruSlabCache {
*/
template<class Cfunction_, class Pfunction_>
const Slab_& find(Id_ id, Cfunction_ create, Pfunction_ populate) {
if (max_slabs == 1) {
// Minor optimization if there's just one slab, in which case we can
// skip the search and the list splice if there's a hit.
if (!cache_data.empty()) {
const auto& solo = cache_data.front();
if (solo.second == id) {
return solo.first;
}
}
} else {
auto it = cache_exists.find(id);
if (it != cache_exists.end()) {
auto chosen = it->second;
cache_data.splice(cache_data.end(), cache_data, chosen); // move to end.
return chosen->first;
}
if (id == last_id && last_slab) {
return *last_slab;
}
last_id = id;

auto it = cache_exists.find(id);
if (it != cache_exists.end()) {
auto chosen = it->second;
cache_data.splice(cache_data.end(), cache_data, chosen); // move to end.
last_slab = &(chosen->first);
return chosen->first;
}

typename std::list<Element>::iterator location;
if (cache_data.size() < max_slabs) {
Expand All @@ -87,6 +86,7 @@ class LruSlabCache {

auto& slab = location->first;
populate(id, slab);
last_slab = &slab;
return slab;
}
};
Expand Down
14 changes: 13 additions & 1 deletion tests/src/LruSlabCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,26 @@ TEST(LruSlabCache, Basic) {
EXPECT_EQ(out.first, 10);
EXPECT_EQ(out.second, 0);

out = cache.find(10, creator, populator); // retrieve from cache with short-circuit optimization.
EXPECT_EQ(out.first, 10);
EXPECT_EQ(out.second, 0);

out = cache.find(30, creator, populator); // new allocation, now we're full.
EXPECT_EQ(out.first, 30);
EXPECT_EQ(out.second, 2);

out = cache.find(30, creator, populator); // retrieve from cache with short-circuit optimization.
EXPECT_EQ(out.first, 30);
EXPECT_EQ(out.second, 2);

out = cache.find(20, creator, populator); // retrieve from cache.
EXPECT_EQ(out.first, 20);
EXPECT_EQ(out.second, 1);

out = cache.find(20, creator, populator); // retrieve from cache with short-circuit optimization.
EXPECT_EQ(out.first, 20);
EXPECT_EQ(out.second, 1);

out = cache.find(10, creator, populator); // retrieve from cache.
EXPECT_EQ(out.first, 10);
EXPECT_EQ(out.second, 0);
Expand Down Expand Up @@ -78,7 +90,7 @@ TEST(LruSlabCache, Solo) {
EXPECT_EQ(out.first, 10);
EXPECT_EQ(out.second, 0);

out = cache.find(10, creator, populator); // retrieve from cache.
out = cache.find(10, creator, populator); // retrieve from cache with short circuit.
EXPECT_EQ(out.first, 10);
EXPECT_EQ(out.second, 0);

Expand Down

0 comments on commit e460e6d

Please sign in to comment.