参考链接先挂上,再补
读书中
https://zhuanlan.zhihu.com/p/33846811
PartA
这相当于一个模拟题目,读入文件后分析是否hit,miss,是否需要eviction。
开始需要进行分析参数,这里使用了getopt函数。
1 #include "cachelab.h"
2 #include<getopt.h>
3 #include<stdlib.h> 4 #include<unistd.h> 5 #include<stdio.h> 6 #include<string.h> 7 #include<getopt.h> 8 int s,E,b,S,number_hits,number_miss,number_eviction; 9 char filename[1000]; 10 char buf[1000]; 11 12 typedef struct{ 13 int valid,tag;//有效位,标记 14 int time;//LRU时间标记 15 }cache_line; 16 cache_line **cache = NULL; 17 void update(unsigned address){ 18 int t_addr,s_addr,max_time = -1,max_time_id = 0; 19 s_addr = (address >> b) & ((0x1 << s) - 1);//取出中间s位 20 t_addr = address >> (s + b);//取出前面t位 21 //check if hit检查命中,命中更新时间标记,hits次数 22 for(int i = 0;i < E;i++){ 23 if(cache[s_addr][i].valid == 1 && 24 cache[s_addr][i].tag == t_addr){ 25 cache[s_addr][i].time = 0; 26 number_hits++; 27 return; 28 } 29 } 30 //check if empty检查是否有空行,有就放进去,到这里说明之前没hit,所以miss++ 31 for(int i = 0;i < E;i++){ 32 if(cache[s_addr][i].valid == 0){ 33 cache[s_addr][i].valid = 1; 34 cache[s_addr][i].time = 0; 35 cache[s_addr][i].tag = t_addr; 36 number_miss++; 37 return; 38 } 39 } 40 //no empty没有空行,需要选出淘汰掉 41 number_eviction++; 42 number_miss++; 43 //找出时间最长的行替换掉 44 for(int i = 0;i < E;i++){ 45 if(cache[s_addr][i].time > max_time){ 46 max_time = cache[s_addr][i].time; 47 max_time_id = i; 48 } 49 } 50 cache[s_addr][max_time_id].tag = t_addr; 51 cache[s_addr][max_time_id].time = 0; 52 return; 53 } 54 int main(int argc,char *argv[]) 55 { 56 int opt,temp; 57 number_hits = number_miss = number_eviction = 0; 58 while(((opt = getopt(argc,argv,"hvs:E:b:t:")) != -1)){ 59 switch(opt){ 60 case 'h':system("./cism -s 1 -E 1 -b 1 -t traces/yi2.trace");//具体要求在参考文档有 61 exit(0);break; 62 case 's':s = atoi(optarg);S = (1<<s);break; 63 case 'E':E = atoi(optarg);break; 64 case 'b':b = atoi(optarg);break; 65 case 't':strcpy(filename,optarg); 66 } 67 } 68 FILE* fp = fopen(filename,"r"); 69 if(fp == NULL){//文件打开失败 70 fprintf(stderr,"The file is wrong! "); 71 exit(-1); 72 } 73 //开辟cache
//cache是二级指针,指向cache;对应一级指针指向每一个组,每组里有许多行 74 cache = (cache_line**)malloc(sizeof(cache_line*) * S); 75 for(int i = 0;i < S;i++){//开辟每个一级指针 76 cache[i] = (cache_line*)malloc(sizeof(cache_line) * E); 77 }//初始化每行 78 for(int i = 0;i < S;i++){ 79 for(int j = 0;j < E;j++){ 80 cache[i][j].valid = 0; 81 cache[i][j].tag = cache[i][j].time = -1; 82 } 83 } 84 unsigned addr; 85 char type; 86 while(fgets(buf,1000,fp)){ 87 sscanf(buf," %c %xu,%d",&type,&addr,&temp); 88 switch(type){ 89 case 'L':update(addr);break;//L操作访问一次cache 90 case 'M':update(addr);
//L操作访问两次cache
91 case 'S':update(addr);break;//访问一次cache 92 } 93 for(int i = 0;i < S;i++){ 94 for(int j = 0;j < E;j++){ 95 if(cache[i][j].valid == 1) 96 cache[i][j].time++;//使用LRU算法,time作为标记 97 } 98 } 99 } 100 for(int i = 0 ;i < S;i++){ 101 free(cache[i]); 102 } 103 free(cache); 104 fclose(fp); 105 printSummary(number_hits, number_miss, number_eviction); 106 return 0; 107 }
链接:https://zhuanlan.zhihu.com/p/28585726
Part B
没大看懂。。
怎么估算的B的miss次数
part B是优化矩阵转置算法,使用分块让Cache的miss次数尽量少。
题目给的Cache大小只有 1024B,s=5,E=1,b=5。
给的数据大小分别是32×32,64×64,61×67
只能只用12个int变量。
32×32
第一题要求miss次数在300以下。
Cache的一个块只有32B,也就是只能容纳8个int。即容纳这个matrix的前8行。
分块的话,肯定是取8×8的比较合适。先读取A的一行,然后放入B的一列。
对于在对角线上的块,A中每读一行,会有一次miss,也就是miss次数是读取操作的1/8,对于B数组的话,第一次读取这行会产生一次miss,之后对于第i行,只有A中读到第i行的时候,会被移除出Cache,然后存的时候会产生一次miss。可以粗略计算为miss次数是读取次数的1/4。
对于不在对角线上的块,做转置的时候,A还是1/8的miss率,B的每行在Cache中和A的行不冲突 ,所以也是1/8的miss率。
大概是
64×64
1300次以下。
Cache中只能放4行A中的行,如果再用8×8的块,前面4行可以填入,后面4行会在Cache中发生冲突,导致miss次数增加。
如果只用4×4的块呢?那么每次Cache中放入8个int,却只用4个,浪费严重。
引用:
1.先考虑把A的上半部分存入到B,但是为了考虑Cache不冲突,所以把右上角的4×4的区域也存在B的右上角。对于在对角线上的块,A的miss率是1/8,B的左上角部分miss率是1/2。对于不在对角线上的块,A的miss率还是1/8,B左上角部分的miss率为1/4.
2. 接下来这步是减少miss率的关键,把A左下角的一列4个数据读出,B右上角的一行4个数据读出,都用int变量暂存,然后把前四个填入B右上角行中,后四个填入B的左下角行中。
因为从B右上角读取的时候,把块放入了Cache,然后从A往B中填的时候,就不会出现miss操作。
来计算一下miss率,对于在对角线上的块,从A左下角读取miss率为1,B的右上角的操作miss率为1/4,B的左下角miss率为1/4。对于不在对角线的快,A的miss率为1/4,B右上角miss率为0,左下角miss率为1/4。
3. 最后一步就是把A的右下角填入B的右下角,对于在对角线上的块,A的miss率为1/4,B的miss率为1/2.不在对角线上的块,A,B的miss率都为0.
最后我们来计算下miss的次数吧,计算出来近似是1280次,实际我们代码跑出来是1219次 。
61×67
用16*16可以解决
void transpose_submit(int M, int N, int A[N][M], int B[M][N]) { int a1,a2,a3,a4,a5,a6,a7,a8; int i,j,k,h; if(N==32){//第一种情况32*32 for(i=0;i<4;i++){ for(j=0;j<4;j++){ //分块8*8 for(k=i*8;k<(i+1)*8;k++){ h=j*8; a1=A[k][h];a2=A[k][h+1];a3=A[k][h+2];a4=A[k][h+3]; a5=A[k][h+4];a6=A[k][h+5];a7=A[k][h+6];a8=A[k][h+7]; B[h][k]=a1;B[h+1][k]=a2;B[h+2][k]=a3;B[h+3][k]=a4; B[h+4][k]=a5;B[h+5][k]=a6;B[h+6][k]=a7;B[h+7][k]=a8; } } } } else if(N==64){//第二种64*64 for(i=0;i<64;i+=8){ for(j=0;j<64;j+=8){//每个块是8*8 for(k=j;k<j+4;++k){//先处理A上半部分4*8 a1=A[k][i];a2=A[k][i+1];a3=A[k][i+2];a4=A[k][i+3]; a5=A[k][i+4];a6=A[k][i+5];a7=A[k][i+6];a8=A[k][i+7]; B[i][k]=a1;B[i][k+4]=a5;B[i+1][k]=a2;B[i+1][k+4]=a6; B[i+2][k]=a3;B[i+2][k+4]=a7;B[i+3][k]=a4;B[i+3][k+4]=a8; } for(k=i;k<i+4;++k){//处理A左下角 a1=B[k][j+4];a2=B[k][j+5];a3=B[k][j+6];a4=B[k][j+7]; a5=A[j+4][k];a6=A[j+5][k];a7=A[j+6][k];a8=A[j+7][k]; B[k][j+4]=a5;B[k][j+5]=a6;B[k][j+6]=a7;B[k][j+7]=a8; B[k+4][j]=a1;B[k+4][j+1]=a2;B[k+4][j+2]=a3;B[k+4][j+3]=a4; } for(k=i+4;k<i+8;++k){//处理A右下角 a1=A[j+4][k];a2=A[j+5][k];a3=A[j+6][k];a4=A[j+7][k]; B[k][j+4]=a1;B[k][j+5]=a2;B[k][j+6]=a3;B[k][j+7]=a4; } } } } else{//最后一种 61*67 for(i=0;i<N;i+=16){ for(j=0;j<M;j+=16){ //16*16分块 for(k=i;k<i+16&&k<N;k++){ for(h=j;h<j+16&&h<M;h++){ B[h][k]=A[k][h]; } } } } } }