博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CSAPP:cachelab(1)
阅读量:6133 次
发布时间:2019-06-21

本文共 4731 字,大约阅读时间需要 15 分钟。

  本项目大体上就是要求用C\C++来模拟cpu对cache的访问,然后统计hits、misses和eviction的次数。其实并没有想象中的那么难,感觉完全可以当成一道acm里面的大模拟题。。下面就对这个题目涉及到的一些知识点做下总结:

(一)linux命令行处理

  由于题目要求是在linux下以附加命令参数的方式来执行程序的,所以要对命令行的参数进行一下处理。这里要用到getopt函数,函数原型为:

int getopt(int argc,char** argv,const char* opstring);

我们都知道linux的命令参数分长参数和短参数,而这个函数是用来且只能用来处理短参数的,函数的返回值即为当前调用它所读取到的那个参数(int对应其ASCII码值),其中的opstring是一个短参数集合的字符串,形如:

const char* optstring = "hvs:t:b:E:";

其中每一个字母后面如果加一个冒号代表其必须带有一个附加的参数,不带就是必须没有附加的参数,而如果有两个冒号则是可带可不带参数,还有一点很重要,这些参数也就是字母的顺序是无所谓前后的,只要在这个字符串里就可以。

这个函数的调用要引用<getopt.h>库,该库还为我们提供了几个比较用帮助的全局变量,比较常见的有:optarg、opterror、optind,分别表示当前解析的参数的附加参数、解析是否出错(存在不能识别的opt即不在我们的optstring中,或者该加附带参数的opt没加不该加的加了)从而打印错误信息、下一个要解析的参数的index。我们可以根据自己的需要来利用并且手动的更改这些变量。

比如opterror=1的如果出错程序就会打印错误信息而当其等于0的时候程序就不会打印错误信息。我们通过optarg来获取每次解析参数所得到的附加参数,以字符串的形式返回!

关于命令行就总结这些,还有长参数的解析函数getopt_long(),详情可以去查看linux man page: http://linux.die.net/man/3/getopt_long

实现代码:

char* trace_file;    const char* optstring = "hvs:E:b:t";    char opt;    //deal with the short-option    while((opt=getopt(argc,argv,optstring))!=-1) {        switch (opt) {            case 'h':                printusage(argv);                break;            case 'v':                flag = 1;                 break;            case 's':                state.s = atoi(optarg);                break;            case 'E':                state.E = atoi(optarg);                break;            case 'b':                state.b = atoi(optarg);                break;            case 't':                trace_file = optarg;                break;            default :                printusage(argv);                break;        }    }

 

(二)Cache的初始化

  这部分其实就是通过编程语言的形式来“模拟”出cache的简单模型,即S=2^s个分组(set),每一组有E行,每行有B=2^b个存储单元,还有tag标记位和valid有效位,具体的图片如下图:

(图片来源:CSAPP.2E P305)

第一看这张图的时候一直看不懂,后来明白了上面得灰色部分是cache,而最下面哪一行长条是内存的一个地址,这个原理就是我们对内存的地址按照给定的参数s,E,b(其实还有个m,不过这题默认m=64)来划分成不同的块,从而借此来在cache中查找其是否存在,如果存在就是一个hit,直接在cache中提取从而节省访存的时间,反之就要将其载入cache然后在提取。

至于语言上的实现,就是设置几个结构体然后用malloc分配空间,或者用3维数组来实现应该也可以(不过数据量太大应该就不行了)

代码实现:

Cache init(int s,int E,int b){    int i,j;    int S = 1<
< S;i++) { set_t.l = (Line*)malloc(sizeof(Line)*E); cache_t.s[i] = set_t; for(j = 0;j < E;j++) { line_t.block = (int*)malloc(sizeof(int)*B); cache_t.s[i].l[j] = line_t; cache_t.s[i].l[j].flag = 0; cache_t.s[i].l[j].tag = 0; cache_t.s[i].l[j].used_time = 0; } } return cache_t;}

(三)核心部分:对内存的访问

关于从trace文件中获取的数据,每条有两个要注意,一是开头的命令字母,这点cmu在一开始的提示文档里也给我们指出来了:

注意这个‘M’是相当于两次访存!

第二个就是提供的address了,其实我们每次更新cache只要将address给传入到state_fresh函数就可以,然后根据给定的b、E、s来确定miss和hit的情况:

void state_fresh(Cache* cache,State* state,int ad,int cflag){    int i,j;    Set set_t;    Line line_t;    int cnt = getbi(ad);    int m = 0;    int set = 0,tag = 0;    //get set     for(i = cnt-state->b;i > cnt-state->b-state->s;i--)        set += bi[i]*(1<<(m++));    //get tag    m = 0;    for(i = cnt-state->b-state->s;i > 0;i--)         tag += bi[i]*(1<<(m++));

首先对于给定的address我们确定他如果在cache中则他必须在的set的序号和他所固有的tag值,计算出这两个值后剩下的就是在cache中的相应位置去查找即可:

首先给出hit的情况:

//search in cache    set_t = cache->s[set];    for(i = 0;i < state->E;i++) {        line_t = set_t.l[i];        if(line_t.flag==1 && line_t.tag==tag) {            state->hit++;            cache->s[set].l[i].used_time++;            if(cflag) printf("hit\n");            return;        }    }

如果set匹配,行匹配(flag=1且tag匹配),则标记位hit做好相应的处理工作,这题的关键点就是miss的情况,因为我们要分别讨论其是否要进行evict以及如何进行evict

还是先给出有空位的情况:

//miss-could be flag = 0 or tag not equal    state->miss++;    //get the max used number    int* used_num = (int*)malloc(2*sizeof(int));    int Min = getnum(&set_t,state,used_num);    //1.search for empty seats    for(i = 0;i < state->E;i++) {        line_t = set_t.l[i];        if(line_t.flag == 0) {            cache->s[set].l[i].flag = 1;            cache->s[set].l[i].tag = tag;            cache->s[set].l[i].used_time = used_num[1]+1;            if(cflag) printf("miss\n");             return;         }    }

getnum函数就是找到当前ad所必须在的set的所有行的最小值和最大值,分别存储在used_num[0]和used_num[1]中,最小值很好理解就是为了稍后的找不到空行时替换用的,而最大值则是为了将miss的ad载入cache时标记其优先级用的。

LRU(Least-Recently used)算法就是在evict时主要用到的,这里只是给出了他的低级实现,简单的记录一下优先级,更全的大家可以参考:http://flychao88.iteye.com/blog/1977653 里面从低级到高级的LRU都有讲解,还有java的实现

//2.evict    if(cflag) {        if(cache->s[set].l[Min].flag==1)             printf("miss ");        else             printf("miss\n");    }    if(cache->s[set].l[Min].flag==1) {        state->evict++;        if(cflag)             printf("eviction\n");    }    cache->s[set].l[Min].flag = 1;    cache->s[set].l[Min].tag = tag;    cache->s[set].l[i].used_time = used_num[1]+1;    free(used_num);

 

转载于:https://www.cnblogs.com/immortal-worm/p/5549014.html

你可能感兴趣的文章
醋泡大蒜有什么功效
查看>>
hdu 5115(2014北京—dp)
查看>>
数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)...
查看>>
PHP读取日志里数据方法理解
查看>>
第五十七篇、AVAssetReader和AVAssetWrite 对视频进行编码
查看>>
Vivado增量式编译
查看>>
一个很好的幻灯片效果的jquery插件--kinMaxShow
查看>>
微信支付签名配置正确,但返回-1,调不出支付界面(有的手机能调起,有的不能)...
查看>>
第二周例行报告
查看>>
如何使frame能居中显示
查看>>
第k小数
查看>>
构建之法阅读笔记三
查看>>
写给对前途迷茫的朋友:五句话定会改变你的人生
查看>>
并行程序设计学习心得1——并行计算机存储
查看>>
JAVA入门到精通-第86讲-半双工/全双工
查看>>
bulk
查看>>
js document.activeElement 获得焦点的元素
查看>>
C++ 迭代器运算
查看>>
【支持iOS11】UITableView左滑删除自定义 - 实现多选项并使用自定义图片
查看>>
day6-if,while,for的快速掌握
查看>>