Skip to content

Commit

Permalink
P1: LRU-K Replacer
Browse files Browse the repository at this point in the history
  • Loading branch information
YinshiSanchez committed Mar 2, 2024
1 parent b964274 commit 95afc60
Show file tree
Hide file tree
Showing 3 changed files with 311 additions and 19 deletions.
220 changes: 214 additions & 6 deletions src/buffer/lru_k_replacer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,228 @@
//===----------------------------------------------------------------------===//

#include "buffer/lru_k_replacer.h"
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <utility>
#include <vector>
#include "common/config.h"
#include "common/exception.h"
#include "common/macros.h"

#define QUEUE_SIZE(START, END, K) (((END) - (START) + ((K) + 1)) % ((K) + 1))
#define QUEUE_POS(POS, K) ((POS) % ((K) + 1))

namespace bustub {

LRUKReplacer::LRUKReplacer(size_t num_frames, size_t k) : replacer_size_(num_frames), k_(k) {}
constexpr size_t INF = SIZE_MAX >> 1;

// LRUKNode

LRUKNode::LRUKNode(const size_t k) : history_(k + 1), k_(k) { k_distance_ = INF; };

auto LRUKNode::operator<(const LRUKNode &rhs) const -> bool { return false; }

void LRUKNode::Init(const size_t k) {
start_ = 0;
end_ = 0;
k_ = k;
k_distance_ = SIZE_MAX;
is_evictable_ = false;
}

void LRUKNode::Access(const size_t timestamp) {
if (QUEUE_SIZE(start_, end_, k_) == k_) {
start_ = QUEUE_POS(start_ + 1, k_);
}
history_[end_++] = timestamp;
end_ = QUEUE_POS(end_, k_);
if (QUEUE_SIZE(start_, end_, k_) != k_) {
k_distance_ = SIZE_MAX - timestamp;
} else {
k_distance_ = INF - history_[start_];
}
}

auto LRUKNode::Valid() const -> bool { return k_ != INF; }

void LRUKNode::SetInvalid() { k_ = INF; }

auto LRUKNode::KDistance() -> size_t { return k_distance_; }

// NodeHeap

NodeHeap::NodeHeap(const size_t node_num, std::vector<LRUKNode> &node_ref)
: node_ref_(node_ref), frame_heap_(node_num + 1), frame_pos_map_(node_num, INF) {
frame_heap_[0] = 0;
}

void NodeHeap::Push(const frame_id_t frame_id) {
if (frame_pos_map_[frame_id] == INF) { // new entry
size_t cur_pos = ++frame_heap_[0];
auto parent_pos = cur_pos >> 1;
while (parent_pos > 0) {
if (node_ref_[frame_id].KDistance() > node_ref_[frame_heap_[parent_pos]].KDistance()) {
frame_heap_[cur_pos] = frame_heap_[parent_pos];
frame_pos_map_[frame_heap_[parent_pos]] = cur_pos;
cur_pos = parent_pos;
parent_pos >>= 1;
} else {
break;
}
}
frame_heap_[cur_pos] = frame_id;
frame_pos_map_[frame_id] = cur_pos;
} else { // old entry
Amend(frame_pos_map_[frame_id]);
}
}

auto NodeHeap::Pop() -> frame_id_t {
// BFS on heap
std::vector<size_t> queue((frame_heap_[0] + 1) >> 1); // posistion of nodes in heap
size_t queue_size(1);
size_t max_distance(0);
int32_t max_frame(-1);

queue[0] = 1;

while (queue_size > 0) {
std::vector<size_t> temp_queue((frame_heap_[0] + 1) >> 1);
size_t temp_size(0);

for (size_t i = 0; i < queue_size; ++i) {
auto cur_frame = frame_heap_[queue[i]];
if (node_ref_[cur_frame].Evictable()) {
if (node_ref_[cur_frame].KDistance() > max_distance) {
max_frame = cur_frame;
max_distance = node_ref_[cur_frame].KDistance();
}
} else { // cur_frame is non-evictable
auto next_pos = queue[i] << 1;
if (next_pos <= static_cast<uint64_t>(frame_heap_[0])) { // left child

if (node_ref_[frame_heap_[next_pos]].KDistance() > max_distance) {
temp_queue[temp_size++] = next_pos;
++next_pos;
}
if (next_pos <= static_cast<uint64_t>(frame_heap_[0]) &&
node_ref_[frame_heap_[next_pos]].KDistance() > max_distance) { // left child
temp_queue[temp_size++] = next_pos;
++next_pos;
}
}
}
}
queue_size = temp_size;
queue = std::move(temp_queue);
}

if (max_frame > 0) {
// swap min frame and evictable max frame
auto min_frame = frame_heap_[frame_heap_[0]];
frame_heap_[frame_pos_map_[max_frame]] = min_frame;
frame_pos_map_[min_frame] = frame_pos_map_[max_frame];
--frame_heap_[0];

// correct node posistion
Amend(frame_pos_map_[max_frame]);
frame_pos_map_[max_frame] = INF;
}

return max_frame;
}

void NodeHeap::Remove(const frame_id_t frame_id) {
size_t pos = frame_pos_map_[frame_id];
BUSTUB_ASSERT(pos != INF, "try to remove an invalid frame");

frame_pos_map_[frame_id] = INF;

// move the lastest frame to cur pos
auto cur_frame = frame_heap_[frame_heap_[0]];
frame_pos_map_[cur_frame] = pos;

// correct heap tree;
Amend(pos);
}

void NodeHeap::Amend(size_t pos) {
int32_t cur_pos = pos;
int32_t next_pos = pos << 1;
while (next_pos <= frame_heap_[0]) {
if (next_pos < frame_heap_[0] &&
node_ref_[frame_heap_[next_pos]].KDistance() < node_ref_[frame_heap_[next_pos + 1]].KDistance()) {
++next_pos;
}
auto cur_frame = frame_heap_[cur_pos];
auto next_frame = frame_heap_[next_pos];
if (node_ref_[cur_frame].KDistance() > node_ref_[next_frame].KDistance()) {
break;
}

std::swap(frame_pos_map_[cur_frame], frame_pos_map_[next_frame]);
std::swap(frame_heap_[cur_pos], frame_heap_[next_pos]);
cur_pos = next_pos;
next_pos <<= 1;
}
}

// LRUKReplacer

LRUKReplacer::LRUKReplacer(const size_t num_frames, const size_t k)
: node_store_(num_frames, LRUKNode{k}), node_heap_(num_frames, node_store_), replacer_size_(num_frames), k_(k) {}

auto LRUKReplacer::Evict(frame_id_t *frame_id) -> bool {
std::lock_guard lg(latch_);
if (curr_size_ > 0) {
auto temp = node_heap_.Pop();
if (temp != -1) {
node_store_[temp].SetInvalid();
*frame_id = temp;
--curr_size_;
return true;
}
}
return false;
}

void LRUKReplacer::RecordAccess(const frame_id_t frame_id, [[maybe_unused]] AccessType access_type) {
BUSTUB_ASSERT(static_cast<size_t>(frame_id) < replacer_size_, "invalid frame id!");

auto LRUKReplacer::Evict(frame_id_t *frame_id) -> bool { return false; }
std::lock_guard lg(latch_);
auto &node = node_store_[frame_id];
if (!node.Valid()) {
node.Init(k_);
}
node.Access(current_timestamp_++);
node_heap_.Push(frame_id);
}

void LRUKReplacer::RecordAccess(frame_id_t frame_id, [[maybe_unused]] AccessType access_type) {}
void LRUKReplacer::SetEvictable(const frame_id_t frame_id, bool set_evictable) {
BUSTUB_ASSERT(static_cast<size_t>(frame_id) < replacer_size_, "invalid frame id!");
std::lock_guard lg(latch_);

void LRUKReplacer::SetEvictable(frame_id_t frame_id, bool set_evictable) {}
if (set_evictable != node_store_[frame_id].Evictable()) {
node_store_[frame_id].SetEvictable(set_evictable);
if (set_evictable) {
++curr_size_;
} else {
--curr_size_;
}
}
}

void LRUKReplacer::Remove(frame_id_t frame_id) {}
void LRUKReplacer::Remove(const frame_id_t frame_id) {
BUSTUB_ASSERT(static_cast<size_t>(frame_id) < replacer_size_, "invalid frame id!");
BUSTUB_ASSERT(node_store_[frame_id].Evictable(), "try to evict a non-evictable frame");
if (node_store_[frame_id].Valid()) {
node_store_[frame_id].SetInvalid();
node_heap_.Remove(frame_id);
--curr_size_;
}
}

auto LRUKReplacer::Size() -> size_t { return 0; }
auto LRUKReplacer::Size() -> size_t { return curr_size_; }

} // namespace bustub
108 changes: 96 additions & 12 deletions src/include/buffer/lru_k_replacer.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@

#pragma once

#include <cstddef>
#include <limits>
#include <list>
#include <mutex> // NOLINT
#include <unordered_map>
#include <vector>

#include "common/config.h"
Expand All @@ -25,15 +24,99 @@ namespace bustub {

enum class AccessType { Unknown = 0, Lookup, Scan, Index };

// #define WORD_OFFSET(i) ((i) >> 6)
// #define BIT_OFFSET(i) ((i) & 0x3f)
// struct BitMap {
// size_t size_;
// uint64_t *data_;

// BitMap() : size_(0), data_(nullptr) {}
// explicit BitMap(size_t size) : size_(size) {
// data_ = new uint64_t[WORD_OFFSET(size) + 1];
// Clear();
// }
// ~BitMap() { delete[] data_; }
// void Clear() {
// size_t bm_size = WORD_OFFSET(size_);
// for (size_t i = 0; i <= bm_size; i++) {
// data_[i] = 0;
// }
// }
// void Fill() {
// size_t bm_size = WORD_OFFSET(size_);
// for (size_t i = 0; i < bm_size; i++) {
// data_[i] = 0xffffffffffffffff;
// }
// data_[bm_size] = 0;
// for (size_t i = (bm_size << 6); i < size_; i++) {
// data_[bm_size] |= 1UL << BIT_OFFSET(i);
// }
// }

// auto GetBit(size_t i) -> uint64_t { return data_[WORD_OFFSET(i)] & (1UL << BIT_OFFSET(i)); }

// void SetBit(size_t i) { data_[WORD_OFFSET(i)] |= (1UL << BIT_OFFSET(i)); }
// };

class LRUKNode {
public:
explicit LRUKNode(size_t k = 0);

auto operator<(const LRUKNode &rhs) const -> bool;

void Init(size_t k);

auto Evictable() const -> bool { return is_evictable_; }

void SetEvictable(bool set_evictable) { is_evictable_ = set_evictable; }

auto Valid() const -> bool;

void SetInvalid();

auto KDistance() -> size_t;

void Access(size_t timestamp);

private:
/** History of last seen K timestamps of this page. Least recent timestamp stored in front. */
// Remove maybe_unused if you start using them. Feel free to change the member variables as you want.

[[maybe_unused]] std::list<size_t> history_;
[[maybe_unused]] size_t k_;
[[maybe_unused]] frame_id_t fid_;
[[maybe_unused]] bool is_evictable_{false};
std::vector<size_t> history_;
size_t start_{0};
size_t end_{0};
size_t k_;
size_t k_distance_;
bool is_evictable_{false};
};

class NodeHeap {
public:
NodeHeap(size_t node_num, std::vector<LRUKNode> &node_ref);

/**
* if node
* 1. has been in heap: correct position of this node in heap
* 2. is not in heap: create a new entry and push into heap
*/
void Push(frame_id_t frame_id);

// pop an evictable node with the biggest k-distance
auto Pop() -> frame_id_t;

// remove specific frame in heap
void Remove(frame_id_t frame_id);

private:
std::vector<LRUKNode> &node_ref_; // reference to node store
std::vector<frame_id_t> frame_heap_; // frame_heap_[0] is a sentinel element, is the size of heap;
std::vector<size_t> frame_pos_map_; // map frame id to posistion in heap

/**
* @brief correct node posistion
* @param pos posistion of the node needed to be corrected in heap
*/
void Amend(size_t pos);
};

/**
Expand Down Expand Up @@ -150,12 +233,13 @@ class LRUKReplacer {
private:
// TODO(student): implement me! You can replace these member variables as you like.
// Remove maybe_unused if you start using them.
[[maybe_unused]] std::unordered_map<frame_id_t, LRUKNode> node_store_;
[[maybe_unused]] size_t current_timestamp_{0};
[[maybe_unused]] size_t curr_size_{0};
[[maybe_unused]] size_t replacer_size_;
[[maybe_unused]] size_t k_;
[[maybe_unused]] std::mutex latch_;
std::vector<LRUKNode> node_store_;
NodeHeap node_heap_;
size_t current_timestamp_{0};
size_t curr_size_{0};
size_t replacer_size_; // max size
size_t k_;
std::mutex latch_;
};

} // namespace bustub
2 changes: 1 addition & 1 deletion test/buffer/lru_k_replacer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace bustub {

TEST(LRUKReplacerTest, DISABLED_SampleTest) {
TEST(LRUKReplacerTest, SampleTest) {
LRUKReplacer lru_replacer(7, 2);

// Scenario: add six elements to the replacer. We have [1,2,3,4,5]. Frame 6 is non-evictable.
Expand Down

0 comments on commit 95afc60

Please sign in to comment.