S3-FIFO

S3-FIFO

本文作爲下一篇緩存文章的預備知識。

背景

基於LRU和FIFO的驅逐

FIFO和LRU都是經典的緩存驅逐算法,在過去幾十年中也出現了很多追求更高效率的驅逐算法,如ARC, 2Q, LIRS, TinyLFU。傳統觀點認爲,基於LRU的緩衝未命中率要低於基於FIFO的算法,如CLOCK,這類高級算法通常都是基於LRU的。但基於LRU的算法存在3個問題:1)每個對象需要兩個指針,對於包含小對象的負載會產生大量存儲開銷;2)由於在緩存命中時需要使用鎖來將請求的對象放到隊列首部,因此無法實現擴展;3)由於是隨機寫,閃存不友好。

可擴展的重要性

由於很多現代CPU都包含多個核,因此緩存的擴展性意味着它的吞吐量可以隨CPU核數的增加而增加。理想情況下,一個緩存的吞吐量和CPU核數呈線性關係。但在基於LRU的算法中,讀取操作需要加鎖才能更新元數據,因此無法完全利用CPU的計算能力。

FIFO的優勢

可以使用ring buffer來實現FIFO,無需爲每個對象分配指向元數據的指針,也無需在每次緩存命中時修改對象的位置,因此不存在可擴展瓶頸。另外FIFO按照先進先出的順序來驅逐對象,因此是一種閃存友好的訪問模式,可以減小閃存寫入以及閃存損耗。但FIFO在效率上落後於LRU和一些先進的驅逐算法。

one-hit wonders ratio

術語"one-hit-wonder ratio"指在一個序列(sequences)中,只被請求一次的對象所佔的比例,通常用於CDN中(其存在較大的one-hit-wonder ratio)。雖然one-hit-wonder ratio會因爲緩存負載的類型而有所變化,但我們發現越短的請求序列(shorter request sequences,即較少的對象)的one-hit-wonder ratio越高

這裏的請求序列可以看做是一個請求樣本

image

上面示例中,在序列長度爲17,包含5個對象的場景中,有1個對象(E)僅被訪問了一次,其one-hit-wonder ratio爲20%;而在序列長度爲7(1st~7st)的場景中,有2個對象(C,D)僅被訪問了一次,其one-hit-wonder ratio爲50%;類似地在序列長度爲4(1st~4st)的場景中,其one-hit-wonder ratio爲67%.

生產中的one-hit wonders ratio

那麼在實際生產中的表現是否和上面示例中一致?下圖展示了對來自MSR的一個塊緩存(hm_0) trace和來自Twitter的一個key-value 緩存的trace結果。X軸表示對象在trace中的比率(分別使用線性(左圖)和對數表示(右圖))。

可以看到完整trace的one-hit-wonder ratio(左圖X軸爲1.00的點)分別爲13%(Twitter)和38%(MSR),而包含10%對象的隨機子序列的one-hit-wonder ratio(右圖X軸爲10-1的點)分別爲26%(Twitter)和75%(MSR)。

image image

生產traces中的one-hit-wonder ratio。完整trace的one-hit-wonder ratio爲13%和38%,可以看到,序列越短,one-hit-wonder ratio越高

我們進一步分析了一個包含6594條trace的大型緩存trace集合,並在箱線圖中繪製了一次命中比例的分佈。完整trace的one-hit-wonder ratio的中位數爲26%,包含50% trace序列的one-hit-wonder ratio的中位數爲38%。此外,包含10%和1% trace序列的one-hit-wonder ratio的中位數分別爲72%和78%。

image

one-hit-wonder ratio的影響

我們在分析中使用的trace大部分是爲期一週,少部分是爲期一個月的。由於緩存大小通常遠小於trace的佔用空間(trace中的對象數量/字節數),因此在短序列場景下就可能會發生緩存驅逐。我們觀察發現,當緩存大小爲trace空間的10%時,大約有72%的對象在驅逐之前不會被再次使用。

image image

我們用緩存仿真進一步證實了觀測結果。上圖展示了驅逐對象的頻率。我們的trace分析表明,在Twitter的trace中,當序列爲trace空間的10%時,one-hit-wonder ratio爲26%,而仿真展示了類似的結果:被LRU逐出的對象中有26%在插入緩存(大小爲trace的10%)後沒有被請求。類似地,在MSR的trace中,當序列長度爲trace的10%時,one-hit-wonder ratio爲75%,而仿真中被LRU驅逐的對象中的82%沒有被再次使用。

很明顯,緩存應該過濾掉這些one-hit wonders,因爲它們佔用了空間,卻沒有帶來好處。

S3-FIFO:一個僅使用FIFO隊列的驅逐算法

受上面觀測結果的啓發,我們設計了一個新的緩存驅逐算法,稱爲S3-FIFO:簡單、使用三個靜態FIFO隊列的可擴展緩存(Simple, Scalable caching with three Static FIFO queues)。

image

S3-FIFO使用3個FIFO隊列:一個small FIFO隊列(S),一個main FIFO隊列(M),一個ghost FIFO隊列(G)。我們將S設置爲10%的緩存空間(實驗得出)。M爲90%的緩存空間,而G的大小和M相同。注意,當在ghost隊列中發現請求的數據時,此時並不算緩存命中,原因是ghost隊列並不保存數據。

  • 緩存讀:S3-FIFO中,每個對象使用兩個bits(freq)來跟蹤對象訪問狀態,上限爲3,緩存命中時自動加1。

  • 緩存寫:當插入一個對象時,如果G中沒有該對象,則插入S。當S滿時,位於S尾部的對象要麼會被轉移到M(訪問非0),要麼被轉移到G(訪問爲0),並在轉移之後清除訪問標記(freq)

    G滿時,它會按照FIFO順序驅逐對象。

    M使用一個類似 FIFO-Reinsertion的算法,但同時使用兩個bits來跟蹤訪問信息。至少對象的freq大於0,會被重新插入M首部,並將freq減1(freq-1)

image

添加對象的演示如下:

image
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章