《深入理解计算机系统》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

  • 明确这只是个简易模拟器,并不需要完全模拟

  • 首先是使用到的库,主要使用的函数或者数据类型见注释

  • 友情链接:getopt()以及cmu给的更多提示

    #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的主要内容,如果未能解决你的问题,请参考以下文章

关于《深入理解计算机系统》一书

深入理解计算机系统之信息的存储和处理

求《深入理解计算机系统(第三版)》的pdf中文版

哪里可以买到<<深入理解计算机系统>>中文版

学习日记之《深入理解计算机系统》

学习日记之《深入理解计算机系统》