csapp:cache lab

这周开始看《深入理解计算机原理》的第六章。学习了局部性定理和高速缓存工作原理的相关知识。看完书后需要实践来巩固和加深理解。
本次实验共分为两个部分,Part A是要求实现一个高速缓存模拟器,计算出高速缓存命中、未命中和驱逐的次数;Part B是要求使用分块的方法降低矩阵转置时的高速缓存miss次数。

Part A

lab文件夹中给了我们一个参考程序,首先来看看这个程序的运行效果:在这里插入图片描述
执行指令为:

./csim-ref -v -s 2 -E 4 -b 4 -t traces/yi.trace

-v:有这个选项会输出每一步的具体操作,如上图所示,无该选项时,只会输出最终计数结果:hits:5 misses:4 evictions:0
-s:设置高速缓存的组数为2^s。
-E:设置高速缓存每组中有E行。
-b:设置高速缓存每行中块大小为2^b。
-t:设置输入文件路径。

现在让我们来看一看输入文件 traces/yi.trace:
在这里插入图片描述
可以看到输入文件由一行一行的指令组成,格式为:<操作> <地址>,<字节数>,其中L代表load,S代表save,M代表modify(相当于先load后save)。比如第一行,表示从地址10(十六进制地址) load一个字节到高速缓存。
现在我们来看一下,参考程序的运行结果:
在这里插入图片描述
分析一下,高速缓存的工作原理,首先我们创建的高速缓存有4个组,每个组有4行,每行16个字节,结构如下:
在这里插入图片描述
高速缓存与地址的对应关系为:
在这里插入图片描述
现在来看第一条指令:
L 10,1
因为高速缓存为空,必定miss,miss后将所需值取到高速缓存中。地址10二进制为0…00010000。我们创建的高速缓存b=4,s=2,根据上述地址与高速缓存的映射关系可知,该数据存到cache中的01组,又因为01组中都为空行,所以随便找一组空行存储,并将该行有效位置1,代表有数据存储。因为每行中块大小为16Bytes,所以与地址0x10一同存放在块中的还有地址0x11、0x12、0x13…0x1f。
第二条指令:
M 20,1
0x20二进制位0…00100000,应该映射到cache的02组中,通过检查02组中并没有这个数据,选择将其取进02组中,并将对应行置1,注意,每次从主存取值都按照块大小取,所以该行中存储0x20、0x21…0x2f共16字节数据。M相当于两次操作,load进cache后还要进行一次save操作。因为之前已经将数据存入cache了,所以随后的save操作命中。
第三条指令:
L 22,1
到这一条指令,就能够体现出高速缓存的作用了,通过映射关系,找到0x22应该在cache的02组中,所有在对应组中查找该数据,发现该数据恰好在02组中(在第二条指令执行时取入cache),缓存命中。
第四条指令:
S 18,1
与第三条相似,通过映射关系,在第一组中查找,发现该数据刚好在缓存中,缓存命中。

所有指令执行完成后,共计命中5次,未命中4次。

现在开始编写我们自己的程序。
整个程序可以简化为一下几个过程:
1.读取命令行指令
2.读取文件获得操作地址
3.将地址解析成组号,标志位。
4.操作cache对应行并计数。

#include "cachelab.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
static int miss=0,hit=0,eviction=0;
static int flag=0;
struct Line//每一行的数据
{
    int valid;//有效位
    int tag;//标志位
    int recent;//计数,用于删除最长时间没有使用过的行
};
struct Group//组
{
    struct Line *line;
};
struct Cache//缓存
{
    int Gnum;//缓存组数
    int Lnum;//每一组行数
    int Block;//块大小
    struct  Group* group;
};
int strtoint(char* str)//字符转数字
{
    int len=strlen(str);
    int res=0;
    for(int i=0;i<len;i++)
    {
        res=res*10+(str[i]-'0');
    }
    return res;
}
long long hexstrtoint(char* str)//16进制字符串转数字
{
    int len=strlen(str);
    long long res=0;//保存地址值,注意用int会溢出
    for(int i=0;i<len;i++)
    {
        if(str[i]>='0'&&str[i]<='9')
            res=res*16+(str[i]-'0');
        if(str[i]>='A'&&str[i]<='F')
            res=res*16+(str[i]-'A'+10);
        if(str[i]>='a'&&str[i]<='f')
            res=res*16+(str[i]-'a'+10);
    }
    return res;
}
void cache_init(int S, int E,int B,struct Cache* cache)//初始化cache
{
    (*cache).Gnum=S;
    (*cache).Lnum=E;
    (*cache).Block=B;
    (*cache).group=(struct Group*)malloc(sizeof(struct Group)*S);
    for(int i=0;i<S;i++)
    {
        (*cache).group[i].line=(struct Line*)malloc(sizeof(struct Line)*E);
        for(int j=0;j<E;j++)
        {
            (*cache).group[i].line[j].valid=0;
            (*cache).group[i].line[j].tag=0;
            (*cache).group[i].line[j].recent=0;
        }
    }
}
void analysis(char* record,char* op,char* add,char* size)//解析文件中每一行指令
{
    int i=0,j=0;
    while(record[i]==' ')
        i++;
    *op=record[i];
    i++;
    for(;record[i]!=',';i++,j++)
    {
        add[j]=record[i];
    }
    add[j]='\0';
    j=0;
    i++;
    for(;record[i]!='\n';i++,j++)
    {
        size[j]=record[i];
    }
    size[j]='\0';
    //printf("%c,%s,%s",op,add,size);
    
}
void operation(struct Cache* cache,char* Add,char* Size,int rec)//操作cache并计数
{
    long long  add=hexstrtoint(Add);
    int GrNumber=(add/cache->Block)%(cache->Gnum);
    long long  Tag=(add/cache->Block)/(cache->Gnum);
	int ValidNum=-1;
    for(int i=0;i<cache->Lnum;i++)
    {
		if(cache->group[GrNumber].line[i].valid==0 && ValidNum<0)
		{
			ValidNum=i;
		}
        if(cache->group[GrNumber].line[i].valid==1)//check valid
        {
			if(cache->group[GrNumber].line[i].tag==Tag)//check tag
			{
				
				if(flag==1)
					printf("hit ");
				hit++;
				cache->group[GrNumber].line[i].recent=rec;
				return ;
			}
        }
    }
	if(flag==1)
		printf("miss ");
	miss++;
	if(ValidNum>=0)//sheng yu kong hang
	{
		cache->group[GrNumber].line[ValidNum].valid=1;
		cache->group[GrNumber].line[ValidNum].tag=Tag;
		cache->group[GrNumber].line[ValidNum].recent=rec;
		return ;
	}
	else
	{
		if(flag==1)
			printf("eviction ");
		eviction++;
		int deleteNum=0;
		int recCount=cache->group[GrNumber].line[0].recent;
		for(int i=0;i<cache->Lnum;i++)
		{
			if(cache->group[GrNumber].line[i].recent<recCount)
			{
				recCount=cache->group[GrNumber].line[i].recent;
				deleteNum=i;
			}
		}
		cache->group[GrNumber].line[deleteNum].tag=Tag;
		cache->group[GrNumber].line[deleteNum].recent=rec;
		return ;
	}
        
}

int main(int argc,char* argv[])
{
    char buf[50];
    struct Cache cache;
	int count=0;
    int s,S,E,b,B;
    FILE* fd;
    for(int i=0;i<argc;i++)//获取命令行参数,getopt函数更好用
    {
        if(argv[i][0]=='-')
        {
			if(argv[i][1]=='v')
			{
				flag=1;
			}
            if(argv[i][1]=='s')
            {
                i++;
                s=strtoint(argv[i]);
                S=(1<<s);
                i++;
                
            }
            if(argv[i][1]=='E')
            {
                i++;
                E=strtoint(argv[i]);
                i++;
            }
            if(argv[i][1]=='b')
            {
                i++;
                b=strtoint(argv[i]);
                B=(1<<b);
                i++;
            }    
            if(argv[i][1]=='t')
            {
                i++;
                if((fd=fopen(argv[i],"r"))==NULL)
                {
                    printf("fopen %s error\n",argv[i]);
                    exit(1);
                }
            }     
        }
    }
    cache_init(S,E,B,&cache);
    while(fgets(buf,sizeof(buf),fd))//按行读取文件
    {
		count++;
		char op;
		char add[50];
		char size[50];
		analysis(buf,&op,add,size);
		if(op=='I')//I操作不操作cache
			continue;
		if(flag==1)
			printf("%c %s,%s ",op,add,size);
		operation(&cache,add,size,count);//L和S操作事实上都一样,都只操作一次缓存
		if(op=='M')//M操作两次缓存
			operation(&cache,add,size,count);
		if(flag==1)
			printf("\n");
		
    }
    printSummary(hit,miss,eviction);
    return 0;
}

测试结果如下:
在这里插入图片描述
可以看到跟参考程序结果一致。
不过比起参考程序还有很多不足,比如并不能识别命令行参数是否合法,我的程序当命令行参数不合法时,很有可能会因为数组越界导致段错误。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值