深入理解计算机系统(CSAPP) 实验详解:CacheLab

近一段时间项目太忙导致没有继续,还好最近空下来一些,咱们继续冲!

更新历史

  • 20210104开始更新
  • 20210107完成实验一内容

本文介绍的是CSAPP书籍中的第四个lab: Cache lab。本实验要求我们实现一个软件高速缓存,以及优化矩阵。通过这个lab我们可以更好的理解缓存以及写出更加对缓存友好的代码。

知识预热

  1. 阅读《深入理解计算机系统》的6.4
  2. 仔细阅读Cache lab的writeup

介绍完上面必须的知识之外,我们就开始吧!

实验构成

本实验一共分为两个实验,第一个软件高速缓存模拟器,第二个优化矩阵。

实验一:
我们主要的操作流程
1.在csim.c里编写代码
2.使用make编译(注意需要将你的结果传入printSummary里)
3../test-csim-终端中检查结果

实验二:
我们主要的操作流程

实验开始

Cache simulator

需求描述:

  • 该实验中是不需要理会行中的数据的(也就是缓存块B=2^b),但是我们需要知道我们每行所占数据块的幂也就是b,需要用于计算t
  • 可配置的高速缓存,也就是说我们的S,E和B是可配置
  • 然后该实验要求我们实现下图的功能
    深入理解计算机系统(CSAPP) 实验详解:CacheLab_第1张图片

预备知识
我们可以把高速缓存看成是一个二维数组,它一共有S=2^s组,有E行,且每行包含一组信息,分别是有效标识,标识,数据块。
深入理解计算机系统(CSAPP) 实验详解:CacheLab_第2张图片
地址的构成是和高速缓存的结构相关的,例如我们有m位地址,那么中间的s位就是高速缓存中S=2^s的幂 。
(这里插入一个个人标注,我认为设计为2的次方的原因是好计算,因为我们使用位计算可以轻易的计算出2的n方)
深入理解计算机系统(CSAPP) 实验详解:CacheLab_第3张图片
缓存的设计
上面讲到我们把高速缓存当成二维数组来看,所以我们数据结构也是这样设计的。


// 行的结构
typedef struct 
{
	int vaild;
	u_int64_t tag;
	int count;
}line;

// 缓存
typedef struct
{
	int s;// 这里用小s的原因是我们输入的也是小s,因为得确保总数等于2的倍数
	int E;
	line* lines;// 指向lines数组的首位
}

// 结果,方便记录结果
typedef struct 
{
	int hitCount;
	int misCount;
	int eviCount;
}

初始化缓存,由于我们的缓存是动态的,所以我们需要动态分配内存

void initCache(int s,int E,cache* inCache)
{
		inCache->E = E;
		inCache->s = s;
		int numb = 1<<s * E;
		for(int i = 0 ; i<numb;i++)
		{
			inCache->lines = calloc(numb,sizeof(cache));// 2^s是行,E是列
		}
}

分配完内存,还有一个关键点就是如何获取t的信息和s的信息
我们先获取s的信息,我们在一个lab中就大量学习到了各种位操作,所以就在这里派上用场了,首先我们得先把我们要的数据移动到最右边,用一个掩码获取到我们要的信息就行了,翻译一下就是以下的代码

	u_int64_t sMask = (1<<s)-1;// 掩码就是需要的位得是1,不需要的位得是0,减一的意义就在于可以获取到一个全一的位例如00111111
	sIndex = (address>>b)&sMask;// 获取地址s位的信息,也就是缓存列的索引

下面一步就是判断是hit还是miss,然后还有另外一个状态是修改。
主要分为三步

  1. 判断有效位vaild为1,且tag和地址里的相同,这个断定为hit
  2. 若有效位vaild为0,那么直接判定为miss,同时更新缓存的信息
  3. 以上两种都不是那只能修改,同时修改也被记上一次miss
    翻译一下就是以下的代码
void missHit(int sIndex, u_int64_t tBit,cache *inCache,result *inResult)
{
    line *leastLine = &inCache->lines[sIndex];
    line *pLine = NULL;
    int minCount = 0;
    for (int i = 0; i < inCache->E; i++)
    {
        
        pLine = &inCache->lines[sIndex+i];

        // true && t = t
        if (pLine->valid && pLine->t == tBit)
        {
            printf("hit\n");
            ++pLine->count;
            ++inResult->hitCount;
            return;
        }

        // false
        if (pLine->valid == false)
        {
            printf("miss\n");
            pLine->valid = true;
            pLine->t = tBit;
            ++pLine->count;
            ++inResult->misCount;
            return;
        }
        
        // true
        leastLine = minCount >= pLine->count ? pLine : leastLine;
        minCount = minCount >= pLine->count ? pLine->count : minCount;
        
    }

    printf("replace\n");
    leastLine->t = tBit;
    ++leastLine->count;
    ++inResult->misCount;
    ++inResult->eviCount;
    return;
}

好了,主要代码就是这些,完整的代码在文末贴出~,-v懒得实现了,但是里面其实有打印的信息了。

#include "cachelab.h"
#include 
#include 

#include  // get param form command
#include  // get param form command

#define bool int
#define true 1
#define false 0
typedef unsigned long int u_int64_t;


typedef struct 
{
    bool valid;
    u_int64_t t;
    int count;
}line;

typedef struct 
{
    int s;
    int E;
    line *lines;
}cache;

typedef struct 
{
    int hitCount;
    int misCount;
    int eviCount;
    /* data */
}result;


void initCache(int s, int E, cache *inCache)
{
    inCache->E = E;
    inCache->s = s;
    int numb = 1 << s;
    numb = numb*E;
    inCache->lines = calloc(numb, sizeof(line)); //根据s,E可以访问到数组任意位置,E为列,2^s为行
}

void realseMemory(cache *inCache)
{
    int num = inCache->E * inCache->s;
    free(inCache->lines);
    free(inCache);
}

void missHit(int sIndex, u_int64_t tBit,cache *inCache,result *inResult)
{
    line *leastLine = &inCache->lines[sIndex];
    line *pLine = NULL;
    int minCount = 0;
    for (int i = 0; i < inCache->E; i++)
    {
        
        pLine = &inCache->lines[sIndex+i];

        // true && t = t
        if (pLine->valid && pLine->t == tBit)
        {
            printf("hit\n");
            ++pLine->count;
            ++inResult->hitCount;
            return;
        }

        // false
        if (pLine->valid == false)
        {
            printf("miss\n");
            pLine->valid = true;
            pLine->t = tBit;
            ++pLine->count;
            ++inResult->misCount;
            return;
        }
        
        // true
        leastLine = minCount >= pLine->count ? pLine : leastLine;
        minCount = minCount >= pLine->count ? pLine->count : minCount;
        
    }

    printf("replace\n");
    leastLine->t = tBit;
    ++leastLine->count;
    ++inResult->misCount;
    ++inResult->eviCount;
    return;
}

void readAndTest(int b,FILE *pFile, cache *inCache,result* inResult)
{
    int s = inCache->s;
    int E = inCache->E;
    u_int64_t sBitMask = (1 << s) - 1; // 这里的s是幂"2^s"
    u_int64_t sIndex;
    u_int64_t tBit;

    char contiueFlag;
    char identifier;
    u_int64_t address;
    int size;
 
    while (fscanf(pFile, "%c %c %lx,%d", &contiueFlag, &identifier, &address, &size) > 1)
    {

        if (contiueFlag == 'I')
            continue;
        sIndex = (address >> b) & sBitMask;
        tBit = (address >> b) >> s;
        sIndex = inCache->E * sIndex;
        switch (identifier)
        {

        case 'M':
            printf("M %lx,%d\n",address,size);
            missHit(sIndex, tBit, inCache, inResult);
            missHit(sIndex, tBit, inCache, inResult);
            break;
        case 'L':
            printf("L %lx,%d\n",address,size);
            missHit(sIndex, tBit, inCache, inResult);
            break;
        case 'S':
            printf("S %lx,%d\n",address,size);
            missHit(sIndex, tBit, inCache, inResult);
            break;
        default:
            break;
        }
    }


}

int main(int argc, char **argv)
{
    int opt, s, E, b;
    FILE *pFile;

    while (-1 != (opt = getopt(argc, argv, "hvs:E:b:t:")))
    {
        switch (opt)
        {
        case 'h':
            printf("Usage: ./csim-ref [-hv] -s  -E  -b  -t \n\
            -h: Optional help flag\n\
            -v: Optional verbose flag that displays trace info\n\
            -s : Number of set index bits(S = 2s is the number of set)\n\
            -E : Associtivity (number of lines per set)\n\
            -b : Number of block bits (B = 2b is the block size)\n\
            -t : Name of the valgrind trace to repla\n");
            break;
        case 's':
            s = atoi(optarg);
            break;
        case 'E':
            E = atoi(optarg);
            break;
        case 'b':
            b = atoi(optarg);
            break;
        case 'v':
            // vFlag = 1;
            printf("show step!\n");
            break;
        case 't':
            pFile = fopen((const char *)optarg, "r");
            break;
        default:
            break;
        }
    }

    if (pFile == NULL)
    {
        printf("error:can't read file!\n");
        // return 0;
    }

    cache *myCache = calloc(1, sizeof(cache));
    result myResult;
    myResult.hitCount = 0;
    myResult.misCount = 0;
    myResult.eviCount = 0;
    initCache(s, E, myCache);
    readAndTest(b, pFile, myCache,&myResult);
    printf("hitCount:%d,misCount:%d,eviCount:%d\n",myResult.hitCount,myResult.misCount,myResult.eviCount);
    realseMemory(myCache);

    if (pFile != NULL)
        fclose(pFile);
    printSummary(myResult.hitCount, myResult.misCount, myResult.eviCount);
    return 0;
}

你可能感兴趣的:(CSAPP,c++)