下载完hand-out没有实验文档,之前的没实验文档看代码注释也知道大概是干嘛的,这次不行,cism.c里面可以说是啥都没有,完全不知道让干嘛,无奈看别人答案才知道原来在wite up里有详细说明,还有提示,受ctf的影响我一直以为那wite up是答案,所以从来没看过,今天下载来看看觉得长得很像课堂ppt,不清楚是不是,如果是的话,那做这个实验还是很有必要听听课的
看到开局的这一张图感觉好好笑啊,突然好想听听课
part A
这是让实现一个对cache的LRU算法的部分,然后有个测试,根据trace里的数据测试命中次数,然后cache块是二维数组,主要是一些开局的初始化需要看看提供的两个函数,getopt
还有fscanf
,write up里都有介绍,看一下就懂了,然后就是
1.第一步初始化,init()里初始化出一个二维数组然后还有命中次数,miss次数,数据个数等等的初始化
2对传入进来的参数做回应,这个参数就是运行程序时加入的argc和argv,argc是传入的参数个数,argv是传入的参数数组,对应argv[1],argv[2]…unix-linux编程实践那本书有讲,这种东西应该挺多教linux编程的都会讲,这里有这一步主要是给的样例有这个功能
3一个循环,读取trace中的数据,并根据参数做相应操作,这一步就不用说了,总之就是LRU算法,然后模拟cache操作数据命中或者miss都要基数就是了,最后结果通过原本就有个printSummary,给输出,这是拿到源码文件里面本身就带的,但是记住把里面的000换成对应的参数
4. free空间
代码贴上我觉得别人写的好的一个,我自己写的太烂了来自
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include "cachelab.h"
#define MAX_CACHE_SET 32
#define MAX_CACHE_LINE 32
#define DEBUG 0
int hit_cnt;
int miss_cnt;
int eviction_cnt;
int cmd_cnt;
int s,E,b;
struct cache_line
{
int valid_bit; //这个位置是否有数据
int tag_bit; //标志位
int last_time; //LRU参考
}cache[MAX_CACHE_SET][MAX_CACHE_LINE];
void init(); //初始化
void args_parse(int argc, char *argv[]); //分析输入参数
void cmd_parse(char *cmd,long long addr); //分析要执行哪条命令,trace数据对应的命令
void exec_cmd(long long addr); //执行命令
void addr_parse(long long addr,int *tag_bit,int *set_id); //存入cache中的哪个位置
int main(int argc,char *argv[]);
void init(){
hit_cnt=miss_cnt=eviction_cnt=cmd_cnt=0;
memset(cache,0,sizeof(cache));
return;
}
void args_parse(int argc, char *argv[]){
char ch;
while((ch=getopt(argc, argv,"s:E:b:t:"))!=-1){
switch (ch)
{
case 's':
s=atoi(optarg);
break;
case 'E':
E=atoi(optarg);
break;
case 'b':
b=atoi(optarg);
break;
case 't':
freopen(optarg, "r", stdin);
}
}
return;
}
void cmd_parse(char *cmd,long long addr){
switch (cmd[0])
{
case 'I':
break;
case 'L':
exec_cmd(addr);
break;
case 'S':
exec_cmd(addr);
break;
case 'M':
exec_cmd(addr);
exec_cmd(addr);
break;
}
return;
}
void addr_parse(long long addr,int *tag_bit,int *set_id){
int tmp=0;
for(int i=0;i<s;i++){ //对数据存放位置的计算,约定
tmp=(tmp<<1)+1;
}
*set_id=((int)(addr>>b)&tmp)%(1<<s);
*tag_bit=(int)(addr>>(b+s));
return;
}
void exec_cmd(long long addr){
int tag_bit,set_id;
cmd_cnt++;
addr_parse(addr,&tag_bit,&set_id);
if(DEBUG) printf("%d %d ",set_id,tag_bit);
for(int i=0;i<E;i++){
if((cache[set_id][i].valid_bit) //命中则return
&&(cache[set_id][i].tag_bit)==tag_bit
){
cache[set_id][i].last_time=cmd_cnt;
hit_cnt++;
if(DEBUG) printf("hit\n");
return;
}
}
miss_cnt++;
for(int i=0;i<E;i++){
if(!cache[set_id][i].valid_bit){ //未命中,且有空位
cache[set_id][i].valid_bit=1;
cache[set_id][i].tag_bit=tag_bit;
cache[set_id][i].last_time=cmd_cnt;
if(DEBUG) printf("miss\n");
return;
}
}
eviction_cnt++; //未命中且无空位
int victim_id=0;
for(int i=0;i<E;i++){
if(cache[set_id][i].last_time<cache[set_id][victim_id].last_time){
victim_id=i; //求出最早未使用
}
}
cache[set_id][victim_id].tag_bit=tag_bit; //将其替换
cache[set_id][victim_id].last_time=cmd_cnt;
if(DEBUG) printf("miss eviction\n");
return;
}
int main(int argc,char *argv[])
{
init();
args_parse(argc,argv);
char cmd[10];
long long addr;
int blocksize;
while(~scanf("%s %llx,%d",cmd,&addr,&blocksize)){
cmd_parse(cmd,addr); //对trace中数据格式的分析
}
printSummary(hit_cnt, miss_cnt, eviction_cnt); //输出结果
return 0;
}
part B
这部分我刚开始没看write up,我当时感觉,对于转置矩阵来说,需要互换位置,也就是两边都要取,即如果要顾着右边横着连续,则左边竖着就无法连续,我有想过分为两次操作,第一次是横着取,把下三角分到另一个同样大小的空间的数组的上三角去,然后再把这个数组中的上三角横着取,放下三角,这样能实现取数据都是在连续的空间取的,当然,存数据不是,但是我觉得存数据不符合局部性应该也还可以
以上是错误思路
看了write up里写了,只有一块cache,单看write up上的说明似乎是让在大数组里分成一块一块的小块来置换,但是具体操作看这没看懂,为了快点结束战斗我去借鉴别人的一下,大意是一次取出多个再一次放入多个,这样存和取都可以用到cache,大致代码就是,
if(N==32&&M==32){
for(i=0;i<N;i+=8){
for(j=0;j<M;j+=8){
for(x=i;x<i+8;x++){
for(y=j;y<j+8;y++){
B[y][x]=A[x][y];
}
}
}
}
}
不同数据规模照着这个套就了事,好的的优化我就不钻研了,对这块不感兴趣