其他分享
首页 > 其他分享> > [满分]CMU-15/445[2021fall]PROJECT #1

[满分]CMU-15/445[2021fall]PROJECT #1

作者:互联网

PROJECT #1 - BUFFER POOL

TASK #1 - LRU REPLACEMENT POLICY

需要实现LRU策略。实现如下函数:

PinUnpin的含义比较令人疑惑,函数的名字是根据Task2中的功能来取的。当从缓冲池中读取一个页时,为了不让其他操作把这个页替换掉,我们需要"Pin"住这个页,不让它被替换(牺牲)。Unpin与之相反,表明我们不再需要这个页, 此页可以被牺牲。

需要注意的是,Pin一个页或Unpin一个页可能重复操作,需要进行处理。同时,Unpin一个操作发生前,此帧已经在牺牲候选队列中,不会对其进行移动!。

错误想法可能是Unpin一个帧,无论是否在队列中,都会将其移动到队列头。通过观察测试代码可以确认函数的功能。

为了O(1)时间进行查找,可以用Hash + LinkList的实现方式。

1.1 数据结构

// lru牺牲队列的最大容量
const size_t capacity_;
std::mutex latch_;
// 牺牲队列
std::list<frame_id_t> lru_list_;
// 将frame_id和其对应的list的迭代器对应起来
std::unordered_map<frame_id_t, std::list<frame_id_t>::iterator> lru_map_;

1.2 VictimHelper函数

编写无锁的,挑选牺牲帧的帮助函数。其合法性由调用者控制。

frame_id_t LRUReplacer::VictimeHelper() {
  // 从list尾拿取被牺牲帧号。
  // 将此帧从list和map中移除
  frame_id_t victime_frame = lru_list_.back();
  lru_list_.pop_back();
  lru_map_.erase(victime_frame);
  return victime_frame;
}

1.3 Victim函数

如果没有可以牺牲的帧直接返回false,否则调用帮助函数拿取牺牲帧。

bool LRUReplacer::Victim(frame_id_t *frame_id) {
  std::lock_guard<std::mutex> guard(latch_);
  if (lru_map_.empty()) {
    *frame_id = -1;
    return false;
  }
  *frame_id = VictimeHelper();
  return true;
}

1.4 Pin函数

Pin表示这个frame被引用了,被引用的frame不能成为LRU的牺牲目标。

void LRUReplacer::Pin(frame_id_t frame_id) {
  std::lock_guard<std::mutex> guard(latch_);
  if (lru_map_.find(frame_id) == lru_map_.end()) {
    return;
  }
  lru_list_.erase(lru_map_.find(frame_id)->second);
  lru_map_.erase(frame_id);
}

1.5 Unpin函数

Unpin表示该页不再使用了,可以被替换出去,加到牺牲队列中。如果此前已在队列中,就不操作。

void LRUReplacer::Unpin(frame_id_t frame_id) {
  std::lock_guard<std::mutex> guard(latch_);
  if (lru_map_.find(frame_id) != lru_map_.end()) {
    return;
  }
  while (lru_map_.size() >= capacity_) {
    VictimeHelper();
  }
  lru_list_.push_front(frame_id);
  lru_map_[frame_id] = lru_list_.begin();
}

1.6 Size函数

size_t LRUReplacer::Size() {
  std::lock_guard<std::mutex> guard(latch_);
  return lru_map_.size();
}

TASK #2 - BUFFER POOL MANAGER INSTANCE

实现一个缓冲池管理器,负责从磁盘中读取数据库页并存储在内存中。系统中所有的内存页都由页对象表示。系统整个生命周期中,重用内存位置,相同的frame位置(包含一个Page对象)可能对应不同的页面。Page对象的标识符(page_id)表示其指向的页面,如果不包含,则设置为INVALID_PAGE_ID。每个Page对象维护一个计数器,以显式Pin该页面的线程数,不允许释放被Pin的页面。每个Page对象还有脏页标记,只有将脏页写回磁盘,才能重用。

需要实现如下函数:

2.1 FindReplaceFrame函数

无锁的帮助函数,选取一个帧用于存储新的页。

bool BufferPoolManagerInstance::FindReplaceFrame(frame_id_t *frame_id) {
  if (free_list_.empty() && replacer_->Size() == 0) {
    return false;
  }
  if (!free_list_.empty()) {
    *frame_id = free_list_.back();
    free_list_.pop_back();
  } else {
    bool is_victim = replacer_->Victim(frame_id);
    if (!is_victim) {
      return false;
    }
    page_id_t page_id = pages_[*frame_id].page_id_;
    page_table_.erase(page_id);
    pages_[*frame_id].WLatch();
    if (pages_[*frame_id].IsDirty()) {
      disk_manager_->WritePage(page_id, pages_[*frame_id].data_);
    }
    pages_[*frame_id].ResetMemory();
    pages_[*frame_id].page_id_ = INVALID_PAGE_ID;
    pages_[*frame_id].pin_count_ = 0;
    pages_[*frame_id].is_dirty_ = false;
    pages_[*frame_id].WUnlatch();
  }
  return true;
}

2.2 FlushPgImp函数

如果page_id是无效值或者page_table_中找不到page_id,返回false。

bool BufferPoolManagerInstance::FlushPgImp(page_id_t page_id) {
  // Make sure you call DiskManager::WritePage!
  std::lock_guard<std::mutex> guard(latch_);
  if (page_id == INVALID_PAGE_ID || page_table_.find(page_id) == page_table_.end()) {
    return false;
  }
  Page &rp = pages_[page_table_.find(page_id)->second];
  rp.RLatch();
  disk_manager_->WritePage(rp.page_id_, rp.data_);
  rp.RUnlatch();
  return true;
}

2.3 FlushAllPgsImp函数

void BufferPoolManagerInstance::FlushAllPgsImp() {
  // You can do it!
  std::lock_guard<std::mutex> guard(latch_);
  for (auto &iterat : page_table_) {
    Page &rp = pages_[iterat.second];
    rp.RLatch();
    disk_manager_->WritePage(rp.page_id_, rp.data_);
    rp.RUnlatch();
  }
}

2.4 NewPgImp函数

Page *BufferPoolManagerInstance::NewPgImp(page_id_t *page_id) {
  // 0.   Make sure you call AllocatePage!
  // 1.   If all the pages in the buffer pool are pinned, return nullptr.
  // 2.   Pick a victim page P from either the free list or the replacer. Always pick from the free list first.
  // 3.   Update P's metadata, zero out memory and add P to the page table.
  // 4.   Set the page ID output parameter. Return a pointer to P.
  std::lock_guard<std::mutex> guard(latch_);
  frame_id_t frame_id = -1;
  if (!FindReplaceFrame(&frame_id)) {
    return nullptr;
  }
  *page_id = AllocatePage();
  page_table_[*page_id] = frame_id;

  pages_[frame_id].WLatch();
  pages_[frame_id].page_id_ = *page_id;
  ++pages_[frame_id].pin_count_;
  disk_manager_->WritePage(pages_[frame_id].page_id_, pages_[frame_id].data_);
  pages_[frame_id].WUnlatch();
  replacer_->Pin(frame_id);   // 冗余、逻辑清晰
  return &(pages_[frame_id]);
}

2.5 FetchPgImp函数

Page *BufferPoolManagerInstance::FetchPgImp(page_id_t page_id) {
  // 1.     Search the page table for the requested page (P).
  // 1.1    If P exists, pin it and return it immediately.
  // 1.2    If P does not exist, find a replacement page (R) from either the free list or the replacer.
  //        Note that pages are always found from the free list first.
  // 2.     If R is dirty, write it back to the disk.
  // 3.     Delete R from the page table and insert P.
  // 4.     Update P's metadata, read in the page content from disk, and then return a pointer to P.
  std::lock_guard<std::mutex> guard(latch_);
  frame_id_t frame_id = -1;
  if (page_table_.find(page_id) != page_table_.end()) {
    frame_id = page_table_.find(page_id)->second;
  } else {
    if (!FindReplaceFrame(&frame_id)) {
      return nullptr;
    }

    pages_[frame_id].WLatch();
    disk_manager_->ReadPage(page_id, pages_[frame_id].data_);
    pages_[frame_id].page_id_ = page_id;
    pages_[frame_id].WUnlatch();

    page_table_[page_id] = frame_id;
  }
  pages_[frame_id].WLatch();
  ++pages_[frame_id].pin_count_;
  pages_[frame_id].WUnlatch();

  replacer_->Pin(frame_id);
  return &(pages_[frame_id]);
}

2.6 DeletePgImp函数

bool BufferPoolManagerInstance::DeletePgImp(page_id_t page_id) {
  // 0.   Make sure you call DeallocatePage!
  // 1.   Search the page table for the requested page (P).
  // 1.   If P does not exist, return true.
  // 2.   If P exists, but has a non-zero pin-count, return false. Someone is using the page.
  // 3.   Otherwise, P can be deleted. Remove P from the page table, reset its metadata and return it to the free list.
  std::lock_guard<std::mutex> guard(latch_);
  if (page_table_.find(page_id) == page_table_.end()) {
    return true;
  }
  frame_id_t frame_id = page_table_.find(page_id)->second;
  pages_[frame_id].WLatch();
  if (pages_[frame_id].pin_count_ > 0) {
    pages_[frame_id].WUnlatch();
    return false;
  }
  if (pages_[frame_id].IsDirty()) {
    disk_manager_->WritePage(pages_[frame_id].page_id_, pages_[frame_id].data_);
  }

  pages_[frame_id].ResetMemory();
  pages_[frame_id].page_id_ = INVALID_PAGE_ID;
  pages_[frame_id].pin_count_ = 0;
  pages_[frame_id].is_dirty_ = false;
  pages_[frame_id].WUnlatch();
  page_table_.erase(page_id);
  DeallocatePage(page_id);
  free_list_.push_front(frame_id);
  replacer_->Pin(frame_id);
  return true;
}

2.7 UnpinPgImp函数

bool BufferPoolManagerInstance::UnpinPgImp(page_id_t page_id, bool is_dirty) {
  std::lock_guard<std::mutex> guard(latch_);
  if (page_table_.find(page_id) == page_table_.end()) {
    return false;
  }
  frame_id_t frame_id = page_table_.find(page_id)->second;
  pages_[frame_id].WLatch();
  if (pages_[frame_id].pin_count_ <= 0) {
    pages_[frame_id].WUnlatch();
    return false;
  }
  --pages_[frame_id].pin_count_;
  pages_[frame_id].is_dirty_ = is_dirty || pages_[frame_id].is_dirty_;
  if (pages_[frame_id].pin_count_ == 0) {
    replacer_->Unpin(frame_id);
  }
  pages_[frame_id].WUnlatch();
  return true;
}

TASK #3 - PARALLEL BUFFER POOL MANAGER

需要实现并行缓冲池,较简单。除NewPgImp外,只需取模计算应当分配给哪个缓冲池实例然后调用即可。不需要加锁,只需将保存start_index_变量设为原子变量即可。

const size_t num_instances_;
const size_t instance_pool_size_;
std::vector<BufferPoolManager *> pool_instances_;
std::atomic<size_t> start_index_ = 0;
ParallelBufferPoolManager::ParallelBufferPoolManager(size_t num_instances, size_t pool_size, DiskManager *disk_manager,
                                                     LogManager *log_manager)
    : num_instances_(num_instances), instance_pool_size_(pool_size) {
  // Allocate and create individual BufferPoolManagerInstances
  for (size_t i = 0; i < num_instances; ++i) {
    pool_instances_.push_back(new BufferPoolManagerInstance(pool_size, num_instances, i, disk_manager, log_manager));
  }
}

// Update constructor to destruct all BufferPoolManagerInstances and deallocate any associated memory
ParallelBufferPoolManager::~ParallelBufferPoolManager() {
  for (auto &pool_instance : pool_instances_) {
    delete pool_instance;
  }
}

size_t ParallelBufferPoolManager::GetPoolSize() {
  // Get size of all BufferPoolManagerInstances
  return num_instances_ * instance_pool_size_;
}

BufferPoolManager *ParallelBufferPoolManager::GetBufferPoolManager(page_id_t page_id) {
  // Get BufferPoolManager responsible for handling given page id. You can use this method in your other methods.
  return pool_instances_[page_id % num_instances_];
}

Page *ParallelBufferPoolManager::FetchPgImp(page_id_t page_id) {
  // Fetch page for page_id from responsible BufferPoolManagerInstance
  return GetBufferPoolManager(page_id)->FetchPage(page_id);
}

bool ParallelBufferPoolManager::UnpinPgImp(page_id_t page_id, bool is_dirty) {
  // Unpin page_id from responsible BufferPoolManagerInstance
  return GetBufferPoolManager(page_id)->UnpinPage(page_id, is_dirty);
}

bool ParallelBufferPoolManager::FlushPgImp(page_id_t page_id) {
  // Flush page_id from responsible BufferPoolManagerInstance
  return GetBufferPoolManager(page_id)->FlushPage(page_id);
}

Page *ParallelBufferPoolManager::NewPgImp(page_id_t *page_id) {
  // create new page. We will request page allocation in a round robin manner from the underlying
  // BufferPoolManagerInstances
  // 1.   From a starting index of the BPMIs, call NewPageImpl until either 1) success and return 2) looped around to
  // starting index and return nullptr
  // 2.   Bump the starting index (mod number of instances) to start search at a different BPMI each time this function
  // is called
  Page *res = nullptr;
  for (size_t i = 0; i < num_instances_; ++i) {
    if ((res = pool_instances_[(i + start_index_) % num_instances_]->NewPage(page_id)) != nullptr) {
      break;
    }
  }
  start_index_ += 1;
  return res;
}

bool ParallelBufferPoolManager::DeletePgImp(page_id_t page_id) {
  // Delete page_id from responsible BufferPoolManagerInstance
  return GetBufferPoolManager(page_id)->DeletePage(page_id);
}

void ParallelBufferPoolManager::FlushAllPgsImp() {
  // flush all pages from all BufferPoolManagerInstances
  for (size_t i = 0; i < num_instances_; ++i) {
    pool_instances_[i]->FlushAllPages();
  }
}

杂项

标签:15,2021fall,frame,445,id,_.,pages,return,page
来源: https://www.cnblogs.com/pannnn/p/16383441.html