实验a
实验一是要我们实现一个模拟缓存。
做实验之前我们先要了解缓存的结构。见下图,图片来自cmu的ppt.实验ppt
一个内存地址会被分为3部分,t,s,b。而缓存的大小由s,E,b三个参数决定。缓存的组(set)数S=2^s,每组的行数=E,组中数据块大小为B=2^b。
实验的介绍文件中告诉我们可以忽略掉b偏移量这个参数(但你还是要接受这个参数,只是不用管他),并且告诉我们保证测试文件的size肯定不会超过块大小。因此实际上我们看一个缓存是命中就只用在意他的地址的s,和t在缓存中是否存在了。
写代码在csim.c中写。
实验的大体思路是这样的:
1.首先你要用getopt函数去接受参数(当然你也可以不用,但确实很方便)。接受完参数之后需要进行一些参数校验,比如s不能为0等等,对了如果参数中有-h是不用进行模拟的,直接输出帮助信息就好了。
2.模拟缓存的数据结构我采用的是二维数组,每一个缓存行我采用的是结构体(结构体的结构cmu的官方ppt已经给出提示了)
3.实验文件要求我们采用LRU算法,我这里用的是时间戳(ppt里面也有提示),大概就是需要淘汰时,淘汰时间戳值最大的,也就是最久没使用的。
4.c程序需要完成这样几个过程,首先接受参数,参数的格式可以去看traces文件夹下的文件,然后I开头的不用处理直接跳过,根据数据访问的类型去缓存里找,把传入的地址进行解析,得到对应的set号和tag位,遍历对应的set,如果有,那么hit++;没有的话miss++,然后看看有没有空行,有的话填入空行,没有空行的话遍历该set的所有行,找到counter最大的,进行替换,eviction++
最后附上代码
#include "cachelab.h"
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <stdio.h>
#include <limits.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
//缓存结构
typedef struct{
int valid_bit;//有效位
int tag; //标识位
int counter; //计数器,记录被访问了多少次
}Cache_line ,*Cache_set,**Cache;
//接受输入参数
int h = 0; //0表示未传入
int v = 0;
int s = 0; //0表示set大小为0
int E = 0; //表示number of lines per set
int b = 0; //B=2^b
char t[1000];
int S = 0;
int B = 0;
//缓存
Cache cache = NULL;
//存储结果
int hit = 0;
int miss = 0;
int eviction = 0;
void printTips(){
printf("Usage: ./csim-ref [-hv] -s <num> -E <num> -b <num> -t <file>\n"
"Options:\n"
" -h Print this help message.\n"
" -v Optional verbose flag.\n"
" -s <num> Number of set index bits.\n"
" -E <num> Number of lines per set.\n"
" -b <num> Number of block offset bits.\n"
" -t <file> Trace file.\n\n"
"Examples:\n"
" linux> ./csim-ref -s 4 -E 1 -b 4 -t traces/yi.trace\n"
" linux> ./csim-ref -v -s 8 -E 2 -b 4 -t traces/yi.trace\n");
}
//初始化缓存
void init_Cache(){
cache = (Cache)malloc(S*sizeof(Cache_set));
for(int i =0;i<S;i++){
cache[i] = (Cache_set)malloc(E*sizeof(Cache_line));
for(int j=0;j<E;j++){
cache[i][j].valid_bit = 0;
cache[i][j].tag = -1;
cache[i][j].counter = 0;
}
}
}
int getParams(int argc,char * argv[]){
int ch = 0;
while ((ch = getopt(argc, argv, "hvs:E:b:t:")) != -1){
switch (ch)
{
case 'h':
h = 1;
break;
case 'v':
v = 1;
break;
case 's':
s = atoi(optarg);
break;
case 'E':
E = atoi(optarg);
break;
case 'b':
b = atoi(optarg);
break;
case 't':
strcpy(t, optarg);
break;
default:
return 0;
}
}
return 1;
}
void deal_inCache(unsigned address){
//获取组号
unsigned mask = 0xffffffffffffffff >> (64 -s);
int s_num = (address >> b) & mask;
//获取tag
mask = 0xffffffffffffffff >> (s + b);
int tag = (address >> (s+b)) & mask;
//查看是否在缓存当中
for(int i=0;i<E;i++){
if(cache[s_num][i].valid_bit==1&&cache[s_num][i].tag == tag){
//说明缓存命中
hit++;
cache[s_num][i].counter = 0;//重置计数器
return;
}
}
//执行到这说明缓存不命中
miss++;
//查看是否有空行
for(int i=0;i<E;i++){
if(cache[s_num][i].valid_bit==0){
cache[s_num][i].tag = tag;
cache[s_num][i].valid_bit = 1;
return;
}
}
//执行到这说明没有空行需要进行驱逐
eviction++;
int max = cache[s_num][0].counter;
int max_index = 0;
for(int i=1;i<E;i++){
if(cache[s_num][i].counter > max){
max = cache[s_num][i].counter;
max_index = i;
}
}
//进行驱逐
cache[s_num][max_index].tag = tag;
cache[s_num][max_index].valid_bit = 1;
cache[s_num][max_index].counter = 0;
}
void update_timeCounter(){
for(int i=0;i<S;i++){
for(int j=0;j<E;j++){
if(cache[i][j].valid_bit==1){
cache[i][j].counter++;
}
}
}
}
void simulate(){
FILE* file = NULL;
file = fopen(t,"r");
if(file == NULL)
{
printf("open error");
exit(-1);
}
char type = '0';
unsigned address = 1;
int size = 0;
while(fscanf(file," %c %x,%d",&type,&address,&size)>0){
switch(type){
case 'L':
deal_inCache(address);
break;
case 'M':
deal_inCache(address);
case 'S':
deal_inCache(address);
}
update_timeCounter();
}
}
int main(int argc,char * argv[])
{
//读取参数
int result = getParams(argc,argv);
if(!result || h==1 || s<=0||E<=0||b<=0){ //如果参数有不在参数列表的或者用户输入了-h或者参数不正确,给出参数输入提示
printTips();
return 0;
}
//参数无误进行初始化操作
S = 1 << s;
B = 1 << b;
init_Cache();
simulate();
printSummary(hit, miss, eviction);
return 0;
}