|
| 1 | +// :bustub-keep-private: |
| 2 | +//===----------------------------------------------------------------------===// |
| 3 | +// |
| 4 | +// BusTub |
| 5 | +// |
| 6 | +// arc_replacer.cpp |
| 7 | +// |
| 8 | +// Identification: src/buffer/arc_replacer.cpp |
| 9 | +// |
| 10 | +// Copyright (c) 2015-2025, Carnegie Mellon University Database Group |
| 11 | +// |
| 12 | +//===----------------------------------------------------------------------===// |
| 13 | + |
| 14 | +#include "buffer/arc_replacer.h" |
| 15 | +#include <optional> |
| 16 | +#include "common/config.h" |
| 17 | + |
| 18 | +namespace bustub { |
| 19 | + |
| 20 | +/** |
| 21 | + * |
| 22 | + * TODO(P1): Add implementation |
| 23 | + * |
| 24 | + * @brief a new ArcReplacer, with lists initialized to be empty and target size to 0 |
| 25 | + * @param num_frames the maximum number of frames the ArcReplacer will be required to cache |
| 26 | + */ |
| 27 | +ArcReplacer::ArcReplacer(size_t num_frames) : replacer_size_(num_frames) {} |
| 28 | + |
| 29 | +/** |
| 30 | + * TODO(P1): Add implementation |
| 31 | + * |
| 32 | + * @brief Performs the Replace operation as described by the writeup |
| 33 | + * that evicts from either mfu_ or mru_ into its corresponding ghost list |
| 34 | + * according to balancing policy. |
| 35 | + * |
| 36 | + * If you wish to refer to the original ARC paper, please note that there are |
| 37 | + * two changes in our implementation: |
| 38 | + * 1. When the size of mru_ equals the target size, we don't check |
| 39 | + * the last access as the paper did when deciding which list to evict from. |
| 40 | + * This is fine since the original decision is stated to be arbitrary. |
| 41 | + * 2. Entries that are not evictable are skipped. If all entries from the desired side |
| 42 | + * (mru_ / mfu_) are pinned, we instead try victimize the other side (mfu_ / mru_), |
| 43 | + * and move it to its corresponding ghost list (mfu_ghost_ / mru_ghost_). |
| 44 | + * |
| 45 | + * @return frame id of the evicted frame, or std::nullopt if cannot evict |
| 46 | + */ |
| 47 | +auto ArcReplacer::Evict() -> std::optional<frame_id_t> { return std::nullopt; } |
| 48 | + |
| 49 | +/** |
| 50 | + * TODO(P1): Add implementation |
| 51 | + * |
| 52 | + * @brief Record access to a frame, adjusting ARC bookkeeping accordingly |
| 53 | + * by bring the accessed page to the front of mfu_ if it exists in any of the lists |
| 54 | + * or the front of mru_ if it does not. |
| 55 | + * |
| 56 | + * Performs the operations EXCEPT REPLACE described in original paper, which is |
| 57 | + * handled by `Evict()`. |
| 58 | + * |
| 59 | + * Consider the following four cases, handle accordingly: |
| 60 | + * 1. Access hits mru_ or mfu_ |
| 61 | + * 2/3. Access hits mru_ghost_ / mfu_ghost_ |
| 62 | + * 4. Access misses all the lists |
| 63 | + * |
| 64 | + * This routine performs all changes to the four lists as preperation |
| 65 | + * for `Evict()` to simply find and evict a victim into ghost lists. |
| 66 | + * |
| 67 | + * Note that frame_id is used as identifier for alive pages and |
| 68 | + * page_id is used as identifier for the ghost pages, since page_id is |
| 69 | + * the unique identifier to the page after it's dead. |
| 70 | + * Using page_id for alive pages should be the same since it's one to one mapping, |
| 71 | + * but using frame_id is slightly more intuitive. |
| 72 | + * |
| 73 | + * @param frame_id id of frame that received a new access. |
| 74 | + * @param page_id id of page that is mapped to the frame. |
| 75 | + * @param access_type type of access that was received. This parameter is only needed for |
| 76 | + * leaderboard tests. |
| 77 | + */ |
| 78 | +void ArcReplacer::RecordAccess(frame_id_t frame_id, page_id_t page_id, [[maybe_unused]] AccessType access_type) {} |
| 79 | + |
| 80 | +/** |
| 81 | + * TODO(P1): Add implementation |
| 82 | + * |
| 83 | + * @brief Toggle whether a frame is evictable or non-evictable. This function also |
| 84 | + * controls replacer's size. Note that size is equal to number of evictable entries. |
| 85 | + * |
| 86 | + * If a frame was previously evictable and is to be set to non-evictable, then size should |
| 87 | + * decrement. If a frame was previously non-evictable and is to be set to evictable, |
| 88 | + * then size should increment. |
| 89 | + * |
| 90 | + * If frame id is invalid, throw an exception or abort the process. |
| 91 | + * |
| 92 | + * For other scenarios, this function should terminate without modifying anything. |
| 93 | + * |
| 94 | + * @param frame_id id of frame whose 'evictable' status will be modified |
| 95 | + * @param set_evictable whether the given frame is evictable or not |
| 96 | + */ |
| 97 | +void ArcReplacer::SetEvictable(frame_id_t frame_id, bool set_evictable) {} |
| 98 | + |
| 99 | +/** |
| 100 | + * TODO(P1): Add implementation |
| 101 | + * |
| 102 | + * @brief Remove an evictable frame from replacer. |
| 103 | + * This function should also decrement replacer's size if removal is successful. |
| 104 | + * |
| 105 | + * Note that this is different from evicting a frame, which always remove the frame |
| 106 | + * decided by the ARC algorithm. |
| 107 | + * |
| 108 | + * If Remove is called on a non-evictable frame, throw an exception or abort the |
| 109 | + * process. |
| 110 | + * |
| 111 | + * If specified frame is not found, directly return from this function. |
| 112 | + * |
| 113 | + * @param frame_id id of frame to be removed |
| 114 | + */ |
| 115 | +void ArcReplacer::Remove(frame_id_t frame_id) {} |
| 116 | + |
| 117 | +/** |
| 118 | + * TODO(P1): Add implementation |
| 119 | + * |
| 120 | + * @brief Return replacer's size, which tracks the number of evictable frames. |
| 121 | + * |
| 122 | + * @return size_t |
| 123 | + */ |
| 124 | +auto ArcReplacer::Size() -> size_t { return 0; } |
| 125 | + |
| 126 | +} // namespace bustub |
0 commit comments