一、实验目的及要求
1.掌握 FIFO 算法原理
2.掌握 LRU 算法的原理。
3.通过编写 C 程序,实现定 FIFO 和 LRU 算法。
实践知识:
C语言基础知识
二、实验环境
计算机:
开发工具:C-Free 5
三、实验内容
当新的一块数据需要装入 Cache 时,原来存储的一个 Cache 行必 须被替换掉。对于直接映射方式,某个特定的块只能有一个对应的行, 因此不需要对将被替换的 Cache 行进行选择。而对于全相联映射方 式,由于有多个 Cache 行都可作为候选替换行,如何在这些 Cache 中
进行选择,从而提高 Cache 的命中率就需要引入替换策略。
1、先进先出算法
在发生替换时,先进先出算法(FIFO)把最先调入的那一个 Cache 行替换出去。FIFO 算法用循环或环形缓冲很容易实现,但是一般来说,采用先进先出算法也不能得到很高的 Cache 命中率。
2、最近最少使用算法
最近最少使用算法(LRU)统计那一个 Cache 行是近段时间内使 用次数最少的 Cach 行,需要替换时就将它替换出去。实际情况证明, 最近最少使用算法是一个比较好的替换算法,采用该替换算法能够得到较高的 Cache 命中率。
#include <stdio.h>
#include <stdlib.h>// malloc() free()
#define num1 4
#define num2 8
typedef struct { // 物理块结构
int number;
int time;
} Block;
void FIFO(int arr[num2], int size) {//先进先出算法
int* blocks = (int*)malloc(num1 * sizeof(int));// 物理块数组
int blockCount = 0;// 物理块数量
int currentIndex = 0;// 当前物理块索引
for (int i = 0; i < size; i++) {// 遍历输入数组
int found = 0;// 物理块是否找到
for (int j = 0; j < blockCount; j++) {// 遍历物理块数组
if (blocks[j] == arr[i]) {
found = 1;
printf("输入的%d物理块号存在,命中并覆盖原来的\n", blocks[j]);
break;
}
}
if (!found) {// 未找到物理块
if (blockCount < num1) {
blocks[blockCount] = arr[i];
printf("cache块未满,直接添加物理块号%d\n", blockCount + 1);
blockCount++;
}
else {// 物理块数量大于等于4,替换最早进入的块
blocks[currentIndex] = arr[i];
printf("cache块已满,替换物理块号%d\n", currentIndex + 1);
currentIndex = (currentIndex + 1) % num1;// 循环移动
}
}
}
// 打印结果
printf("FIFO替换结果:");
for (int i = 0; i < num1; i++) {
printf("%d", blocks[i]);// 物理块数组
}
printf("\n");
free(blocks);// 释放内存
}
void LRU(int arr[num2],int size) {//近期最少使用算法
Block* blocks = (Block*)malloc(num1 * sizeof(Block)); // 物理块数组
int blockCount = 0;// 物理块数量
for (int i = 0; i < size; i++) {
int found = 0;
for (int j = 0; j < blockCount; j++) {
if (blocks[j].number == arr[i]) { // 找到物理块
found = 1;
printf("输入的%d物理块号存在,命中并覆盖原来的\n", blocks[j]);
blocks[j].time = 0;
}
else {
blocks[j].time++;
}
}
if (!found) { // 未找到物理块
if (blockCount < num1) {
blocks[blockCount].number = arr[i];// 物理块数量小于4,直接添加
printf("cache块未满,直接添加物理块号%d\n", blockCount + 1);
blocks[blockCount].time = 1;// 时间置1
blockCount++;
}
else { // 物理块数量大于等于4,替换最久未使用块
int maxTime = 0;
int replaceIndex = 0;
for (int k = 0; k < num1; k++) {
if (blocks[k].time > maxTime) {
maxTime = blocks[k].time;
replaceIndex = k;
}
}
blocks[replaceIndex].number = arr[i];// 物理块数量大于等于4,替换最久未使用块
printf("cache块已满,替换物理块号%d\n", replaceIndex + 1);
blocks[replaceIndex].time = 1;//时间置1
}
}
}
// 打印结果
printf("LRU替换结果:");
for (int i = 0; i < num1; i++) {
printf("%d", blocks[i].number);
}
printf("\n");
free(blocks);
}
void LFU(int arr1[num2], int size) {//最不经常使用算法
int* blocks = (int*)malloc(num1 * sizeof(int));// 物理块数组
int blockCount = 0;// 物理块数量
int* count = (int*)malloc(num1 * sizeof(int));// 物理块使用次数数组
int currentIndex = 0;// 当前物理块索引
for (int i = 0; i < size; i++) {// 遍历输入数组
int found = 0;
for (int j = 0; j < blockCount; j++) {// 遍历物理块数组
if (blocks[j] == arr1[i]) {
found = 1;
printf("输入的%d物理块号存在,命中并覆盖原来的\n", blocks[j]);
count[j]++;
break;
}
}
if (!found) {// 未找到物理块
if (blockCount < num1) {
blocks[blockCount] = arr1[i];
printf("cache块未满,直接添加物理块号%d\n", blockCount + 1);
count[blockCount] = 1;
blockCount++;
}
else {// 物理块数量大于等于4,替换最少使用次数的块
int minCount = 10;
int replaceIndex = 0;
for (int k = 0; k < num1; k++) {// 遍历物理块数组//替换规则修改FIFO递归
if (count[k] < minCount) {
minCount = count[k];
replaceIndex = k;
}
}
blocks[replaceIndex] = arr1[i];
printf("cache块已满,替换物理块号%d\n", replaceIndex + 1);
count[replaceIndex] = 1;
}
}
}
// 打印结果
printf("LFU替换结果:");
for (int i = 0; i < num1; i++) {
printf("%d", blocks[i]);
}
printf("\n");
free(blocks);
free(count);
}
int main() {
int arr[num2];
int i = 0;
printf("请输入8个物理块号\n");
for (i = 0; i < 8; i++)
scanf("%d", &arr[i]);
FIFO(arr, 8);
LRU(arr, 8);
LFU(arr, 8);
return 0;
}
实验结果分析:
Cache调度算法中:
- FIFO算法先进去的cache行总是先被替换且cache的命中率不高。
- LRU算法总是将近期最久未被访问的cache行给替换出去且cache的命中率高。
3.LFU算法总是将访问次数最少的cache行替换,但是不能严格反应近期访问情况。
心得体会:
1.模块化设计:我将整个程序划分为多个模块,每个模块负责不同的功能,比如模拟3种替换算法和cache输出来提高代码的可读性和可维护性。
2.优化性能:在代码实现过程中,我注意各个替换算法函数内部到一些可以优化的地方,比如减少不必要的内存访问、减少循环次数,以提高程序的性能。
3.更加熟悉算法原理:将算法转化为代码实现后,对Cache调度算法的原理和思想有了更加深刻的了解,提高了自己的编程经验和独立解决问题的能力。
改进意见:1.可以通过改变增大cache块的物理块数和输入的物理块号数量进行扩展实验。
2.可以将此次cache的调度算法与其地址映射的三种方式(全相联映射,直接相联映射,组相联映射)结合起来进行实验的扩展。