近一段时间项目太忙导致没有继续,还好最近空下来一些,咱们继续冲!
更新历史
20210104
开始更新20210107
完成实验一内容本文介绍的是CSAPP书籍中的第四个lab: Cache lab。本实验要求我们实现一个软件高速缓存,以及优化矩阵。通过这个lab我们可以更好的理解缓存以及写出更加对缓存友好的代码。
介绍完上面必须的知识之外,我们就开始吧!
本实验一共分为两个实验,第一个软件高速缓存模拟器,第二个优化矩阵。
实验一:
我们主要的操作流程
1.在csim.c里编写代码
2.使用make编译(注意需要将你的结果传入printSummary里)
3../test-csim
-终端中检查结果
实验二:
我们主要的操作流程
需求描述:
(也就是缓存块B=2^b)
,但是我们需要知道我们每行所占数据块的幂也就是b
,需要用于计算t
。预备知识
我们可以把高速缓存看成是一个二维数组,它一共有S=2^s组,有E行,且每行包含一组信息,分别是有效标识,标识,数据块。
地址的构成是和高速缓存的结构相关的,例如我们有m位地址,那么中间的s位就是高速缓存中S=2^s
的幂 。
(这里插入一个个人标注,我认为设计为2的次方的原因是好计算,因为我们使用位计算可以轻易的计算出2的n方)
缓存的设计
上面讲到我们把高速缓存当成二维数组来看,所以我们数据结构也是这样设计的。
// 行的结构
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,然后还有另外一个状态是修改。
主要分为三步
vaild
为1,且tag
和地址里的相同,这个断定为hitvaild
为0,那么直接判定为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;
}