缓存CACHE
一、基础知识
c语言中文件读取的顺序需要先打开文件,然后再进行读取操作
1.不带缓冲的文件打开读取
File* fp;
fp=fopen(char* filename,char* mode);
char * fgets (char *s, int size, FILE *fp);
2.带有缓冲的文件打开读取(更加安全),经常使用。返回的是一个文件指针,指向文件读写的起始位置。
File* fp;
fp=fopen(char* filename,char* mode);
char * fgets (char *s, int size, FILE *fp);
fscanf可以按照特定格式进行读取
Cache相关理解:
1)Cache是由硬件进行管理的,设计cache的目的是能够让最常访问的数据集中在访问速度最快的缓存中,使得alu读写数据速度快,效率高。
一个Cache可以看成是一个二维数组,这个数组内的元素就是一个cache_line结构体,这个cache_line结构体内部保存的有一个valid bit,一些tag bits,以及一个2^b bytes大小的数据块,这个数据块是连续的。
二、CACHE LAB
cache lab
目标:做一个缓存模拟器,命令行执行,对-t后面界的文件内容进行解析,打印出命中数,非命中数以及页面驱赶数(即发生页面置换后,被驱逐的次数)。
Usage: ./csim-ref [-hv] -s <s> -E <E> -b <b> -t <tracefile>
• -h: Optional help flag that prints usage info
• -v: Optional verbose flag that displays trace info
• -s <s>: Number of set index bits (S = 2s is the number of sets)
• -E <E>: Associativity (number of lines per set)
• -b <b>: Number of block bits (B = 2b is the block size)
• -t <tracefile>: Name of the valgrind trace to replay
-t 文件的内容格式:
I 0400d7d4,8
M 0421c7f0,4
L 04f6b868,8
S 7ff0005c8,8
[space]operation address,size
I:代表指令,直接跳过,忽略即可。
S:代表存储,需要执行一次命中判断
M:代表修改,需要执行两次命中判断
L:代表加载,需要执行一次命中判断
//第一版本
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "math.h"
const int maxi=999;
typedef struct{
unsigned int valid;//有效位
unsigned int tag_bit;//标识位
int time_stamp;
} cache_line,*cache_asso,**cache;
cache _cache;
int hits=0;int evictions=0;int mises=0;
int getNum(char* str,int n);
void parse_command(char* argc[],int* S,int* E,int* b,char* filename,int* isVerbose);
void init(int S,int E);
void parse_addr(unsigned int addr);
void parse_trace(char* filename,int S,int E,int s,int b,int isVerbose);
/*
使用LRU算法来寻找victim,LRU算法在具体实现的时候,
可以用权重来看,每一次访问时权重增加1,最终要被
替换的是权重最轻的那一个line
*/
int getNum(char* str,int n){
int sum=0;
for(int i=0;i<n;i++){
sum+=sum*10+str[i]-'0';
}
return sum;
}
/*
Usage: ./csim-ref [-hv] -s <s> -E <E> -b <b> -t <tracefile>
• -h: Optional help flag that prints usage info
• -v: Optional verbose flag that displays trace info
• -s <s>: Number of set index bits (S = 2^s is the number of sets)
• -E <E>: Associativity (number of lines per set)
• -b <b>: Number of block bits (B = 2b is the block size)
• -t <tracefile>: Name of the valgrind trace to replay
*/
void parse_command(char* argc[],int* s,int* E,int* b,char* filename,int* isVerbose){
if(strcmp(argc[1],"-v")==0) {
*isVerbose=1;
(*s)=getNum(argc[3],strlen(argc[3]));
(*E)=getNum(argc[5],strlen(argc[5]));
(*b)=getNum(argc[7],strlen(argc[7]));
filename=argc[9];
} else{
*isVerbose=0;
(*s)=getNum(argc[2],strlen(argc[2]));
(*E)=getNum(argc[4],strlen(argc[4]));
(*b)=getNum(argc[6],strlen(argc[6]));
filename=argc[8];
}
}
void init(int S,int E){
_cache=(cache)malloc(sizeof(cache_asso)*S);
for(int i=0;i<S;i++){
_cache[i]=(cache_asso)malloc(sizeof(cache_line)*E);
for(int j=0;j<E;j++){
_cache[i][j].tag_bit=0;
_cache[i][j].valid=0;
_cache[i][j].time_stamp=0;
}
}
}
/*
memory address format:
高位 低位
tag == sets_index == block_offset
t s b
命中的原则是:
1.设置了有效位
2.请求地址的标记位与cache地址的标位一致
*/
int bi2decimal(unsigned int sets_index){
int sum=0;
while(sets_index){
sum=sum*2+sets_index%10;
sets_index=sets_index/10;
}
return sum;
}
void exec(unsigned int tag,unsigned int sets_index,int E,int isVerbose){
int index=bi2decimal(sets_index);
cache_asso asso=_cache[index];
for(int i=0;i<E;i++){
if(asso[i].valid==1 && asso[i].tag_bit==tag){
asso[i].time_stamp++;
hits++;
if(isVerbose) printf("hits\n");
return;
}
}
if(isVerbose) printf("mise ");
mises++;
for(int i=0;i<E;i++){
if(asso[i].valid==0){
asso[i].valid=1;
asso[i].tag_bit=tag;
asso[i].time_stamp=0;
printf("\n");
return;
}
}
int victim_index=-1;int victim_stamp=maxi;
for(int i=0;i<E;i++){
if(victim_stamp>asso[i].time_stamp){
victim_stamp=asso[i].time_stamp;
victim_index=i;
}
}
evictions++;
asso[victim_index].tag_bit=tag;
printf("evict\n");
}
void parse_trace(char* filename,int S,int E,int s,int b,int isVerbose){
FILE* fp;
fp = fopen(filename,"r");
if(fp==NULL) printf("Error reading file!");
char op;unsigned int addr;unsigned int offset;
while(fscanf(fp," %c %d,%d",&op,&addr,&offset)!=EOF){
unsigned int tag=addr>>(s+b);
unsigned int sets_index=(addr>>b)&( (unsigned int) (pow(2,s)-1) );
if(op=='I') continue;
else if(op=='L') {
if(isVerbose) printf("%c %d ",op,addr);
exec(tag,sets_index,E,isVerbose);
}
else if(op=='M') {
if(isVerbose) printf("%c %d ",op,addr);
exec(tag,sets_index,E,isVerbose);
exec(tag,sets_index,E,isVerbose);
}
else continue;
// printf("%c,%d,%d",op,addr,offset);
}
fclose(fp);
for(int i=0;i<S;i++){
free(_cache[i]);
}
free(_cache);
}
int main(int argv,char* argc[]){
if(argv==1){
printf("Wrong input!");
return 0;
}
int s;int E;int b;int isVerbose;char filename[maxi];
parse_command(argc,&s,&E,&b,filename,&isVerbose);
printf("%s",filename);
int S=pow(2,s);int B=pow(2,b);
/*test for function parse_command;
printf("%d,%d,%d,%d,%s",S,E,B,isVerbose,filename);
*/
init(S,E);
/*test for function init;
for(int i=0;i<S;i++){
for(int j=0;j<E;j++){
printf("%d,%d\n",_cache[i][j].tag_bit,_cache[i][j].valid);
}
}
*/
/*
test for function bi2decimal
printf("%d\n",bi2decimal(11));
*/
parse_trace(filename,S,E,s,b,isVerbose);
printf("hits:%d, evictions:%d, mises:%d",hits,evictions,mises);
return 0;
}
第一版本测试后,出现报错,报错如下图所示。出现段错误报错,初步怀疑是filename读取的问题。尝试放弃自己的parse_command函数,直接用getopt函数来分析,利用man 3 getopt找到,getopt需要include的头文件。
***linux在使用man指令时,可以加上参数: man X funcname
2 系统调用,即由内核提供的函数。
3 例程,即库函数,比如标准C库libc。
getopt寒素需要包含unistd.h的头文件。
在编译c文件的过程中,出现无法编译的问题,因为包含了math.h头文件,所以必须在命令行最后加上-lm。
int getopt(int argc, char * const argv[],const char *optstring);
/*
对于getopt函数,前两个参数和main函数相同,直接从main函数传递给getopt。第三个参数optstring(字符串)是一种排序编码,包含了用户程序想要接收的所有单字母选项。
比如用户程序接收以下的选项: -a -b -c
那么optstring参数将只包含字符串“abc(其中a、b、c字符的顺序以及字符是否存在并不重要,但是字符区分大小写)”
*/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8GOiaV53-1629079212302)(C:\Users\zgyd\AppData\Roaming\Typora\typora-user-images\image-20210816002549028.png)]
void parse_command(int argc,char* argv[],int* s,int* E,int* b,char* filename,int* isVerbose){
int opt;
while ((opt = getopt(argc, argv, "vs:E:b:t:")) != -1)
{
switch (opt)
{
case 'v':
*isVerbose = 1;
break;
case 's':
*s = atoi(optarg);
break;
case 'E':
*E = atoi(optarg);
break;
case 'b':
*b = atoi(optarg);
break;
case 't':
strcpy(filename,optarg);
break;
}
}
}
//问题解决,是filename读取有问题。最终版本的cache lab如下
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "unistd.h"
const int maxi=999;
typedef struct{
unsigned int valid;//有效位
unsigned int tag_bit;//标识位
int time_stamp;
} cache_line,*cache_asso,**cache;
cache _cache;
int hits=0;int evictions=0;int mises=0;
int getNum(char* str,int n);
void parse_command(int argc,char* argv[],int* s,int* E,int* b,char* filename,int* isVerbose);
void init(int S,int E);
void parse_addr(unsigned int addr);
void parse_trace(char* filename,int S,int E,int s,int b,int isVerbose);
/*
使用LRU算法来寻找victim,LRU算法在具体实现的时候,
可以用权重来看,每一次访问时权重增加1,最重要被
替换的是权重最轻的那一个line
*/
int getNum(char* str,int n){
int sum=0;
for(int i=0;i<n;i++){
sum+=sum*10+str[i]-'0';
}
return sum;
}
/*
Usage: ./csim-ref [-hv] -s <s> -E <E> -b <b> -t <tracefile>
• -h: Optional help flag that prints usage info
• -v: Optional verbose flag that displays trace info
• -s <s>: Number of set index bits (S = 2^s is the number of sets)
• -E <E>: Associativity (number of lines per set)
• -b <b>: Number of block bits (B = 2b is the block size)
• -t <tracefile>: Name of the valgrind trace to replay
*/
void parse_command(int argc,char* argv[],int* s,int* E,int* b,char* filename,int* isVerbose){
int opt;
while ((opt = getopt(argc, argv, "vs:E:b:t:")) != -1)
{
switch (opt)
{
case 'v':
*isVerbose = 1;
break;
case 's':
*s = atoi(optarg);
break;
case 'E':
*E = atoi(optarg);
break;
case 'b':
*b = atoi(optarg);
break;
case 't':
strcpy(filename,optarg);
break;
}
}
}
void init(int S,int E){
_cache=(cache)malloc(sizeof(cache_asso)*S);
for(int i=0;i<S;i++){
_cache[i]=(cache_asso)malloc(sizeof(cache_line)*E);
for(int j=0;j<E;j++){
_cache[i][j].tag_bit=0;
_cache[i][j].valid=0;
_cache[i][j].time_stamp=0;
}
}
}
/*
memory address format:
高位 低位
tag == sets_index == block_offset
t s b
命中的原则是:
1.设置了有效位
2.请求地址的标记位与cache地址的标位一致
*/
int bi2decimal(unsigned int sets_index){
int sum=0;
while(sets_index){
sum=sum*2+sets_index%10;
sets_index=sets_index/10;
}
return sum;
}
void exec(unsigned int tag,unsigned int sets_index,int E,int isVerbose){
int index=bi2decimal(sets_index);
cache_asso asso=_cache[index];
for(int i=0;i<E;i++){
if(asso[i].valid==1 && asso[i].tag_bit==tag){
hits++;
asso[i].time_stamp++;
if(isVerbose) printf("hit\n");
return;
}
}
if(isVerbose) printf("miss\n");
mises++;
for(int i=0;i<E;i++){
if(asso[i].valid==0){
asso[i].valid=1;
asso[i].tag_bit=tag;
asso[i].time_stamp=1;
return;
}
}
int victim_index=-1;int victim_stamp=maxi;
for(int i=0;i<E;i++){
if(victim_stamp>asso[i].time_stamp){
victim_stamp=asso[i].time_stamp;
victim_index=i;
}
}
evictions++;
asso[victim_index].tag_bit=tag;
printf("evict\n");
}
void parse_trace(char* filename,int S,int E,int s,int b,int isVerbose){
FILE* fp;
fp = fopen(filename,"r");
if(fp==NULL) printf("Error reading file!");
char op;unsigned int addr;unsigned int offset;
while(fscanf(fp," %c %d,%d",&op,&addr,&offset)!=EOF){
unsigned int tag=addr>>(s+b);
unsigned int sets_index=(addr>>b)&( (unsigned int) (pow(2,s)-1) );
if(op=='I') continue;
else if(op=='L') {
if(isVerbose) printf("%c %d,%d ",op,addr,offset);
exec(tag,sets_index,E,isVerbose);
}
else if(op=='M') {
if(isVerbose) printf("%c %d,%d ",op,addr,offset);
exec(tag,sets_index,E,isVerbose);
exec(tag,sets_index,E,isVerbose);
}
else continue;
// printf("%c,%d,%d",op,addr,offset);
}
fclose(fp);
for(int i=0;i<S;i++){
free(_cache[i]);
}
free(_cache);
}
int main(int argc,char* argv[]){
if(argc==1){
printf("Wrong input!");
return 0;
}
int s;int E;int b;int isVerbose=0;char filename[maxi];
parse_command(argc,argv,&s,&E,&b,filename,&isVerbose);
// printf("%s",filename);
int S=pow(2,s);int B=pow(2,b);
/*test for function parse_command;
printf("%d,%d,%d,%d,%s",S,E,B,isVerbose,filename);
*/
init(S,E);
/*test for function init;
for(int i=0;i<S;i++){
for(int j=0;j<E;j++){
printf("%d,%d\n",_cache[i][j].tag_bit,_cache[i][j].valid);
}
}
*/
/*
test for function bi2decimal
printf("%d\n",bi2decimal(11));
*/
parse_trace(filename,S,E,s,b,isVerbose);
printf("hits:%d, evictions:%d, mises:%d",hits,evictions,mises);
return 0;
}