part A
Cache Lab分为两部分,第一部分是写一个缓存的模拟器,这个其实还比较简单,只要弄明白缓冲的工作原理就行。写的时候,对于LRU的实现,开始我打算使用使用标志位,每次缓存+1的策略实现,但这样可能会溢出。后来看了别人的代码,可以使用一个大数初始化,然后把其他缓存减一的策略,这样可以有效克服溢出的情况。我写代码的时候,没有认真读实验手册,导致没有使用原来定义好的结构体,但还是可以通过测试。
#define MAX 100000
struct line{
int *flags;/* 是否有效 */
long long *heads;/* 缓存头部 */
int *times;/* 记录每组的时间 */
int E;/* 有多少组 */
};
struct cache{
int b;/* 记录偏移的长度 */
struct line *lines;/* 缓存的地址 */
int S;/* 缓存的组数 */
long long mask_index;/* 索引的掩码 */
long long mask_mark;/* 标记的掩码 */
int hits;/* 命中 */
int misses;/* 未命中 */
int evictions;/* 驱逐 */
};
/* 对函数进行声明 */
void init_cache(struct cache *cache,int s,int E,int b);/* 初始化缓存 */
void init_line(struct line *line,int E);/* 初始化缓存行 */
void get_cache(struct cache *cache,long long addr);/* 得到缓存 */
void init_cache(struct cache *cache,int s,int E,int b){
cache->S = 1<lines = malloc(cache->S * sizeof(struct line));
int i;
for(i=0;iS;i++){
init_line(cache->lines+i,E);
}
cache->b = b;
long long mask_index = -1ll;
long long mask_mark = -1ll;
mask_mark = mask_mark<<(s + b);
mask_index = ~( mask_mark | ~(mask_index << b));
cache->mask_index = mask_index;
cache->mask_mark = mask_mark;
//printf("%p %p\n",mask_mark,mask_index);
cache->misses = 0;
cache->hits = 0;
cache->evictions = 0;
}
void init_line(struct line *line,int E){
/*为其他的内容分配内存*/
line->heads = (long long *)malloc(E * sizeof(long long));
line->flags = (int *)malloc(E * sizeof(int));
line->times = (int *)malloc(E * sizeof(int));
line->E = E;
int i;
for(i =0;iflags[i] = 0;
line->times[i] = 0;
}
}
void get_cache(struct cache *cache,long long addr){
/* 根据索引找到组 */
int index = (int)(addr & cache->mask_index)>>cache->b;
printf("index = %d\n",index);
/* 在组内找标记 */
struct line *line= cache->lines + index;
int i;
for(i = 0;iE;i++){
long long content = line->heads[i];
int flag = line->flags[i];
if((addr & cache->mask_mark) == (content & cache->mask_mark) && flag == 1){
printf("hits\n");
/* 更新使用时间 */
int j;
for(j=0;jE;j++){
if(j == i){
line->times[j] = MAX;
}else{
line->times[j]--;
}
}
cache->hits++;
return;
}
}
cache->misses++;
/* 如果没满,进行缓存 */
for(i = 0;iE;i++){
if(line->flags[i] == 0){
line->heads[i] = addr;
line->flags[i] = 1;
printf("flag = %d\n",line->flags[i]);
/* 更新使用时间 */
int j;
for(j=0;jE;j++){
if(j == i){
line->times[j] = MAX;
}else{
line->times[j]--;
}
}
printf("miss\n");
return;
}
}
/* 驱逐 */
cache->evictions++;
printf("eviction\n");
int j,pos=0,min = MAX;
for(j = 0;jE;j++){
if(line->times[j] < min){
min = line->times[j];
pos = j;
}
}
line->heads[pos] = addr;
/* 驱逐之后仍然需要更新时间 */
for(i=0;iE;i++){
if(i == pos){
line->times[i] = MAX;
}else{
line->times[i]--;
}
}
}
int main(int argc,char *argv[])
{
int s = 1;
int E = 1;
int b = 1;
char *file = NULL;
int opt;
while( (opt = getopt(argc,argv,"s:E:b:t:")) != -1){
switch(opt){
case 's':
s = atoi(optarg);
break;
case 'E':
E = atoi(optarg);
break;
case 'b':
b = atoi(optarg);
break;
case 't':
file = optarg;
break;
default:
break;
}
}
printf("s = %d E = %d b = %d file = %s\n",s,E,b,file);
/* 初始化缓存 */
struct cache cache;
init_cache(&cache,s,E,b);
FILE *fp = fopen(file,"r");
if(fp == NULL){
printf("open file fail\n");
return 1;
}
char c;
long long x,y;
while(fscanf(fp,"%c%llx%llx",&c,&x,&y) != EOF){
switch(c){
case 'L':
case 'S':
get_cache(&cache,x);
break;
case 'M':
get_cache(&cache,x);
get_cache(&cache,x);
break;
default:
break;
}
}
printSummary(cache.hits, cache.misses, cache.evictions);
return 0;
}
part B
个人感觉part B很难,感觉不好分析,代码实现也略有难度。先说说32 * 32的情况,因为使用的缓存大小为s=5,E=1,b=5。我们假设B[0][0]映射到组1,因为缓存的大小为32字节,所以可以放8个int型数据。先看看直接转置的问题。因为矩阵是连续存储的,所以B[1][0]映射到组5,B[2][0]映射到组9,以此类推。我们发现,B[8][0]映射到组1。所以,如果我们直接转置,写入8个int后,下次就会发生驱逐。所以,我们应该先填满缓存,然后在读入下一组。对应的代码如下。
void transpose_submit(int M, int N, int A[N][M], int B[M][N]){
if(M == 32){
int i,j,tmp0,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7;
for(j=0;j< 32;j+=8){
for(i=0;i<32;i++){
tmp0 = A[i][j+0];
tmp1 = A[i][j+1];
tmp2 = A[i][j+2];
tmp3 = A[i][j+3];
tmp4 = A[i][j+4];
tmp5 = A[i][j+5];
tmp6 = A[i][j+6];
tmp7 = A[i][j+7];
B[j+0][i] = tmp0;
B[j+1][i] = tmp1;
B[j+2][i] = tmp2;
B[j+3][i] = tmp3;
B[j+4][i] = tmp4;
B[j+5][i] = tmp5;
B[j+6][i] = tmp6;
B[j+7][i] = tmp7;
}
}
return;
}
if(M == 64){
int i,j,tmp0,tmp1,tmp2,tmp3;
for(j=0;j
64 * 64的分析差不多,但是这样做得不了满分,可以拿到8分中的4分。看了别人的解答,感觉还是很复杂。至于61 * 67,直接使用8 * 8的分块就过了。
个人对于Part B的理解确实不多,就写到这里吧。