文章目录
The LRU-K algorithm evicts a frame whose backward k-distance is maximum of all frames in the replacer. Backward k-distance is computed as the difference in time between current timestamp and the timestamp of kth previous access. A frame with less than k historical accesses is given +inf as its backward k-distance. When multipe frames have +inf backward k-distance, the replacer evicts the frame with the earliest timestamp.
大意是,每次替换会优先替换k-距离最远的一个数。
假如这个k是3,简单画一下一个实例,依次加入下面几个数

如果一个数出现次数达到了K次,那么k-distance就是倒数第K次出现的位置
如果一个数出现不到K次,那么k-distance就是+inf
这是对K-distance严谨的定义
优先驱逐距离为 + i n f +inf +inf的frame,如果有多个这样的数,其实LRU-K是有多种策略来决定下一个驱逐谁的(比如用LRU),在本题目中
When multipe frames have +inf backward k-distance, the replacer evicts the frame with the earliest timestamp.
使用的先进先出(FIFO)策略,比如在上图中,4和1都出现了不足K次,如果要驱逐就驱逐先出现的4。
如果没有k-distance为正无穷的frame,优先驱逐该距离最大的。
我们需要理解为什么需要用LRU-K而非LRU策略
Intra表达在一个Process里面,Inter表示在两个Process之间。这个例子是说:一个事务访问一个页,然后另外一个不同流程的事务也访问这个页。这两个事务之前是完全独立的,目的也不一样。一次关联访问的结束和下一段关联访问开始之间的时间间隔。
HIST(p),是关于p的访问记录(history)的。HIST(p,n)就是页面p倒数第n次被访问的时间。例如HIST(2,1)就是2号页面最后一次被访问的时间(这个是不算关联访问的,也就是说每段关联访问只被记录一次)LAST(p)就是页面p最后一次被访问的时间,这个是无论关联访问的LAST(p)就好了
HIST不同的page出现时间,然后用了循环来寻找满足条件最小的时间戳。如果被驱逐的是个被修改过的页面,还需要将它写回硬盘。

size_t current_timestamp_{0};
size_t curr_size_{0};
size_t max_size_;
size_t replacer_size_;
size_t k_;
std::mutex latch_;
using timestamp = std::list<size_t>;//记录单个页时间戳的列表
using k_time = std::pair<frame_id_t,size_t>;
std::unordered_map<frame_id_t,timestamp> hist;//用于记录所有页的时间戳
std::unordered_map<frame_id_t,size_t> recorded_cnt_;//用于记录,访问了多少次
std::unordered_map<frame_id_t,bool> evictable_;//用于记录是否可以被驱逐
std::list<frame_id_t> new_frame_;//用于记录不满k次的页
std::unordered_map<frame_id_t,std::list<frame_id_t>::iterator> new_locate_;
std::list<k_time> cache_frame_;//用于记录到达k次的页
std::unordered_map<frame_id_t,std::list<k_time>::iterator> cache_locate_;
| 名称 | 作用 |
|---|---|
| current_timestamp_ | 当前的时间戳,每进行一次record操作加一 |
| curr_size_ | 当前存放的可驱逐页面数量 |
| max_size | 最多可驱逐页面数量(去掉被pin住的页面) |
| replacer_size_ | 整个主存大小(包括被pin的页面) |
| k_ | lru-k中的k值 |
| latch_ | 用于维护多线程的锁 |
| timestamp | 单个页面的一连串时间戳 |
| k_time | 页面和kth时间戳 |
| hist | 所有页面的访问记录 |
| recorded_cnt_ | 被访问次数的记录 |
| evictable | 记录一个页面是否可以被驱逐 |
| new_frame_ | 记录不满足k次访问页的页号 |
| new_locate_ | 页号到上面这个链表迭代器的哈希表 |
| cache_frame | 到达k次页的链表 |
| cache_locate_ | 哈希表,解释同上 |
reverse_iterator反向迭代器从后往前遍历,查找允许被驱逐的页面
using k_time = std::pair<frame_id_t,size_t>;这个表示页面对应的倒数第k次访问的时间戳。std::list<k_time> cache_frame_;//用于记录到达k次的页
std::unordered_map<frame_id_t,std::list<k_time>::iterator> cache_locate_;
cache_frame_的稳定性 auto kth_time = hist[frame_id].front();//获取当前页面的倒数第k次出现的时间
k_time new_cache(frame_id,kth_time);
auto it = std::upper_bound(cache_frame_.begin(),cache_frame_.end(),new_cache, CmpTimestamp);//找到该插入的位置
it = cache_frame_.insert(it,new_cache);
cache_locate_[frame_id] = it;
| 方法原型 | 作用 |
|---|---|
| LRUKReplacer(size_t num_frames, size_t k) | 生成器,num_frames是最大缓存,k是lru_k中的k值 |
| auto Evict(frame_id_t *frame_id) -> bool | 驱逐一个页面,并保存到frame_id中 |
| void RecordAccess(frame_id_t frame_id); | 增加一个页面的访问记录 |
| void SetEvictable(frame_id_t frame_id, bool set_evictable); | 设置一个页面是否可以被驱逐 |
| void Remove(frame_id_t frame_id); | 移除指定页面 |
| auto Size() -> size_t; | 返回可驱逐页面的大小 |
LRU-K.h//
// Created by Anti on 2022/12/27.
//
#ifndef LRU_K_H
#define LRU_K_H
#include <limits>
#include <list>
#include <mutex>
#include <unordered_map>
#include <vector>
#include <algorithm>
class LRUKReplacer {
public:
using frame_id_t = int;
explicit LRUKReplacer(size_t num_frames, size_t k);
~LRUKReplacer()=default;
auto Evict(frame_id_t *frame_id) -> bool;
void RecordAccess(frame_id_t frame_id);
void SetEvictable(frame_id_t frame_id, bool set_evictable);
void Remove(frame_id_t frame_id);
auto Size() -> size_t;
private:
size_t current_timestamp_{0};
size_t curr_size_{0};
size_t max_size_;
size_t replacer_size_;
size_t k_;
std::mutex latch_;
using timestamp = std::list<size_t>;//记录单个页时间戳的列表
using k_time = std::pair<frame_id_t,size_t>;
std::unordered_map<frame_id_t,timestamp> hist;//用于记录所有页的时间戳
std::unordered_map<frame_id_t,size_t> recorded_cnt_;//用于记录,访问了多少次
std::unordered_map<frame_id_t,bool> evictable_;//用于记录是否可以被驱逐
std::list<frame_id_t> new_frame_;//用于记录不满k次的页
std::unordered_map<frame_id_t,std::list<frame_id_t>::iterator> new_locate_;
std::list<k_time> cache_frame_;//用于记录到达k次的页
std::unordered_map<frame_id_t,std::list<k_time>::iterator> cache_locate_;
static auto CmpTimestamp(const k_time &f1,const k_time &f2) -> bool;
};
#endif //LRU_K_H
LRU-K.cpp//
// Created by Anti on 2022/12/27.
//
#include "LRU_K.h"
LRUKReplacer::LRUKReplacer(size_t num_frames, size_t k) : replacer_size_(num_frames), k_(k) {
max_size_=num_frames;
}
auto LRUKReplacer::Evict(frame_id_t *frame_id) -> bool {
std::lock_guard<std::mutex> lock(latch_);
/**
* 如果没有可以驱逐元素
*/
if(Size()==0)
{
return false;
}
/**
* 首先尝试删除距离为无限大的缓存
*/
for(auto it = new_frame_.rbegin();it!=new_frame_.rend();it++)
{
auto frame = *it;
if(evictable_[frame])//如果可以被删除
{
recorded_cnt_[frame] = 0;
new_locate_.erase(frame);
new_frame_.remove(frame);
*frame_id = frame;
curr_size_--;
hist[frame].clear();
return true;
}
}
/**
* 再尝试删除已经访问过K次的缓存
*/
for(auto it =cache_frame_.begin();it!=cache_frame_.end();it++)
{
auto frame = (*it).first;
if(evictable_[frame])
{
recorded_cnt_[frame] = 0;
cache_frame_.erase(it);
cache_locate_.erase(frame);
*frame_id = frame;
curr_size_--;
hist[frame].clear();
return true;
}
}
return false;
}
void LRUKReplacer::RecordAccess(frame_id_t frame_id)
{
std::lock_guard<std::mutex> lock(latch_);
if(frame_id>static_cast<frame_id_t>(replacer_size_))
{
throw std::exception();
}
current_timestamp_++;
recorded_cnt_[frame_id]++;
auto cnt = recorded_cnt_[frame_id];
hist[frame_id].push_back(current_timestamp_);
/**
* 如果是新加入的记录
*/
if(cnt==1)
{
if(curr_size_==max_size_)
{
frame_id_t frame;
Evict(&frame);
}
evictable_[frame_id] = true;
curr_size_++;
new_frame_.push_front(frame_id);
new_locate_[frame_id] = new_frame_.begin();
}
/**
* 如果记录达到k次,则需要从新队列中加入到老队列中
*/
if(cnt==k_)
{
new_frame_.erase(new_locate_[frame_id]);//从新队列中删除
new_locate_.erase(frame_id);
auto kth_time = hist[frame_id].front();//获取当前页面的倒数第k次出现的时间
k_time new_cache(frame_id,kth_time);
auto it = std::upper_bound(cache_frame_.begin(),cache_frame_.end(),new_cache,CmpTimestamp);//找到该插入的位置
it = cache_frame_.insert(it,new_cache);
cache_locate_[frame_id] = it;
return;
}
/**
* 如果记录在k次以上,需要将该frame放到指定的位置
*/
if(cnt>k_)
{
hist[frame_id].erase(hist[frame_id].begin());
cache_frame_.erase(cache_locate_[frame_id]);//去除原来的位置
auto kth_time = hist[frame_id].front();//获取当前页面的倒数第k次出现的时间
k_time new_cache(frame_id,kth_time);
auto it = std::upper_bound(cache_frame_.begin(),cache_frame_.end(),new_cache, CmpTimestamp);//找到该插入的位置
it = cache_frame_.insert(it,new_cache);
cache_locate_[frame_id] = it;
return;
}
/**
* 如果cnt<k_,是不需要做更新动作的
*/
}
void LRUKReplacer::SetEvictable(frame_id_t frame_id, bool set_evictable)
{
std::lock_guard<std::mutex> lock(latch_);
if(recorded_cnt_[frame_id]==0)
{
return ;
}
auto status = evictable_[frame_id];
evictable_[frame_id] = set_evictable;
if(status&&!set_evictable)
{
--max_size_;
--curr_size_;
}
if(!status&&set_evictable)
{
++max_size_;
++curr_size_;
}
}
void LRUKReplacer::Remove(frame_id_t frame_id) {
std::lock_guard<std::mutex> lock(latch_);
if (frame_id > static_cast<frame_id_t>(replacer_size_)) {
throw std::exception();
}
auto cnt = recorded_cnt_[frame_id];
if (cnt == 0)
{
return ;
}
if(!evictable_[frame_id])
{
throw std::exception();
}
if(cnt<k_)
{
new_frame_.erase(new_locate_[frame_id]);
new_locate_.erase(frame_id);
recorded_cnt_[frame_id] = 0;
hist[frame_id].clear();
curr_size_--;
}
else
{
cache_frame_.erase(cache_locate_[frame_id]);
cache_locate_.erase(frame_id);
recorded_cnt_[frame_id] = 0;
hist[frame_id].clear();
curr_size_--;
}
}
auto LRUKReplacer::Size() -> size_t { return curr_size_; }
auto LRUKReplacer::CmpTimestamp(const LRUKReplacer:: k_time &f1,const LRUKReplacer:: k_time &f2) -> bool {
return f1.second<f2.second;
}
TEST(LRUKReplacerTest, AntiO2)
{
LRUKReplacer lru_replacer(3, 3);
frame_id_t frame;
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);
ASSERT_EQ(frame,3);
lru_replacer.Evict(&frame);
EXPECT_EQ(frame,1);
lru_replacer.RecordAccess(1);
lru_replacer.RecordAccess(3);
lru_replacer.RecordAccess(1);
lru_replacer.Evict(&frame);
EXPECT_EQ(frame,1);
lru_replacer.RecordAccess(3);
lru_replacer.RecordAccess(3);
lru_replacer.Evict(&frame);
EXPECT_EQ(frame,2);
lru_replacer.Evict(&frame);
EXPECT_EQ(frame,3);
}

我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:
尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot
原始问题Letd(n)bedefinedasthesumofproperdivisorsofn(numberslessthannwhichdivideevenlyinton).Ifd(a)=bandd(b)=a,whereab,thenaandbareanamicablepairandeachofaandbarecalledamicablenumbers.Forexample,theproperdivisorsof220are1,2,4,5,10,11,20,22,44,55and110;therefored(220)=284.Theproperdivisorsof284are1,2,
一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su
TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是
当我尝试进行bundle安装时,我的gem_path和gem_home指向/usr/local/rvm/gems/我没有写入权限,并且由于权限无效而失败。因此,我已将两个路径都更改为我具有写入权限的本地目录。这样做时,我进行了bundle安装,我得到:bruno@test6:~$bundleinstallFetchinggemmetadatafromhttps://rubygems.org/.........Fetchinggemmetadatafromhttps://rubygems.org/..Bundler::GemspecError:Couldnotreadgemat/afs/
我一直在Heroku上尝试不同的缓存策略,并添加了他们的memcached附加组件,目的是为我的应用程序添加Action缓存。但是,当我在我当前的应用程序上查看Rails.cache.stats时(安装了memcached并使用dalligem),在执行应该缓存的操作后,我得到current和total_items为0。在Controller的顶部,我想缓存我有的Action:caches_action:show此外,我修改了我的环境配置(对于在Heroku上运行的配置)config.cache_store=:dalli_store我是否可以查看其他一些统计数据,看看它是否有效或我做错
我有一个具有页面缓存的ControllerAction,我制作了一个清扫程序,它使用Controller和指定的Action调用expire_page...Controller操作呈现一个js.erb模板,所以我试图确保expire_page删除public/javascripts中的.js文件,但它没有这样做。classJavascriptsController"javascripts",:action=>"lol",:format=>'js')endend...所以,我访问javascripts/lol.js并呈现我的模板。我验证了public/javascripts/lol.js
我的Controller有这个:caches_action:render_ticker_for_channel,:expires_in=>30.seconds在我的路由文件中我有这个:match'/render_c_t/:channel_id'=>'render#render_ticker_for_channel',:as=>:render_channel_ticker在日志文件中我看到了这个:Writefragmentviews/mcr3.dev/render_c_t/63(11.6ms)我如何手动使它过期?我需要从与渲染Controller不同的Controller使它过期,但即使
我在开发和生产中都使用docker,真正困扰我的一件事是docker缓存的简单性。我的ruby应用程序需要bundleinstall来安装依赖项,因此我从以下Dockerfile开始:添加GemfileGemfile添加Gemfile.lockGemfile.lock运行bundleinstall--path/root/bundle所有依赖项都被缓存,并且在我添加新gem之前效果很好。即使我添加的gem只有0.5MB,从头开始安装所有应用程序gem仍然需要10-15分钟。由于依赖项文件夹的大小(大约300MB),然后再花10分钟来部署它。我在node_modules和npm上遇到了