CMU15445-project1-满分收获总结
TASK #0 gradescope
0.0 关于线上测试的天坑
在gradescope上测试代码时,没有通过的案例如果显示的是超时,那不一定是因为死锁或者死循环!
- 我没有写线程安全的代码,显示的是超时
- 我代码有的部分会造成内存泄漏,显示的还是超时
- 死锁或者replacer复杂度太高确实有可能造成超时
TASK #1 - LRU REPLACEMENT POLICY
1.1线程安全
要求了线程安全但又没有说具体怎么做,以至于在最后才回来考虑上锁的问题。
其实很简单,在头文件内声明一个锁。在实现的每个函数开头上锁,结尾释放就行。
// 头文件
#include<mutex>
// 声明
std::mutex lock_;
//上锁(请求锁),释放锁
lock_.lock();
lock_.unlock();
注意事项:
1.每个return之前都要记得释放锁
2.关于死锁,如果都是开头上锁结尾解锁,根本不会有产生死锁的条件(请求并保持)。
3.如果在一个函数里调用了另一个使用lock的函数,百分百会卡死,因为这个函数的lock还未释放,调用的函数就会一直等待lock。解决办法:在函数调用前释放锁,调用后申请锁。(但这个project可其实以避免这种调用关系)
1.2 upin 和 pin 如何理解
pin():就理解成有人正在使用这个页面,不能用来作为牺牲了,故需要删除当前frame_id(而不是放在末尾)
unpin():lru_relpacer不需要考虑什么时候需要unpin,直接理解成当前frame_id可以作为牺牲id,装入队列就行。
1.3 优化搜索效率
使用STL自带的unsord_map()
作为hash表,key
是frame_id
,value
是list
内对应元素的指针(迭代器),这样对frame_id
对应的元素的操作只需要O(1)级别复杂度。
(对应的leetcode题,代码相似度很高)
//初始化
std::list<frame_id_t> page_list_;
std::unordered_map<frame_id_t, std::list<frame_id_t>::iterator> frame_table_;
// 使用示例
auto it = frame_table_.find(frame_id);
if (it != frame_table_.end()) { // exist
page_list_.erase(it->second);
frame_table_.erase(it);
}
TASK #2 - BUFFER POOL MANAGER INSTANCE
2.1 frame_id和page_id的关系
- 每一个BPLI(BUFFER POOL MANAGER INSTANCE)都会被分配一个固定的
pool_size_
,根据pool_size_
新建的pages_
数组实际上就是BPLI用来存放页面的盒子。 - 直观上来看,
frame_id
就是是pages_
数组的下标,而page_id
指的放入盒子(数组)的page号。 - 所以
replacer
管理的是frame_id
的分配,BPLI 的工作就是负责分配frame_id
(实则是分配盒子)给pages
。
2.2 天坑:DeletePgImp() 的返回值问题
这个坑卡了我一下午的时间debug,就一个返回值让我从62分跳到了96分。
按道理找不到页面应该返回false
,这里却要求的是true
。虽然也的确是审题不当,但这个着实把我坑惨了。
2.3 UnpinPgImp() 注意事项,dirty()测试跑不通
这个函数没有太多注解,所以有的地方都是靠上传测试摸索出来的,感觉实在没必要。
1.对于pin_count_大于1的情况,是减一,而不是清零。
2.不需要对脏页面进行写回磁盘,只需要把脏标记在对应page上就行。
TASK #3 - PARALLEL BUFFER POOL MANAGER
这部分其实看了需求就会知道,是最简单的一部分,只需要对之前两部分进行调用就行了,没有太大问题。
3.1 线程安全
这个管理器不需要设置锁,只需要在replacer
和bpli
设置锁就行。