diff --git a/include/tatami_chunked/LruSlabCache.hpp b/include/tatami_chunked/LruSlabCache.hpp index 5edb136..f5ec6cc 100644 --- a/include/tatami_chunked/LruSlabCache.hpp +++ b/include/tatami_chunked/LruSlabCache.hpp @@ -28,7 +28,9 @@ class LruSlabCache { typedef std::pair Element; std::list cache_data; std::unordered_map::iterator> cache_exists; - size_t max_slabs = 0; + size_t max_slabs; + Id_ last_id = 0; + Slab_* last_slab = NULL; public: /** @@ -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. * @@ -55,23 +59,18 @@ class LruSlabCache { */ template 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::iterator location; if (cache_data.size() < max_slabs) { @@ -87,6 +86,7 @@ class LruSlabCache { auto& slab = location->first; populate(id, slab); + last_slab = &slab; return slab; } }; diff --git a/tests/src/LruSlabCache.cpp b/tests/src/LruSlabCache.cpp index e961e0d..1f8f6f9 100644 --- a/tests/src/LruSlabCache.cpp +++ b/tests/src/LruSlabCache.cpp @@ -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); @@ -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);