Loading

CMU 15-445(Fall 2023) Project1 Buffer Pool Manager 个人笔记

PROJECT #1 - BUFFER POOL

总结#

Project1没有做针对排行榜的优化。Project1基础部分不算难,但Bustub中只提供了简单的测试样例,通过了本地的测试后提交到gradescope可能拿不了满分,需要根据gradescope的提示信息再进行更正。

建议在函数中多打印一些调试信息,这些调试信息在gradescope的报告中是能看到的。

例如在函数入口处打印函数调用信息和参数信息,在实现Task1时,把当前replacer管理的frame信息打印出来。

auto LRUKReplacer::Evict(frame_id_t *frame_id) -> bool {
  std::cout << "Before evict: ";
  PrintNodeStore();
  // ...
  std::cout << "After evict: ";
  PrintNodeStore();
}

auto BufferPoolManager::FetchPage(page_id_t page_id, [[maybe_unused]] AccessType access_type) -> Page * {
  std::cout << "FetchPage: " << page_id << "\n";
  // ...
}

其它经验#

头文件中包含了要实现的方法的详细注释,结合这些注释以及测试文件中的代码,基本就能够很好的理解方法要实现什么功能了。

Task #1 - LRU-K Replacement Policy#

Evict方法的补充测试函数#

这里补充一个Evict方法的测试函数,可以直接放到测试文件中用,直接把它粘贴到lru_k_replacer_test.cpp中即可。

TEST(LRUKReplacerTest, SecondSimpleTest) {
  LRUKReplacer lru_replacer(7, 3);

  // [4, 3, 2, 1]
  lru_replacer.RecordAccess(1);
  lru_replacer.RecordAccess(2);
  // 3的最早访问时间
  lru_replacer.RecordAccess(3);
  // 4的最早访问时间
  lru_replacer.RecordAccess(4);
  // [4, 1, 2, 3]
  lru_replacer.RecordAccess(1);
  lru_replacer.RecordAccess(2);
  lru_replacer.RecordAccess(3);
  // [4, 3, 1, 2]
  lru_replacer.RecordAccess(1);
  lru_replacer.RecordAccess(2);
  // 把1,2,3,4都标记为Evictable
  lru_replacer.SetEvictable(1, true);
  lru_replacer.SetEvictable(2, true);
  lru_replacer.SetEvictable(3, true);
  lru_replacer.SetEvictable(4, true);

  // 此时4被访问过1次,3被访问过2次, 4和3的访问次数都小于k
  // 但是3的第一次访问时间早于4, 所以被淘汰的应该是3,而不是4
  frame_id_t frame_id;
  lru_replacer.Evict(&frame_id);
  ASSERT_EQ(3, frame_id);
}

LRU算法简介#

Task1的要求是实现一个LRU-K算法,在做实验之前要先做一下leetcode的146题(LRU缓存),了解一下LRU算法的实现,做完leetcode中的习题,再结合头文件中的注释以及测试文件中的代码,应该很快就有一个大体的思路了。

下面简单介绍下LRU算法,具体的讲解请参考其他资料。

LRU(Least Recently Used)算法即"最近最少使用算法",通常应用于缓存的管理,当缓存区域已满,且有新的数据需要进入缓存时,使用LRU算法来决定淘汰缓存中的哪部分数据,而LRU算法的思想就是优先淘汰最近一段时间最少使用的数据。

LRU算法的一种实现方式是通过哈希表+双向链表的方式,每次淘汰的都是链表头部的节点,当数据被访问时,就将数据移动到链表的尾部。

图例:

淘汰元素

访问元素

题目链接: 146. LRU 缓存

LRU-K算法简介#

题目中要求的LRU-K算法,相比于LRU算法,多了一个访问次数的属性,题目中还规定了一个evictable属性,来标记一个元素是否是可淘汰的。其实实现方式和LRU算法基本一致,只不过需要多维护一些信息,以及在淘汰算法的策略上有所区别。

Evict方法的分情况讲解#

Case1: 所有元素都不可以驱逐,那么直接返回False

Case2: 存在可以驱逐的元素且这些可驱逐元素中存在访问次数小于k次的元素,那么将从这些访问次数小于k且可驱逐的元素中选择一个元素来驱逐。

驱逐策略: 获取这些元素的第一次访问时间,选择出第一次访问时间最早的那一个来驱逐即可。LRUKReplacer类中提供了一个curr_timestamp_属性,每当我们访问一个元素时,将curr_timestamp_的值记录到其访问历史中,当需要比较时,取出访问历史中的第一个元素进行比较即可。

例: 对于下面这种情况,f1f2访问次数都小于3并且都可驱逐,但是f1的最早访问时间是3,所以这里应该驱逐f1,而不是f2

k = 3;
f4:  不可驱逐 访问历史: <1>
f2:  可驱逐  访问历史: <4>
f1:  可驱逐  访问历史: <3, 4>
f3:  可驱逐  访问历史: <1, 2, 3>

Case3: 存在可以驱逐的元素,并且这些可驱逐的元素的访问次数都大于等于k,那么此时算法就退化成了LRU算法,按照LRU算法的策略来实现即可,对于使用哈希表+双链表的实现方式,遍历链表,淘汰第一个可驱逐的元素即可。

Task3#

task3要实现缓冲池管理器,这个task并不难,这里记录几个我自己遇到的问题。

BufferPoolManager的pages_属性用法#

pages_用于存储缓冲器管理器的所有页面,在构造函数中就申请好了内存,因此,在NewPage方法中,并不需要真正的创建新的页面,而是找到空闲的frame_id,复用pages_[frame_id]这个页面。

BufferPoolManager::BufferPoolManager(size_t pool_size, DiskManager *disk_manager, size_t replacer_k,
                                     LogManager *log_manager)
    : pool_size_(pool_size), disk_scheduler_(std::make_unique<DiskScheduler>(disk_manager)), log_manager_(log_manager) {
  // ...
  pages_ = new Page[pool_size_];
  // ...
}
class BufferPoolManager {
    /** Array of buffer pool pages. */
    Page *pages_;
}

UnpinPage方法#

该方法有一个is_dirty参数,注意不要把该参数直接赋值给页面的is_dirty_属性,这个参数的含义是调用UnpinPage方法的线程是否修改了页面(是否使得页面变脏了)。

如果这个is_dirty参数是false,我们并不应该把对应页面的is_dirty_属性置为false,因为它仅仅代表该线程没有修改页面。

所有,只有is_dirty参数是true时,才把对应页面的is_dirty_属性置为true,其它时候忽略该参数即可。

FetchPage方法#

在实现FetchPage方法时,一开始没有完全理解方法的作用。

Fetch the requested page from the buffer pool这句话意味着,如果要Fetch的页面已经在缓存池中,那就并不需要将硬盘中的内容读至缓存中,而仅仅是更新一下缓冲池中该页面的状态信息即可。

/**
 * @brief Fetch the requested page from the buffer pool. Return nullptr if page_id needs to be fetched from the disk
 */
auto FetchPage(page_id_t page_id, AccessType access_type = AccessType::Unknown) -> Page *;

通过截图#

posted @   焚风  阅读(487)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
主题色彩
点击右上角即可分享
微信分享提示