《深入理解计算机系统》CSAPP_CacheLab
Posted duile
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《深入理解计算机系统》CSAPP_CacheLab相关的知识,希望对你有一定的参考价值。
CacheLab
开始日期:21.12.25
操作系统:linux
调试工具:valgrind
Part A
pre-knowledge
如上图所示,我们需要理清楚organization(组织)和address(地址)的区别:
organization表明cache(高速缓存)是如何组织的;
address表明指令的位置,当cpu需要执行某指令(instruction)时(instruction被存储在cache组织中),我们通过address找到这个指令。
同第一张图一起理解所给的参数(parameter),大小写不同时所对应的意思也是不同。
那么我们可以知道writeup.pdf所给的例子:
linux> ./csim-ref -s 4 -E 1 -b 4 -t traces/yi.
tracehits:4 misses:5 evictions:3
等价于:(s, E, b)中的数值为:2^4 sets, 1 line per set, block of 4 bytes per line
(此处用英文来理解会更好,address的位数默认为32bits)
从cache中读取指令,采用的策略是LRU(Least Recently Used),该策略可以看作一个算法,简单来说,该算法是将最近最少使用的line重置,实现可以用累加器(时间戳)和队列,这里使用的是累加器(counter),具体代码实现见下文。
AC code
-
明确这只是个简易模拟器,并不需要完全模拟
-
首先是使用到的库,主要使用的函数或者数据类型见注释
-
#include "cachelab.h" //printSummary() #include<stdio.h> //fopen() fclose() fscanf() #include<getopt.h> //getopt() #include <stdlib.h> //malloc() #include <string.h> //strcpy() #include <stdbool.h> //bool
-
然后是数据结构和一个结构体全局变量
- 参数
Args
是服务于linux指令的(eg../csim-ref -s 4 -E 1 -b 4 -t traces/yi.
) Line, Set, Sets
是用来构建Cacha的(在之后的函数部分会给出如何构建的简易图示)Result
是我们最终输出结果,用结构体表示能节省空间
typedef struct bool h; bool v; int s; int E; int b; char t[50]; Args; typedef struct int miss; int hit; int eviction; Result; typedef struct bool valid; int tag; int counter; // use LRU by countering numbers of valid Line, *Set, **Sets; // set == one ground of lines, all sets == one cache Result result;
- 参数
-
函数声明和主函数调用内容
- 步骤是:先根据linux指令初始化参数,再根据参数构建Cache(sets),然后解析得出结果(解析过程会用到get()函数),最后打印结果
Args initArgs(int argc, char* const argv[]); Sets createCache(Args args); void parse(Sets sets, Args args); void printSummary(int hits, int misses, int evictions); void get(Sets sets, Args args, unsigned int address); int main(int argc, char* const argv[]) Args args = initArgs(argc, argv); // initialize arguments from linux command(hvs:E:b:t:) Sets sets = createCache(args); // initialize one Cache by arguments parse(sets, args); // parse Cache and get result printSummary(result.hit, result.miss, result.eviction); //print result return 0;
-
接下来进入函数部分
-
首先是初始化参数这里用到了getopt函数(友情链接:getopt()以及cmu给的更多提示)以及归属于getopt的atol函数,该函数将参数转化为了长整型,还有复制文件名的strcpy函数。
- -h 和 -v指令是可以无视的,不影响评分,所以这里的具体代码只是服务linux指令,没有作用
Args initArgs(int argc, char* const argv[]) Args args; args.h = false; args.v = false; int opt; while (-1 != (opt = getopt(argc, argv, "hvs:E:b:t:"))) //argues the \'agrs\' one by one switch (opt) case \'h\': args.h = true; //help_message, but we don\'t have to write it break; case \'v\': args.v = true; //verbose mode(express detials), but we don\'t have to write it break; case \'s\': args.s = atol(optarg); // change s(int) to long type break; case \'E\': args.E = atol(optarg); break; case \'b\': args.b = atol(optarg); break; case \'t\': strcpy(args.t, optarg); //copy tracefile\'name to optarg break; default: args.h = true; break; return args;
-
根据参数构建Cache(Sets)
- 一个Cache即是一个Sets,由S个set组成的,而每一个set又是由E个line组成的,采用malloc即可构建
- 可以看到,一个Sets其实是以结构体line为元素组成的2维矩阵
Sets[S][E]
- 注意到Sets、set分别是二级指针、一级指针(更详细的解释可以看这的图示)
Sets createCache(Args args) int S = 1 << args.s; //S = 2^s int E = args.E; Sets sets = (Sets)malloc(sizeof(Set) * S); //build Sets(one Sets have S numbers of Set), 2D(row and colum) for (int i = 0; i < S; i++) sets[i] = (Set)malloc(sizeof(Line) * E); //build Set(one Set have E numbers of line), [i] is number of row for (int j = 0; j < E; j++) //build Line sets[i][j].valid = false; sets[i][j].tag = -1; //-1 == 0xffffffff sets[i][j].counter = 0; return sets;
-
这是一个简易Cache,
Sets[4][5]
// sets[4][5]: // Line Line Line Line Line // Line Line Line Line Line // Line Line Line Line Line // Line Line Line Line Line
-
解析得出结果
-
首先是parse函数
- 涉及到了fopen() fclose() fscanf() ,作用分别是打开,关闭,读入文件(链接:fopen)
- 这里读入的文件指令显然和linux指令不同,是从tracefile中读取的,给出了操作,地址和字节大小
I
,载入指令,在这个简易的cache simulator中没有对应的实际操作- 记得要释放内存
void parse(Sets sets, Args args) FILE* fp = fopen(args.t, "r"); //open a tracefile with \'r\'ead mode if (fp == NULL) printf("open error"); exit(0); //exit this process char operation; unsigned int address; int size; while(fscanf(fp, " %c %xu,%d", &operation, &address, &size) > 0) //write instrution from file, abandon \'I\' without [space] switch(operation) // we don\'t care \'I\' case \'L\': get(sets, args, address); break; case \'M\': // \'M\' equal two get() so we don\'t need \'break;\' get(sets, args, address); case \'S\': get(sets, args, address); default:; fclose(fp); // free store for (int i = 0, S = 1 << args.s; i < S;i++) free(sets[i]); free(sets);
-
然后是get函数
-
把一个文件指令给出的地址转化为我们要的index(组号)和tag,这个简易不需要用到data of block及block offset(具体转化过程见注释)
-
然后根据index(组号)和tag先找出对应的set(组),注意要和sets集合区别开来
-
一个set由许多line组成,一个line的构成见下:
//line ==> 0x|valid bit|tag bits|counter bits|
-
这里我用
set = sets[index]
表示哪一组,用set[j]
表示这一组哪一行 -
LRU的具体实现依靠counter(累加器),在只要valid有效counter就加一的操作之后,所有情况见下:
-
valid tag operation false unequal miss++,get tag,valid = 1,counter = 0 false equal miss++,get tag,valid = 1,counter = 0 true unequal eviction++,miss++,get tag,counter = 0 true equal hit++,counter = 0 -
这里需要特别注意的是,当valid有效且tag不相等时,我们就要找到目前该组counter最大的line舍去它,同时将其重置。
-
在这里,所谓的LRU,最近最少使用,counter最大就说明这个line离寄存器最近且最少使用
void get(Sets sets, Args args, unsigned int address) // mask ==> 0x|0...|1 of s bits| unsigned long mask = 0xffffffffffffffff >> (64 - args.s); //because it is 64bits computer and atol(optarg) // get index of set //address = 0x|t bits|s bits|b bits| ==> address = 0x|0...|t bits|s bits| address >>= args.b; int index = mask & address; //index = 0x|0...|s bits| int tag = address >> args.s; //tag = 0x|0...|t bits| Set set = sets[index]; //LRU int j; int E = args.E; //counter == valid_counter for (j = 0; j < E; j++) if (set[j].valid == true) set[j].counter++; //valid and equaling of tag so hit++ for (j = 0; j < E; j++) if (set[j].valid == true && set[j].tag == tag) set[j].counter = 0; result.hit++; return; //invalid so miss++, change to valid and get tag bits(tag = 0x|0...|t bits|) for (j = 0; j < E; j++) if (set[j].valid == false) result.miss++; set[j].valid = true; set[j].tag = tag; return; //no equaling of tag so eviction++, evict line of max_counter(change to 0) and update tag //in the meantime, miss++ result.eviction++; result.miss++; int max_counter = set[0].counter; int max_j = 0; for (j = 1; j < E; j++) if (set[j].valid == true && set[j].counter > max_counter) max_j = j; max_counter = set[j].counter; set[max_j].tag = tag; set[max_j].counter = 0; return;
-
完成日期:21.12.28
参考链接:
CS:APP3e 深入理解计算机系统_3e CacheLab实验
《深入理解计算机系统》配套实验4: Cache lab
CSAPP实验之cachelab
ps:最近在练习写英文注释,有错误的话,敬请指正
Part B
请注意要安装valgrind
valgrind --version # 查看版本号检查
sudo -i # 进入root模式后才能安装
apt install valgrind # 安装
Continue...
以上是关于《深入理解计算机系统》CSAPP_CacheLab的主要内容,如果未能解决你的问题,请参考以下文章