CMU15445 FALL2022 Project #1 - Buffer Pool

CMU15445 FALL2022 Project #1 - Buffer Pool

Task #1 - Extendible Hash Table

Find(K, V)

  • 通过K利用IndexOf得到索引,取对应桶中Find(K,V)

Insert(K, V)

  • 通过K利用IndexOf得到索引,获取对应桶

  • 如果桶满了

    • 如果桶的DepthGlobalDepthInternal相等了

      • GlobalDepthInternal++

      • 将dir_增加一倍,并将第i+原capacitydir_指向第i的dir_

      • 新建两个深度为原桶Depth+1的桶,按照mask将原桶中的内容插入新建的桶

        • auto mask = 1 << target_bucket->GetDepth();
          for (const auto &item : target_bucket->GetItems()) {
              size_t hash_key = std::hash<K>()(item.first);
              if ((hash_key & mask) == 0U) {
                  bucket0->Insert(item.first, item.second);
              } else {
                  bucket1->Insert(item.first, item.second);
              }
          }
          
      • 遍历dir_,按照mask将指向原来桶的跟新到新建的两个桶

        • auto capacity = dir_.size();
          for (size_t i = 0; i < capacity; i++) {
              if (dir_[i] == target_bucket) {
                  if ((i & mask) == 0U) {
                      dir_[i] = bucket0;
                  } else {
                      dir_[i] = bucket1;
                  }
              }
          }
          
  • 取对应桶中Insert(K,V)

Remove(K)

  • 通过K利用IndexOf得到索引,取对应桶中Remove(K,V)

  • 注意给新增加的target_bucket设置dir_的时候,不一定只有之前的两个,要遍历检测一边

  • for (size_t i = 0; i < capacity; i++) {
          if (dir_[i] == target_bucket) {
            if ((i & mask) == 0U) {
              dir_[i] = bucket0;
            } else {
              dir_[i] = bucket1;
            }
          }
        }
    
    • 将新增的bucke配对时,要涉及到所有之前配对tartge bucketdir_
Task #2 - LRU-K Replacement Policy
[[maybe_unused]] size_t current_timestamp_{0};
size_t curr_size_{0};
size_t replacer_size_;
size_t k_;
std::mutex latch_;
std::unordered_map<frame_id_t, size_t> access_count_;

std::list<frame_id_t> history_list_; // 用于存放可以剔除的帧,即访问次数未达到 k
std::unordered_map<frame_id_t, std::list<frame_id_t>::iterator> history_map_; // 用于存放可以剔除的帧的id和iter映射,方便快速查找

std::list<frame_id_t> cache_list_; // 用于存放不可以被剔除的帧,即访问次数已达到k,头插,尾部取
std::unordered_map<frame_id_t, std::list<frame_id_t>::iterator> cache_map_; // 用于存放不可以被剔除的帧的id和iter映射,方便快速查找

std::unordered_map<frame_id_t, bool> is_evictable_; // 用于是否被evict

Evict(frame_id_t)*

  • 遍历 history_list_:
    • 如果存在且is_evictable_true:
      • access_count_ 设为 0
      • history_list_history_map_ 中删除对应项。
      • curr_size_1,并将 is_evictable_ 设为 false
      • frame_id 设为当前帧,返回 true
  • 尾部向前 遍历 cache_list_:
    • 如果存在且is_evictable_为 true:
      • access_count_ 设为 0
      • cache_list_cache_map_ 中删除对应项。
      • curr_size_1,并将 is_evictable_ 设为 false
      • frame_id 设为当前帧,返回 true
  • 返回 false

RecordAccess(frame_id_t)

  • 首先access_count_++
  • 如果access_count_等于阈值k_
    • history_list_history_map_删除对应项
    • cache_list_将该项插入头部、cache_map_中也插入对应项
  • 如果access_count_大于阈值k_
    • cache_list_cache_map_中删除对应项
    • cache_list_将该项插入头部、cache_map_中也插入对应项
  • 如果access_count_小于阈值k_
    • history_list_将该项插入头部、history_map_中也插入对应项

Remove(frame_id_t)

  • 如果access_count_小于k_

    • history_list_history_map_删除对应项
  • 如果access_count_大于等于k_

    • cache_list_cache_map_删除对应项
  • size_–,access_count_设为0,is_evictable_设为false

SetEvictable(frame_id_t, bool set_evictable)

  • 如果is_evictable_falseset_evictabletrue
    • size++
  • 如果is_evictable_trueset_evictablefalse
    • size
  • is_evictable_设置为set_evictable
Task #3 - Buffer Pool Manager Instance

FetchPgImp(page_id)

  1. 检查页面是否已经在缓冲池中

    • 使用 page_table_->Find(page_id, frame_id) 检查指定的 page_id 是否已经在页面表(page_table_)中

    • 如果找到:

      • 增加该页面的固定计数(pin_count_

      • 记录该页面被访问了(replacer_->RecordAccess(frame_id)

      • 设置该页面为不可逐出状态(replacer_->SetEvictable(frame_id, false)

        返回该页面的指针

  2. 检查是否有空闲页面

    • 遍历缓冲池中的所有页面,检查是否存在固定计数为零的页面(即空闲页面)
    • 如果找到空闲页面,将 is_free_page 设置为 true
  3. 如果没有空闲页面

    • 如果没有空闲页面,直接返回 nullptr,表示无法获取页面
  4. 寻找或逐出页面

    • 如果有空闲页面,首先检查空闲列表是否为空:
      • 如果不为空,从空闲列表中弹出一个帧 ID(frame_id
      • 如果为空,则使用替换策略(replacer_->Evict(&frame_id))逐出一个页面
      • 逐出页面时,如果页面是脏的(IsDirty()),则将其写回磁盘(disk_manager_->WritePage),并清除脏标记
      • 清除逐出页面的内存(pages_[frame_id].ResetMemory()
      • 从页面表中移除逐出的页面
  5. 加载新页面

    • 将新页面插入页面表(page_table_->Insert(page_id, frame_id)
    • 设置新页面的 ID 和固定计数
    • 从磁盘读取页面内容(disk_manager_->ReadPage(page_id, pages_[frame_id].GetData())
    • 记录新页面的访问(replacer_->RecordAccess(frame_id)
    • 设置新页面为不可逐出状态(replacer_->SetEvictable(frame_id, false)
  6. 返回页面指针

    • 返回加载或找到的页面的指针(&pages_[frame_id]

UnpinPgImp(page_id, is_dirty)

  1. 加锁保护
    • 使用 std::scoped_lock<std::mutex>latch_ 进行加锁,以确保线程安全
  2. 查找页面
    • 使用 page_table_->Find(page_id, frame_id) 查找页面表(page_table_)中的指定页面 ID (page_id),获取对应的帧 ID (frame_id)
    • 如果页面表中没有找到对应的页面 ID,返回 false
  3. 检查页面固定计数
    • 如果找到的页面固定计数(pin_count_)小于等于零,返回 false
  4. 处理脏页
    • 如果 is_dirtytrue,将页面标记为脏页
  5. 减少页面的固定计数
    • 将页面的固定计数减一
  6. 设置页面为可逐出状态
    • 如果页面的固定计数减为零,将该页面设置为可逐出状态

FlushPgImp(page_id)

  1. 无效页面 ID 检查
    • 如果传入的 page_id 是无效的,直接返回 false,表示刷新操作失败
  2. 查找页面
    • 使用 page_table_->Find(page_id, frame_id) 在页面表(page_table_)中查找指定的页面 ID (page_id),获取对应的帧 ID (frame_id)
    • 如果页面表中没有找到对应的页面 ID,返回 false,表示页面不在缓冲池中,刷新操作失败
  3. 写入磁盘
    • 调用 disk_manager_->WritePage(page_id, pages_[frame_id].data_) 将页面数据写入磁盘。这里使用的是页面 ID 和页面数

NewPgImp(page_id)

  1. 检查是否有空闲页面
    • 遍历缓冲池中的所有页面,检查是否存在固定计数为零的页面(即空闲页面)
    • 如果找到空闲页面,将 is_free_page 设置为 true
  2. 如果没有空闲页面
    • 如果没有空闲页面,返回 nullptr,表示无法分配新页面
  3. 分配新页面 ID
    • 调用 AllocatePage() 分配一个新的页面 ID,并将其存储在 *page_id
  4. 寻找或逐出页面
    • 检查空闲列表是否为空:
      • 如果不为空,从空闲列表中弹出一个帧 ID(frame_id
      • 如果为空,则使用替换策略(replacer_->Evict(&frame_id))逐出一个页面
      • 逐出页面时,如果页面是脏的(IsDirty()),则将其写回磁盘(disk_manager_->WritePage),并清除脏标记
      • 清除逐出页面的内存
      • 从页面表中移除逐出的页面
  5. 插入新页面
    • 将新页面插入页面表
    • 设置新页面的 ID 和固定计数
  6. 更新替换器状态
    • 记录新页面的访问
    • 设置新页面为不可逐出状态

DeletePgImp(page_id)

  1. 查找页面
    • 使用 page_table_->Find(page_id, frame_id) 在页面表(page_table_)中查找指定的页面 ID (page_id),获取对应的帧 ID (frame_id)
    • 如果页面表中没有找到对应的页面 ID,返回 true,表示页面不存在,因此也不需要删除
  2. 检查页面固定计数
    • 如果找到的页面固定计数(pin_count_)大于零,返回 false,表示页面仍在使用,无法删除
  3. 从替换器中移除页面
    • 调用 replacer_->Remove(frame_id) 将页面从替换器中移除
  4. 重置页面内存
    • 调用 pages_[frame_id].ResetMemory() 清除页面的内存
    • 将页面 ID 设置为无效值
    • 将页面的固定计数重置为零
    • 将页面的脏标记清除)
  5. 更新页面表和空闲列表
    • 从页面表中移除该页面
    • 将帧 ID 添加到空闲列表中
  6. 释放页面 ID
    • 调用 DeallocatePage(page_id) 释放页面 ID。

FlushAllPagesImpl()

  • 循环FlushPgImp所有的page

注意事项

  • 注意newfetch后要设置evitfalse
  • pin--等于0的时候设置evittrue

最后放个线上测试图
在这里插入图片描述

CMU 15445 课程的 Project 0 是一个调试练习,旨在帮助学生熟悉调试工具和技术。在这个项目中,你将开始使用 GDB 和 Valgrind 这两个常用的调试工具,以及一些其他辅助工具。以下是一些问题和步骤,帮助你完成这个练习: 1. 你需要查看项目中提供的代码,并了解它的结构和功能,这样你才能更好地理解程序的逻辑和可能出现的 bug。 2. 接下来,你需要编译项目,并确保没有编译错误。如果出现错误,你需要修复它们,这可能需要检查一些语法错误或缺失的库。 3. 一旦成功编译项目,你就可以使用 GDB 进行调试了。GDB 是一个强大的调试器,可以帮助你找出程序中的错误。你可以使用 GDB 来单步执行代码、设置断点、查看变量的值等等。通过使用 GDB,你可以逐步查看代码运行的路径,并找出程序崩溃或产生错误的原因。 4. 在使用 GDB 进行调试时,你可以通过设置断点来暂停程序的执行,并查看变量的值和程序的状态。你可以使用“break”命令在程序中设置断点,并通过“run”命令启动程序。当程序到达这个断点时,它会停止执行,你可以使用“print”命令查看变量的值,或者“step”命令逐步执行代码。 5. 另一个常用的调试工具是 Valgrind。Valgrind 可以帮助你检测内存泄漏和错误的访问方式。你可以使用“valgrind”命令来运行程序,并查看 Valgrind 的输出。它会告诉你有关程序中任何潜在问题的信息,例如未初始化的变量、访问越界等。 6. 最后,当你发现 bug 并修复它们后,可以运行各种测试用例来验证程序的正确性。测试用例可以帮助你确定程序是否按预期工作,并且在修改代码后,它们可以帮助你确保你的修复没有引入新的错误。 通过完成 CMU 15445 项目 0 的调试练习,你将掌握一些重要的调试技巧和工具,这对于进一步开发和调试软件应用程序将非常有用。希望上述步骤和建议对你有所帮助,祝你顺利完成这个项目!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值