Skip to content

Commit 7311a5d

Browse files
authored
change LRU-K Evict() signature (#731)
* change Evict() signature * update stand-in cpp * update lru k tests for new evict signature
1 parent a08b1c5 commit 7311a5d

File tree

3 files changed

+57
-32
lines changed

3 files changed

+57
-32
lines changed

src/buffer/lru_k_replacer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace bustub {
1717

1818
LRUKReplacer::LRUKReplacer(size_t num_frames, size_t k) : replacer_size_(num_frames), k_(k) {}
1919

20-
auto LRUKReplacer::Evict(frame_id_t *frame_id) -> bool { return false; }
20+
auto LRUKReplacer::Evict() -> std::optional<frame_id_t> { return std::nullopt; }
2121

2222
void LRUKReplacer::RecordAccess(frame_id_t frame_id, [[maybe_unused]] AccessType access_type) {}
2323

src/include/buffer/lru_k_replacer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <limits>
1616
#include <list>
1717
#include <mutex> // NOLINT
18+
#include <optional>
1819
#include <unordered_map>
1920
#include <vector>
2021

@@ -83,7 +84,7 @@ class LRUKReplacer {
8384
* @param[out] frame_id id of frame that is evicted.
8485
* @return true if a frame is evicted successfully, false if no frames can be evicted.
8586
*/
86-
auto Evict(frame_id_t *frame_id) -> bool;
87+
auto Evict() -> std::optional<frame_id_t>;
8788

8889
/**
8990
* TODO(P1): Add implementation

test/buffer/lru_k_replacer_test.cpp

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@
1717
namespace bustub {
1818

1919
TEST(LRUKReplacerTest, DISABLED_SampleTest) {
20+
// Note that comparison with `std::nullopt` always results in `false`, and if the optional type actually does contain
21+
// a value, the comparison will compare the inner value.
22+
// See: https://devblogs.microsoft.com/oldnewthing/20211004-00/?p=105754
23+
std::optional<frame_id_t> frame;
24+
25+
// Initialize the replacer.
2026
LRUKReplacer lru_replacer(7, 2);
2127

22-
// Scenario: add six elements to the replacer. We have [1,2,3,4,5]. Frame 6 is non-evictable.
28+
// Add six frames to the replacer. We now have frames [1, 2, 3, 4, 5]. We set frame 6 as non-evictable.
2329
lru_replacer.RecordAccess(1);
2430
lru_replacer.RecordAccess(2);
2531
lru_replacer.RecordAccess(3);
@@ -32,25 +38,24 @@ TEST(LRUKReplacerTest, DISABLED_SampleTest) {
3238
lru_replacer.SetEvictable(4, true);
3339
lru_replacer.SetEvictable(5, true);
3440
lru_replacer.SetEvictable(6, false);
41+
42+
// The size of the replacer is the number of frames that can be evicted, _not_ the total number of frames entered.
3543
ASSERT_EQ(5, lru_replacer.Size());
3644

37-
// Scenario: Insert access history for frame 1. Now frame 1 has two access histories.
38-
// All other frames have max backward k-dist. The order of eviction is [2,3,4,5,1].
45+
// Record an access for frame 1. Now frame 1 has two accesses total.
3946
lru_replacer.RecordAccess(1);
47+
// All other frames now share the maximum backward k-distance. Since we use timestamps to break ties, where the first
48+
// to be evicted is the frame with the oldest timestamp, the order of eviction should be [2, 3, 4, 5, 1].
4049

41-
// Scenario: Evict three pages from the replacer. Elements with max k-distance should be popped
42-
// first based on LRU.
43-
int value;
44-
lru_replacer.Evict(&value);
45-
ASSERT_EQ(2, value);
46-
lru_replacer.Evict(&value);
47-
ASSERT_EQ(3, value);
48-
lru_replacer.Evict(&value);
49-
ASSERT_EQ(4, value);
50+
// Evict three pages from the replacer.
51+
// To break ties, we use LRU with respect to the oldest timestamp, or the least recently used frame.
52+
ASSERT_EQ(2, lru_replacer.Evict());
53+
ASSERT_EQ(3, lru_replacer.Evict());
54+
ASSERT_EQ(4, lru_replacer.Evict());
5055
ASSERT_EQ(2, lru_replacer.Size());
56+
// Now the replacer has the frames [5, 1].
5157

52-
// Scenario: Now replacer has frames [5,1].
53-
// Insert new frames 3, 4, and update access history for 5. We should end with [3,1,5,4]
58+
// Insert new frames [3, 4], and update the access history for 5. Now, the ordering is [3, 1, 5, 4].
5459
lru_replacer.RecordAccess(3);
5560
lru_replacer.RecordAccess(4);
5661
lru_replacer.RecordAccess(5);
@@ -59,40 +64,59 @@ TEST(LRUKReplacerTest, DISABLED_SampleTest) {
5964
lru_replacer.SetEvictable(4, true);
6065
ASSERT_EQ(4, lru_replacer.Size());
6166

62-
// Scenario: continue looking for victims. We expect 3 to be evicted next.
63-
lru_replacer.Evict(&value);
64-
ASSERT_EQ(3, value);
67+
// Look for a frame to evict. We expect frame 3 to be evicted next.
68+
ASSERT_EQ(3, lru_replacer.Evict());
6569
ASSERT_EQ(3, lru_replacer.Size());
6670

67-
// Set 6 to be evictable. 6 Should be evicted next since it has max backward k-dist.
71+
// Set 6 to be evictable. 6 Should be evicted next since it has the maximum backward k-distance.
6872
lru_replacer.SetEvictable(6, true);
6973
ASSERT_EQ(4, lru_replacer.Size());
70-
lru_replacer.Evict(&value);
71-
ASSERT_EQ(6, value);
74+
ASSERT_EQ(6, lru_replacer.Evict());
7275
ASSERT_EQ(3, lru_replacer.Size());
7376

74-
// Now we have [1,5,4]. Continue looking for victims.
77+
// Mark frame 1 as non-evictable. We now have [5, 4].
7578
lru_replacer.SetEvictable(1, false);
79+
80+
// We expect frame 5 to be evicted next.
7681
ASSERT_EQ(2, lru_replacer.Size());
77-
ASSERT_EQ(true, lru_replacer.Evict(&value));
78-
ASSERT_EQ(5, value);
82+
ASSERT_EQ(5, lru_replacer.Evict());
7983
ASSERT_EQ(1, lru_replacer.Size());
8084

81-
// Update access history for 1. Now we have [4,1]. Next victim is 4.
85+
// Update the access history for frame 1 and make it evictable. Now we have [4, 1].
8286
lru_replacer.RecordAccess(1);
8387
lru_replacer.RecordAccess(1);
8488
lru_replacer.SetEvictable(1, true);
8589
ASSERT_EQ(2, lru_replacer.Size());
86-
ASSERT_EQ(true, lru_replacer.Evict(&value));
87-
ASSERT_EQ(value, 4);
8890

91+
// Evict the last two frames.
92+
ASSERT_EQ(4, lru_replacer.Evict());
93+
ASSERT_EQ(1, lru_replacer.Size());
94+
ASSERT_EQ(1, lru_replacer.Evict());
95+
ASSERT_EQ(0, lru_replacer.Size());
96+
97+
// Insert frame 1 again and mark it as non-evictable.
98+
lru_replacer.RecordAccess(1);
99+
lru_replacer.SetEvictable(1, false);
100+
ASSERT_EQ(0, lru_replacer.Size());
101+
102+
// A failed eviction should not change the size of the replacer.
103+
frame = lru_replacer.Evict();
104+
ASSERT_EQ(false, frame.has_value());
105+
106+
// Mark frame 1 as evictable again and evict it.
107+
lru_replacer.SetEvictable(1, true);
89108
ASSERT_EQ(1, lru_replacer.Size());
90-
lru_replacer.Evict(&value);
91-
ASSERT_EQ(value, 1);
109+
ASSERT_EQ(1, lru_replacer.Evict());
92110
ASSERT_EQ(0, lru_replacer.Size());
93111

94-
// This operation should not modify size
95-
ASSERT_EQ(false, lru_replacer.Evict(&value));
112+
// There is nothing left in the replacer, so make sure this doesn't do something strange.
113+
frame = lru_replacer.Evict();
114+
ASSERT_EQ(false, frame.has_value());
96115
ASSERT_EQ(0, lru_replacer.Size());
116+
117+
// Make sure that setting a non-existent frame as evictable or non-evictable doesn't do something strange.
118+
lru_replacer.SetEvictable(6, false);
119+
lru_replacer.SetEvictable(6, true);
97120
}
121+
98122
} // namespace bustub

0 commit comments

Comments
 (0)