😋 发布且更更新于个人小破站:进去瞅瞅
这篇文章侧重于实践部分,对于四个页面置换算法的概念描述一笔带过,不太清楚的可以先从课本上读一读相关概念,之后结合代码来理解。全文的叙事逻辑是从「代码撰写」的先后顺序展开的,先实现基础的功能,之后搭建整个算法的框架,然后实现并测试算法的核心功能,最后对结果进行分析以及思考。
1. 产生一个指令序列
实验要求以及实施办法
为了用来比较各个算法的优劣以及测试算法的准确度,需要首先创建出来一个指令序列,对于指令序列的要求如下:
- 50% 的指令是顺序执行的;
- 25% 的指令是均匀分布在前地址部分;
- 25% 的指令是均匀分布在后地址部分;
具体的实现的时候也是有一定的套路而言的:
- 在 [0,319] 的指令地址之间随机选取一起点 m;
- 顺序执行一条指令,即执行地址为 m+1 的指令;
- 在前地址 [0,m+1] 中随机选取一条指令并执行,该指令的地址为 m’;
- 顺序执行一条指令,地址为 m’+1 的指令
- 在后地址 [m’+2,319] 中随机选取一条指令并执行;
- 重复上述步骤 2~ 5,直到执行 320 次指令
上面的所有区间都是左闭右开的
指令序列的实现原理
从上面的步骤中可以看出,步骤 2 执行的指令相对于上一条指令是「顺序执行」,步骤 3 执行的指令相对于步骤 2 的 m+1 是「前地址」的指令,步骤 4 执行的指令相对于步骤 3 的 m‘ 是「顺序执行」,步骤 5 相对于步骤 4 的指令 m’+1 是「后地址」指令。
这里需要注意的是,在循环进行的时候,步骤 5 会产生一个新的 m 并执行,这样下一轮循环的步骤 2 就会在新的 m 后顺序执行,也就是执行 m+1 处的指令。下图中的箭头表示两个指令的先后关系。如:2 相对于 1 是顺序执行,3 相对于 2 是前地址指令。
这样在每次循环中会执行 4 条指令,其中 2 条是顺序执行,1 条是前地址部分,1 条是后地址部分,完美符合要求。
实现 320 条指令
根据上面的原理,编写代码:
def produceAddstream():
instruct = []
m = np.random.randint(0, 319)
# 每次循环生成 4 条,所以需要循环 80 次
for i in range(80):
instruct.append(m+1) # 顺序
n = np.random.randint(0, m+1)
instruct.append(n) # 前地址
instruct.append(n+1) # 顺序
m = np.random.randint(n+2, 319)
instruct.append(m) # 后地址
return instruct
同样,在写算法的过程中,这些随机变量虽然能够看到最终的效果,但是在调试的时候我们难以准确的知道自己算法的准确性,所以我还根据书本上的例子给出了一份测试数据(共计 20 条指令);
return [70, 0, 10, 20, 0, 30, 0, 40, 20, 30, 0, 30, 20, 10, 20, 0, 10, 70, 0, 10]
2. 算法整体思路
虚存的大小以及存放方式
假定页面大小为 1k,「用户虚存」容量为 32k,「用户内存容量」为 4 页到 32 页。在用户虚存中,按每 k 存放 10 条指令排列虚存地址,即 320 条指令在虚存中的存放方式为:
第 0 条 ~ 第 9 条指令为第 0 页(对应虚存地址为 [0,9]);
第 10 条 ~ 第 19 条指令为第 1 页(对应虚存地址为 [10,19]);
……
第 310 条 ~ 第 319 条指令为第 31 页(对应虚存地址为 [310,319]);
按以上方式,用户指令可组成 32 页。
所以可以直接使用 「指令 // 10」的方法来得到指令所在的页数;
对命中率的判定
使用「命中的次数」除以「指令的总数」来表示命中率,其中命中的意思是指当访问某个指令的时候,正好该指令对应的页在「用户内存」中;
程序一开始,创建 320 条指令,然后对于用户内存容量在 4 - 32 中的每一种情况下,测试四种算法( 最佳置换算法「OPT」、先进先出算法「IFO」、最近最久未使用页面置换「LRU」、最少使用页面淘汰算法「LFU 」)的命中率,最终绘制成图来展示;
def main(