日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

操作系统:基于页面置换算法的缓存原理详解(下)

發(fā)布時間:2025/3/20 windows 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 操作系统:基于页面置换算法的缓存原理详解(下) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

概述:

? 在上一篇《操作系統(tǒng):基于頁面置換算法的緩存原理詳解(上)》中,我們主要闡述了FIFO、LRU和Clock頁面置換算法。接著上一篇說到的,本文也有三個核心算法要講解。分別是LFU(Least Frequently Used)、LRU-KMQ(Multi Queue)算法。


本文鏈接:http://blog.csdn.net/lemon_tree12138/article/details/50475240 --Coding-Naga
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?--轉(zhuǎn)載請注明出處

1.LFU

? 我們從LFU的英文全稱Least Frequently Used中就可以看到,此算法是基于資源被訪問的次數(shù)來實現(xiàn)的。由于算法很簡單,我們就直接給出思路,而邏輯代碼就不再展示了(因為下面的LRU-KMQ才是本文的關(guān)鍵)。LFU的原理圖可參見圖-1.


圖-1 LFU置換算法原理圖


算法步驟:

(1)當有新資源被訪問時,就把這個資源添加到緩存隊列的尾部;

(2)當訪問一個已經(jīng)存在的資源時,就把這個資源被訪問的次數(shù)+1,再上移至合適的位置;

(3)在被訪問次數(shù)相同的資源集合中,是按照訪問時間來排序的;

(4)當新資源加入時,檢測到此時隊列已滿,那么就把隊列尾部的資源換出,將新資源添加到隊列的尾部。

算法評價:

? 個人認為此算法并不是一個很好緩存算法,因為它不能很好地反映“用戶”在一個比較短的時間里訪問資源的走向。

2.LRU-K

LRU算法中存在的問題:

? LRU-K算法從名字就可以知道這一定是基于LRU算法的,LRU-K算法是在LRU算法上的一次改進。現(xiàn)在我們分兩種情況分別來看看LRU算法的效果。

? 第一種,假設(shè)存在一定量連續(xù)的訪問,比如說我先訪問A資源10次,再訪問B資源10次,再訪問A資源10次,再訪問C資源10次,等等等等。這樣我可以大致補上一個畫面就是緩存隊列是在一個比較小的范圍里來回替換,這樣就減少換入換出的次數(shù),提高系統(tǒng)的性能,這是第一種情況。

? 第二種情況就是我們依次訪問資源A、B、C、...、Y、Z,再重復(fù)n次。并且我們的緩存列隊長度比這個循環(huán)周期要小。這樣,我們的資源就需要不停地換入換出,增加IO操作,效率自然就下來了。這種情況導(dǎo)致的效果也被稱著是緩存污染。

LRU-K的優(yōu)勢:

? 針對上面第二種情況產(chǎn)生的緩存污染,我們做了一個相應(yīng)地調(diào)整——加入了一個新的歷史記錄的隊列。前面LRU算法中,我們是只要訪問了某一個資源,那么就把這個資源加入緩存,這樣的結(jié)果是資源浪費,畢竟緩存隊列的資源有限。而在LRU-K算法中,我們不再把只訪問一次的資源放入緩存,而是當資源被訪問了K次之后,才把這個資源加入到緩存隊列中去,并且從歷史隊列中刪除。當資源放入緩存中之后,我們就不用再考慮它的訪問次數(shù)了。在緩存隊列中,我們是以LRU算法來進行更新和淘汰的(對于歷史隊列可以使用FIFO也可以使用LRU)。

? 你是不是要問,既然這里加了一個新的歷史隊列還是要使用LRU算法,那么優(yōu)勢在哪里?而且加入了一個新的隊列,也是開銷呀,怎么能說還解決了LRU的問題呢?事實上一開始我也有這樣的念頭,后來一想,我們的歷史記錄隊列只是一個記錄數(shù)組,我們可以讓它的開銷很小。這里要怎么做呢?能想到么?

? 我們知道我們這里要說的資源,可能是一個進程,可能是一個什么其他比較大的對象。那么,如果直接在歷史隊列中保存這些對象或是進程,并不是一件很劃算的事情,不是嗎?所以,我們的突破點就是這個歷史記錄隊列能不能盡可能地小?是可以的。我們可以對對象進行Hash成一個整數(shù),這樣就可以不用保存原來的對象了,而后面的次數(shù)可以使用byte來保存(因為我們可以默認某一個資源在一定時間內(nèi),訪問的次數(shù)不會大得離譜,當然可以使用int或是long,沒問題)。如此一來,歷史列隊就可以做得很小了。如果你對于Hash這一點還不清楚,那么你需要做的是補補你的基礎(chǔ)知識了。

? LRU-K算法原理圖解請下面的圖-1.


圖-2 LRU-K置換算法原理圖

代碼實現(xiàn):

? 在代碼實現(xiàn)的過程,我們有一個理想地假設(shè),那就是我們將添加新資源與訪問老資源完全獨立開來。添加(offer)資源時默認在兩個隊列中都不包含,訪問的資源總是存在于某一個隊列當中。

添加新元素(offer):

public void offer(Object object) {if (histories == null) {throw new NullPointerException();}if (histories.size() == maxHistoryLength) {cleanHistory();}LRUHistory history = new LRUHistory();history.setHash(object.hashCode());history.setTimes(1);histories.add(history);}

訪問一存在的資源(visitting):

public void visitting(Object object) {if (histories == null) {throw new NullPointerException();}if (caches == null) {throw new NullPointerException();}int hashCode = object.hashCode();if (inHistory(hashCode)) {boolean offerCache = modifyHistory(hashCode);if (!offerCache) {return;}offerToCache(object);} else if (inCache(object)) {displace(object);} else {throw new NullPointerException("對象不存在");}}

修改歷史記錄:

boolean offerCache = modifyHistory(hashCode); if (!offerCache) {return; } offerToCache(object); ? 上面代碼的邏輯描述是當我去修改歷史記錄隊列時,發(fā)現(xiàn)某一資源可以加入到緩存中去了,就把這個資源添加到緩存中去。

訪問緩存隊列某元素:

? 因為LRU-K中的緩存隊列就是一個完完全全的LRU,所以LRU-K中緩存隊列的訪問與LRU中訪問方式一致,如下:

private void displace(Object object) {for (Object item : caches) {if (item.equals(object)) {caches.remove(item);break;}}caches.add(object);}

3.Multi Queue(MQ)

解決的問題:

? 在上面的LRU-K算法中,只要被訪問資源的訪問次數(shù)達到一定數(shù)量時,就將這個資源添加到緩存中去。當然,這種做法是合理的,不過,這里我們對這種思路進行了一個擴展。我們按資源被訪問的次數(shù)對資源進行分級緩存。針對上面的LRU-K算法,我們假設(shè)當資源訪問次數(shù)超過3次時,就加入緩存隊列。此時有兩個資源A和B,A已經(jīng)被訪問了20次,B已經(jīng)被訪問了4次。這時如果再訪問一次B(假設(shè)不考慮后續(xù)的訪問操作),那么B的被淘汰的可能性比A被淘汰的可能要小。可是從整體上來看這種情況,我們知道理論上是A應(yīng)該比B具備更大的優(yōu)先級。

? 上面說到了LRU-K的“緩存污染”(這里并不否認URL-K算法對URL算法的“緩存污染”改善貢獻),所以我們就想了一個辦法來解決。正是這里要講解的MQ(Multi Queue)算法。下面請看MQ置換算法的原理圖:


圖-3 MQ置換算法原理圖

算法步驟:

(1)我們需要一個歷史記錄隊列和一個緩存隊列的數(shù)組。這個數(shù)組中保存的就是真正的緩存隊列,每個緩存隊列和歷史隊列均按LRU算法進行“淘汰”。而緩存隊列與緩存隊列之間是按訪問次數(shù)進行分級;

(2)當我們需要訪問一個全新的資源時,就把這個資源加入到最低等級的Q0中,如果有需要淘汰的資源,就把這個淘汰的資源加入到歷史隊列中;

(3)在某一個緩存隊列中資源再次被訪問時,就把這個資源加入到隊列的頭部。如果當前資源被訪問的次數(shù)達到一定的次數(shù),就把當前資源從當前隊列中刪除,并加入到更高級的緩存隊列的頭部;

(4)為了從一定程序上防止(并沒有絕對防止)高級別的緩存資源被刪除,當一定時間之內(nèi),某一資源還未訪問,將此資源等級下滑到下一等級;

(5)從上面緩存隊列數(shù)組中的緩存資源如果被“淘汰”,是被加入到了歷史隊列。加入歷史隊列的資源,如果被再次訪問可以重新計算資源被訪問次數(shù),并添加相應(yīng)的緩存隊列中去;而如果是歷史隊列中的資源被淘汰了,則是真正意義上的被淘汰。

邏輯實現(xiàn):

從上面的算法步驟中,我們來編寫代碼,并展示關(guān)鍵性的代碼。如下:

添加新資源:

public void offer(Object object) {if (object == null) {throw new NullPointerException();}CacheBean cacheBean = new CacheBean(object);cacheBean.setTimes(1);cacheBean.setLastVisitTime(System.currentTimeMillis());CacheQueue firstQueue = cacheQueueList.get(0);CacheBean pollObject = firstQueue.offer(cacheBean);if (pollObject == null) {return;}historyQueue.offer(pollObject);}

訪問某一資源:

public void visitting(Object object) {if (object == null) {throw new NullPointerException();}CacheBean cacheBean = new CacheBean(object);// 先在緩存隊列里找CacheBean tmpBean = null;int currentLevel = 0;boolean needUp = false;for (CacheQueue cacheQueue : cacheQueueList) {if (cacheQueue.contains(cacheBean)) {tmpBean = cacheQueue.get(cacheBean);if (tmpBean.getTimes() < timesDistance * (currentLevel + 1)) {tmpBean.setTimes(tmpBean.getTimes() + 1);cacheQueue.visiting(tmpBean);return;} else {tmpBean.setTimes(tmpBean.getTimes() + 1);cacheQueue.remove(tmpBean);needUp = true;}break;}currentLevel++;}// 是否需要升級if (needUp) {int times = tmpBean.getTimes();times = times > cacheQueueList.size() * timesDistance ? cacheQueueList.size() * timesDistance : times;CacheQueue queue = cacheQueueList.get(times / timesDistance);queue.offer(tmpBean);return;}// 如果數(shù)據(jù)在history中被重新訪問,則重新計算其優(yōu)先級,移到目標隊列的頭部if (historyQueue.contains(cacheBean)) {CacheBean reVisitBean = historyQueue.revisiting(cacheBean);reVisitBean.setTimes(reVisitBean.getTimes() + 1);System.out.println(reVisitBean);int times = reVisitBean.getTimes();times = times > cacheQueueList.size() * timesDistance ? cacheQueueList.size() * timesDistance : times;CacheQueue queue = cacheQueueList.get(times / timesDistance);CacheBean cb = queue.offer(reVisitBean);if (cb != null) {historyQueue.offer(cb);}return;}}

Ref:

http://flychao88.iteye.com/blog/1977653

http://www.cs.cmu.edu/~christos/courses/721-resources/p297-o_neil.pdf

http://flychao88.iteye.com/blog/1977642

GitHub源碼下載:

https://github.com/William-Hai/LRU-Cache

https://github.com/William-Hai/MultiQueue-Cache


總結(jié)

以上是生活随笔為你收集整理的操作系统:基于页面置换算法的缓存原理详解(下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。