4 种 cache 替换策略对比
4 种 cache 替换策略对比
RAND, FIFO, LRU, LFU
实验参考条件
参考的是我电脑上的 i7-10750H ,根据《Intel Core i7-10750H Processor 12M Cache up to 5.00 GHz 产品规范》显示,这款 CPU 的缓存容量大小为 12MB ,行大小为 64 Bytes,考虑到其有 6 个相同大小的核心,实际运行简单程序时,基本上是单核心工作,我们将其进行平分为每个核 2MB (实际上 intel 处理器缓存不止一层,实验为了简化模型,只采取一层模型)
个人考虑了 5 种常见的使用情况
- linar array random query / 线性表随机访问
- 2-demension random query / 二维随机访问
- binary tree query / 二叉树式数据结构询问
- DP trans / DP转移
- Graph DFS / 图DFS遍历
根据根号分块原理,我们采取 128 路组相联映射方式
实验结果
以下图片都是 gif ,可以点开播放
数据量均在 \(10^6\) ~ \(8 \cdot 10^6\) 个地址左右(约15MB ~ 45MB)
1-2 随机访问
结果相差不大
|Cache 命中率|RAND|FIFO|LRU|LFU|
|---|---|---|---|---|---|
|一维|25.71|25.99|26.00|25.91|
|二维|25.69|25.97|25.96|25.96|
二叉树式数据结构
局部性原理在此体现的较为突出
二叉树式数据结构能维护 set, map, heap 等信息,是极为常见的数据结构,故 LRU, LFU 还是体现出了较大的优越性
||RAND|FIFO|LRU|LFU|
|---|---|---|---|---|---|
|Cache 命中率|90.29|90.49|91.81|92.76|
DP 转移
由于 DP 转移主要在时间上较为饱和,占用空间普遍不多,测试了多种 DP 代码,cache 命中率普遍高于 99%
||RAND|FIFO|LRU|LFU|
|---|---|---|---|---|---|
|Cache 命中率|99.33|99.50|99.50|100.00|
图的 DFS
||RAND|FIFO|LRU|LFU|
|---|---|---|---|---|---|
|Cache 命中率|7.52|7.72|7.72|7.69|
由于 DFS 耗时最大的主要是栈递归,在空间使用分布上极为稀疏,cache 命中率普遍较低
总结
在 cache 缓存映射方式较为合理、缓存大小较大等条件下,四种替换策略差距并不是很大,考虑对 cache 空间的额外占用, LFU 虽然在几种情况下表现出了一定的时间上的优越性(主要在二叉式数据结构上),但结合空间上的占用,总体可能并没有明显的优势。
总图表变化(随机替换策略全程垫底)
附代码
"""
RAND, FIFO, LRU, LFU
i7-10750H cache size 12MB = 12,582,912 Bytes (3,145,728 integers) 3e6
6 cores
2MB cache per core
1. linar array random query
2. 2-demension random query
3. binary tree query
4. DP trans
5. Graph DFS
coherency_line_size = 64 Bytes = 16 integers
Cache: 2,097,152 Bytes --- 32,768 Blocks
Memory: 17,179,869,184 Bytes --- 268,435,456 Blocks
use "128-set-associative mapping" # 256 * 128
data size: about 2e6
"""
import random
import time
# data test, done
def test(flag):
st = time.time()
lst = []
tot = []
for i in range(0,256):
lst.append([[0,0,0]])
tot.append(0)
for j in range(1,128):
lst[i].append([0,0,0])
Tm = 0
cached = 0
uncached = 0
with open(f'out{2}.txt','r') as fio:
content = fio.read().rstrip(',')
# datas = [eval('0x4bb7318'),eval('0x4bb7318'),]
datas = [int(n,16) for n in content.split(',')]
for address in datas:
address >>= 6
thesign = address % 256
address <<= 6
lowC = [0,lst[thesign][0][1]]
lowT = [0,lst[thesign][0][2]]
isFound = False
for i in range(0,128):
thiz = lst[thesign][i]
if thiz[0] == address:
thiz[1] += 1
thiz[2] = Tm
isFound = True
if lowC[1] > thiz[1]:
lowC[0] = i
lowC[1] = thiz[1]
if lowT[1] > thiz[2]:
lowT[0] = i
lowT[1] = thiz[2]
if isFound:
cached += 1
else:
chosen = 0
if flag == 0:
chosen = int(random.random() * 128)
elif flag == 1:
chosen = tot[thesign]
tot[thesign] += 1
tot[thesign] %= 128
elif flag == 2:
chosen = lowT[0]
else:
chosen = lowC[0]
lst[thesign][chosen] = [address,1,Tm]
uncached += 1
Tm += 1
print(f'{cached / (cached + uncached) * 100:.2f}%')
test(3)
# out1.txt RAND 25.71%
# out1.txt FIFO 25.99%
# out1.txt LRU 26.00%
# out1.txt LFU 25.91%
"""
const data = [0,0,0,0,0]
const data = [25.71,25.99,26.00,25.91,23]
const data = [25.69,25.97,25.96,25.96,23]
const data = [90.29,90.49,91.81,92.76,88]
const data = [99.33,99.50,99.50,100.00,97]
const data = [7.52,7.72,7.72,7.69,5]
"""
# out3.txt RAND 90.29%
# out3.txt FIFO 90.49%
# out3.txt LRU 91.81%
# out3.txt LFU 92.76%
# out4.txt RAND 99.33%
# out4.txt FIFO 99.50%
# out4.txt LRU 99.50%
# out4.txt LFU 100.00%
"""# out4.txt RAND 99.97%
# FIFO 99.97%
# LRU 99.97%
# LFU 99.97%"""
# 5 7.52%
# 7.72%
# 7.72%
# 7.69%
"""js
const data = [0,0,0,0,0];
const next = [[0,0,0,0,0],[25.71,25.99,26.00,25.91,23],
[25.69,25.97,25.96,25.96,23],
[90.29,90.49,91.81,92.76,88],
[99.33,99.50,99.50,100.00,97],
[7.52,7.72,7.72,7.69,5]];
var cnt = 0;
// 123a = 5;
option = {
xAxis: {
max: 'dataMax',
min: 'dataMin'
},
yAxis: {
type: 'category',
data: ['RAND', 'LIFO', 'LRU', 'LFU',''],
inverse: true,
animationDuration: 300,
animationDurationUpdate: 300,
max: 3, // only the largest 3 bars will be displayed
axisLabel: {
show: true,
fontSize: 20
},
},
series: [
{
realtimeSort: true,
name: 'X',
type: 'bar',
data: data,
label: {
show: true,
position: 'right',
valueAnimation: true
}
}
],
legend: {
show: true
},
animationDuration: 0,
animationDurationUpdate: 3000,
animationEasing: 'linear',
animationEasingUpdate: 'linear'
};
function run(cnt) {
for (var i = 0; i < data.length; ++i) {
data[i] = next[cnt][i];
}
myChart.setOption({
series: [
{
type: 'bar',
data
}
]
});
}
setTimeout(function () {
run();
}, 0);
function sleep(time){
var timeStamp = new Date().getTime();
var endTime = timeStamp + time;
while(true){
if (new Date().getTime() > endTime){
return;
}
}
}
setInterval(function () {
cnt += 1;
cnt %= 6;
run(cnt);
// sleep(1000);
}, 6000);
"""