CMU 15-445 Project #1 - Buffer Pool(Task #2 - LRU-K Replacement Policy)

news2024/9/20 10:32:16

Task #2 - LRU-K Replacement Policy

  • 一、题目链接
  • 二、准备工作
  • 三、部分实现
  • 四、自定义测试用例

在这里插入图片描述

一、题目链接


二、准备工作

见 CMU 15-445 Project #0 - C++ Primer 中的准备工作。


三、部分实现

LRU-K的提出是为了更好地解决传统LRU中的缓存污染问题。所谓缓存污染,是指突发的不常用数据的访问可能会挤出缓存中的常用数据,从而导致缓存的效果下降。而LRU-K通过增加了一个历史队列,保证了不常用数据的突发访问不会干扰缓存队列中的常用数据缓存。

根据课程文档的描述,历史队列需要通过FIFO来进行缓存淘汰,而缓存队列则需要根据 k-distance(可是雪啊,飘进双眼~)来进行缓存淘汰,也即依据倒数第k次访问的时间戳来进行淘汰。因此缓存队列使用的并非传统的LRU,一个缓存队列中的页框尽管刚刚被访问,但它仍有可能被立即淘汰。

比如当k值为 3 时有访问序列 1->1->1->2->2->2->1,此时 页框1页框2 均被放入缓存队列,如果按照传统LRU,页框1 刚刚被访问,因此会淘汰 页框2,但如果按照LRU-K,页框1 的倒数第三次访问更早,因此正确的淘汰页框应该是 页框1

LRUKReplacer部分成员变量及页框实体Frame的定义

class LRUKReplacer {
public:
    class Frame {
    public:
        Frame(std::list<frame_id_t>::iterator iter, size_t timestamp) : iter_(iter) {
            timestamp_list_.emplace_back(timestamp);
        }

    public:
        size_t access_count_ = 1;               // 该页框被访问的次数,与timestamp_list_的长度一致
        bool evictable_ = true;                 // 该页框是否可以被驱逐
        std::list<size_t> timestamp_list_;      // 该页框的历史访问时间戳序列,长度最大值为k_
        std::list<frame_id_t>::iterator iter_;  // 该页框对应的frame_id在历史队列或缓存队列中的迭代器,用于提高查找效率
    };

	...

private:
    size_t current_timestamp_ = 0;  // 当前时间戳
    size_t curr_size_ = 0;          // 当前可驱逐的页框总数
    size_t replacer_size_;          // 内存所能容纳的最大页框数
    size_t k_;                      // LRU-K中的K值
    std::mutex latch_;              // 互斥锁

    std::list<frame_id_t> history_list_;                  // 历史队列,使用FIFO进行淘汰处理
    std::list<frame_id_t> cache_list_;                    // 缓存队列,根据k-distance进行淘汰处理
    std::unordered_map<frame_id_t, Frame> id_frame_map_;  // 映射页框号和其对应的页框实体
};

RecordAccess

void RecordAccess(frame_id_t frame_id) {
    std::scoped_lock<std::mutex> locker(latch_);

    /* frame_id不能超过replacer_size_,由此来限制页框总数。 */
    if (frame_id > static_cast<int>(replacer_size_)) {
        return;
    }

    /* 如果frame_id不在id_frame_map_中,表明是首次访问,需要将frame_id插入到历史队列队尾,同时新建页框实体并建立与frame_id的映射;
     * 否则需要根据+1后的访问次数与k_的关系进行相应处理。 */
    auto cur_pair = id_frame_map_.find(frame_id);
    if (cur_pair == id_frame_map_.end()) {
        history_list_.emplace_back(frame_id);
        Frame new_frame(std::prev(history_list_.end()), current_timestamp_);
        id_frame_map_.emplace(frame_id, new_frame);
        curr_size_++;
    } else {
        /* 增加访问次数 */
        cur_pair->second.access_count_++;

        /* 如果访问次数小于k次,只需要扩充访问时间序列;
         * 如果访问次数等于k次,需要扩充访问时间序列,然后将frame_id从历史队列删除,并根据倒数第k次的访问时间插入到缓存队列相应位置;
         * 如果访问次数大于k次,需要更新访问时间序列,并根据倒数第k次的访问时间将frame_id移动到缓存队列相应位置。 */
        if (cur_pair->second.access_count_ < k_) {
            /* 扩充访问时间序列 */
            cur_pair->second.timestamp_list_.emplace_back(current_timestamp_);
        } else if (cur_pair->second.access_count_ == k_) {
            /* 扩充访问时间序列 */
            cur_pair->second.timestamp_list_.emplace_back(current_timestamp_);

            /* 将frame_id从历史队列删除 */
            history_list_.erase(cur_pair->second.iter_);

            /* 根据倒数第k次的访问时间将frame_id插入到缓存队列相应位置 */
            size_t k_timestamp = cur_pair->second.timestamp_list_.front();
            auto iter = cache_list_.begin();
            while (iter != cache_list_.end() && k_timestamp > id_frame_map_.at(*iter).timestamp_list_.front()) {
                iter++;
            }
            cur_pair->second.iter_ = cache_list_.insert(iter, frame_id);  // 插入后要修改对应的迭代器
        } else {
            /* 更新访问时间序列 */
            cur_pair->second.timestamp_list_.pop_front();
            cur_pair->second.timestamp_list_.emplace_back(current_timestamp_);

            /* 根据倒数第k次的访问时间将frame_id移动到缓存队列相应位置 */
            cache_list_.erase(cur_pair->second.iter_);
            size_t k_timestamp = cur_pair->second.timestamp_list_.front();
            auto iter = cache_list_.begin();
            while (iter != cache_list_.end() && k_timestamp > id_frame_map_.at(*iter).timestamp_list_.front()) {
                iter++;
            }
            cur_pair->second.iter_ = cache_list_.insert(iter, frame_id);  // 插入后要修改对应的迭代器
        }
    }

    /* 时间增加 */
    current_timestamp_++;
}

Evict

auto Evict(frame_id_t *frame_id) -> bool {
    std::scoped_lock<std::mutex> locker(latch_);

    /* 没有允许驱逐的页框 */
    if (curr_size_ == 0) {
        return false;
    }

    /* 如果历史队列不为空,优先按FIFO从历史队列中驱逐 */
    if (!history_list_.empty()) {
        for (auto iter = history_list_.begin(); iter != history_list_.end(); iter++) {
            if (id_frame_map_.at(*iter).evictable_) {
                *frame_id = *iter;
                id_frame_map_.erase(*iter);
                history_list_.erase(iter);
                curr_size_--;
                return true;
            }
        }
    }

    /* 如果没能从历史队列中驱逐,则从缓存队列中按照依据k-distance驱逐 */
    if (!cache_list_.empty()) {
        for (auto iter = cache_list_.begin(); iter != cache_list_.end(); iter++) {
            if (id_frame_map_.at(*iter).evictable_) {
                *frame_id = *iter;
                id_frame_map_.erase(*iter);
                cache_list_.erase(iter);
                curr_size_--;
                return true;
            }
        }
    }

    return false;
}

SetEvictable

void SetEvictable(frame_id_t frame_id, bool set_evictable) {
    std::scoped_lock<std::mutex> locker(latch_);

    auto cur_pair = id_frame_map_.find(frame_id);

    /* frame_id不存在 */
    if (cur_pair == id_frame_map_.end()) {
        return;
    }

    /* 根据evictable_的初始值和目标值修改curr_size_ */
    if (cur_pair->second.evictable_ && !set_evictable) {
        curr_size_--;
    } else if (!cur_pair->second.evictable_ && set_evictable) {
        curr_size_++;
    }

    cur_pair->second.evictable_ = set_evictable;
}

Remove

void Remove(frame_id_t frame_id) {
    std::scoped_lock<std::mutex> locker(latch_);

    auto cur_pair = id_frame_map_.find(frame_id);

    /* 对应页框不存在 */
    if (cur_pair == id_frame_map_.end()) {
        return;
    }

    /* 对应页框不可驱逐 */
    if (!cur_pair->second.evictable_) {
        return;
    }

    /* 根据对应页框的引用次数从历史队列或缓存队列中将其删除,同时也要删除id_frame_map_中的映射 */
    if (cur_pair->second.access_count_ >= k_) {
        cache_list_.erase(cur_pair->second.iter_);
    } else {
        history_list_.erase(cur_pair->second.iter_);
    }
    id_frame_map_.erase(cur_pair);

    curr_size_--;
}

Size

auto Size() -> size_t {
    std::scoped_lock<std::mutex> locker(latch_);

    return curr_size_;
}

提交后的结果如下,LRU-K的所有测试用例全部通过:

在这里插入图片描述


四、自定义测试用例

由于默认提供的本地测试用例过于简单,很多问题都无法排查,因此这里分享两个个稍微复杂一点的测试用例:

TEST(LRUKReplacerTest, EvictTest1) {
    LRUKReplacer lru_replacer(3, 3);
    frame_id_t frame_id;

    ASSERT_EQ(lru_replacer.Size(), 0);

    lru_replacer.RecordAccess(1);
    lru_replacer.RecordAccess(1);
    lru_replacer.RecordAccess(1);
    lru_replacer.RecordAccess(2);
    lru_replacer.RecordAccess(2);
    lru_replacer.RecordAccess(2);
    lru_replacer.RecordAccess(1);

    ASSERT_EQ(lru_replacer.Size(), 2);

    lru_replacer.RecordAccess(3);

    lru_replacer.Evict(&frame_id);
    ASSERT_EQ(frame_id, 3);

    lru_replacer.Evict(&frame_id);
    EXPECT_EQ(frame_id, 1);

    lru_replacer.RecordAccess(1);
    lru_replacer.RecordAccess(3);
    lru_replacer.RecordAccess(1);

    lru_replacer.Evict(&frame_id);
    EXPECT_EQ(frame_id, 1);

    lru_replacer.RecordAccess(3);
    lru_replacer.RecordAccess(3);

    lru_replacer.Evict(&frame_id);
    EXPECT_EQ(frame_id, 2);

    lru_replacer.Evict(&frame_id);
    EXPECT_EQ(frame_id, 3);
}

TEST(LRUKReplacerTest, EvictTest2) {
    LRUKReplacer lru_replacer(3, 3);
    frame_id_t frame_id;

    ASSERT_EQ(lru_replacer.Size(), 0);

    lru_replacer.RecordAccess(1);
    lru_replacer.RecordAccess(1);
    lru_replacer.RecordAccess(1);
    lru_replacer.RecordAccess(2);
    lru_replacer.RecordAccess(2);
    lru_replacer.RecordAccess(3);
    lru_replacer.RecordAccess(3);
    lru_replacer.RecordAccess(3);
    lru_replacer.RecordAccess(2);

    ASSERT_EQ(lru_replacer.Size(), 3);

    lru_replacer.Evict(&frame_id);
    EXPECT_EQ(frame_id, 1);

    lru_replacer.Evict(&frame_id);
    EXPECT_EQ(frame_id, 2);

    lru_replacer.Evict(&frame_id);
    EXPECT_EQ(frame_id, 3);
}

参考 :

https://blog.csdn.net/Altair_alpha/article/details/127745308
https://blog.csdn.net/AntiO2/article/details/128439155

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/638991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

互联网大厂Java面试题1000+附答案(合适各级Java开发人员)

作为一名优秀的程序员&#xff0c;技术面试都是不可避免的一个环节&#xff0c;一般技术面试官都会通过自己的方式去考察程序员的技术功底与基础理论知识。 2022 年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;很多粉丝朋友私信希望我出一篇面试专题或…

【QQ界面展示-细节调整 Objective-C语言】

一、刚才我们已经做到这个状态了 1.刚才我们做到这里了,就是文字可以显示出来了, 基本数据给大家加载了,然后我们再把这个美化一下,再把这个时间处理一下, 2.咱们先把它这个效果稍微美化一下吧,怎么美化呢,来看一下, 首先,我们看一下示例程序,实际程序,中间是没有…

深度学习pytorch实战五:基于ResNet34迁移学习的方法图像分类篇自建花数据集图像分类(5类)超详细代码

1.数据集简介 2.模型相关知识 3.split_data.py——训练集与测试集划分 4.model.py——定义ResNet34网络模型 5.train.py——加载数据集并训练&#xff0c;训练集计算损失值loss&#xff0c;测试集计算accuracy&#xff0c;保存训练好的网络参数 6.predict.py——利用训练好的网…

ChatGPT 应用——使用 chatGPT 写高考作文

写作文&#xff0c;很简单&#xff0c;但写一篇好的作文&#xff0c;是非常有难度的。 想要写一篇高分作文&#xff0c;需要对作文题目有正确的理解&#xff0c;需要展现独到的观点和深入的思考&#xff0c;需要具备清晰的逻辑结构&#xff0c;需要准确而得体的语言表达。 正…

Python程序设计基础:数值

文章目录 一、数值数据类型二、python内置的数值操作三、math库 一、数值数据类型 Python语言可以很方便的用于处理数值运算问题&#xff0c;在数值运算过程中&#xff0c;常见的额两种数据类型分别为整数类型&#xff08;int&#xff09;和浮点类型&#xff08;float&#xf…

【论文阅读】(2023.06.09-2023.06.18)论文阅读简单记录和汇总

(2023.06.09-2023.06.12)论文阅读简单记录和汇总 2023/06/09&#xff1a;虽然下周是我做汇报&#xff0c;但是到了周末该打游戏还是得打的 2023/06/12&#xff1a;好累好困&#xff0c;现在好容易累。 目录 &#xff08;TCSVT 2023&#xff09;Facial Image Compression via …

2021-07-12

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

VMware12虚拟机安装macoxs10.14

按照以下教程操作 VMware12虚拟机安装Mac系统虚拟机与本机传输文件虚拟机联本机网络教程【图文详解】_vmware12 mac_扑在代码上的高尔基的博客-CSDN博客 补充&#xff0c;我操作时出现的状况 1、虚拟机的macos补丁 unlocker207.7z文件需要放在全英文路径下运行&#xff0c;…

flutter:文件系统目录、文件读写

参考 参考&#xff1a;老孟 文件存储和网络请求 数据存储 Dart的 IO 库包含了文件读写的相关类&#xff0c;它属于 Dart 语法标准的一部分&#xff0c;所以通过 Dart IO 库&#xff0c;无论是 Dart VM 下的脚本还是 Flutter&#xff0c;都是通过 Dart IO 库来操作文件的。但…

H桥级联型五电平逆变器MATLAB仿真模型

H桥级联型五电平逆变器MATLAB仿真模型资源-CSDN文库https://download.csdn.net/download/weixin_56691527/87899094 模型简介&#xff1a; MATLAB21b版本 逆变器采用H桥级联的形式连接&#xff0c;加设LCL滤波器&#xff0c;三相负载构成主电路。 采用SPWM调制&#xff0c;可…

解密混沌工程——混沌工程价值

在数字化转型、十四五规划的大背景 下&#xff0c;大规模上云、分布式的核心改造等“云化”逐渐走进企业。 但是&#xff0c;云化的发展&#xff0c;使企业系统的复杂度呈指数级增长&#xff0c;故障越来越多。 企业在数字化转型中拥抱云计算、 信创国产化、分布式核心等新技…

【软件测试】性能测试的9大误区,带你少走弯路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 误区1&#xff1a…

外卖点餐APP小程序系统开发功能有哪些?

外卖点餐APP小程序系统开发功能有哪些&#xff1f; 1、餐饮分类。点餐外卖小程序需要提供餐厅列表&#xff0c;以便于用户浏览餐厅信息。用户可以根据不同的筛选条件&#xff0c;如菜系、评价、地理位置等来选择自己想要的餐厅。 2、美食列表。针对特定餐厅&…

pandas---算术运算、逻辑运算、统计运算、自定义运算

1. Series的运算 适用于NumPy的数组运算也适用于Series。 # 基本算术运算 s 100 s - 100 s * 100 s / 100 s // 2 s ** 2 s % 2 Series之间的运算&#xff1a; 在运算中自动对齐索引&#xff1b;如果索引不对应&#xff0c;则补NaN&#xff1b;Series没有广播机制。 s3 …

Makerbase VESC 75200 AS5047P编码器测试

Makerbase VESC 75200 AS5047P编码器测试 提示&#xff1a;MKESC75200主板推荐使用VESC TOOL V3.0&#xff0c;固件版本V5.2。 第一部分 硬件介绍 1.1 硬件接口 1.2 硬件清单 序号品名数量1MKSESC 75200 V2.0主板122808电机&#xff08;带AS5047编码器&#xff09;13DC 24电…

网络安全合规-安全合规地图

网络安全行业国内本质上是合规驱动&#xff0c;但对合规的理解和尺度&#xff0c;甲方与甲方、甲方与乙方之间就有很大差别。 甲方通常来说都具有一定合规方面的积累&#xff0c;包括体系、制度、结构以及职责等等&#xff0c;如果有新的监管要求或是强制性标准发布&#xff0c…

HarmonyOS学习路之开发篇—Java UI框架(DependentLayout)

DependentLayout DependentLayout是Java UI框架里的一种常见布局。与DirectionalLayout相比&#xff0c;拥有更多的排布方式&#xff0c;每个组件可以指定相对于其他同级元素的位置&#xff0c;或者指定相对于父组件的位置。 支持的XML属性DependentLayout的共有XML属性继承自…

ASEMI代理艾赛斯IXFA14N85XHV功率MOSFET综合指南

编辑-Z 在当今世界&#xff0c;电力电子在各种应用中发挥着至关重要的作用&#xff0c;从电源和电机驱动到电动汽车和可再生能源系统。这些应用中的关键部件之一是功率MOSFET&#xff08;金属氧化物半导体场效应晶体管&#xff09;。IXFA14N85XHV是一款先进的功率MOSFET&#…

互联网医院app开发|互联网医院小程序开发

随着人们健康意识的增强&#xff0c;互联网医院系统已经成为了现代医疗服务的重要组成部分。互联网医院系统是指利用信息技术手段&#xff0c;建立具有医疗服务能力、具有与普通医院异地协同和医联体功能的综合性医疗服务平台。它可以为患者提供一系列完善的医疗服务&#xff0…

Vue3-03-Vue2 响应式 VS Vue3 响应式

本文来讲解从 Vue2 到 Vue3 响应式底层的一些改变。 前言 Vue 2.x 为什么不监听数组下标索引值的变化&#xff1f; 参考了很多博主的推文&#xff0c;自己也尝试了一下&#xff0c;Object.defineProperty 是可以做到监听数组的索引值的变化的&#xff0c;来做 getter 和 sette…