cache lab 缓存实验
从CSAPP上面下载对应的lab代码
http://csapp.cs.cmu.edu/3e/labs.html
需要安装 valgrind
。可以参考文章Valgrind centos。
安装好以后执行valgrind --version
可以看到版本号。
cache simulator not a cache
。我们不是实现一个真正的缓存,只是实现一个模拟器。Hints
要求我们实现csim.c
文件,给了一个示例csim-ref
文件。
输入./csim-ref -h
可以看到我们要实现的东西。
首先需要接受参数,参数有
L 10,1 miss
这些信息根据上面的提示可以知道,通过getopt
函数来接收参数,并通过switch来处理。读取文件则通过fscanf
函数,来读取-t传的文件。
下面是./traces/yi.trace
文件的内容
L 10,1
M 20,1
L 22,1
S 18,1
L 110,1
L 210,1
M 12,1
完整代码
#include "cachelab.h"
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct{
int valid;
int tag;
int time_stamp;
} cache_line;
int timestamp = 0;
// 开始匹配到合适的set 找到命中的cache,如果命中返回1,如果没有命中返回0
int find_hit_cache(cache_line *cache_line,int E,int tag, int*hits) {
int isHit = 0;
// 循环set中的cache_line 找到是否有匹配tag && valid
for(int i = 0; i < E; i++) {
if (cache_line[i].valid == 1 && cache_line[i].tag == tag ) {
//hit
printf("hit \n");
*hits = *hits + 1;
isHit = 1;
//刷新时间
cache_line[i].time_stamp = timestamp;
return isHit;
}
}
return isHit;
}
// 找到一个空的cache line放进去,找到了就返回1,没有找到就返回0
int find_empty_cache(cache_line *cache_line,int E,int tag) {
int have_empty_cache = 0;
for (int i = 0; i< E; i++) {
if (cache_line[i].valid == 0) {
// 空的
// 把当前内存放入cache
cache_line[i].valid = 1;
cache_line[i].tag = tag;
cache_line[i].time_stamp = timestamp;
// 找到了就不需要替换了
have_empty_cache = 1;
return have_empty_cache;
}
}
return have_empty_cache;
}
// 获取要替换的索引
int get_eviction_index(cache_line *cache_line, int E) {
int max_time_stamp = timestamp;
int eviction_index = -1;
for (int i = 0; i< E; i++) {
if (cache_line[i].time_stamp < max_time_stamp) {
//找到time_stamp最小的那个
max_time_stamp = cache_line[i].time_stamp;
eviction_index = i;
}
}
return eviction_index;
}
// LRU替换
void LRU(cache_line *cache_line, int E,int tag) {
// 获取要替换的索引
int eviction_index = get_eviction_index(cache_line, E);
// 替换
cache_line[eviction_index].valid = 1;
cache_line[eviction_index].tag = tag;
cache_line[eviction_index].time_stamp = timestamp;
}
// L和S操作,M就调用两次这个
int load_and_store(unsigned address,int b,int s,int u_max,int E,cache_line **cache, int *hits,int *misses,int *evications) {
// 获取set index block offset tag
int set_index,tag;
set_index = (address >> b) & u_max;
tag = (address >> b) >> s;
// 开始匹配到合适的set 找到命中的cache,如果命中返回1,如果没有命中返回0
int isHit = find_hit_cache(cache[set_index], E, tag, hits);
if (isHit == 0) {
// miss
printf("miss \n");
*misses = *misses + 1;
// 找到一个空的cache line放进去,找到了就返回1,没有找到就返回0
int have_empty_cache = find_empty_cache(cache[set_index], E, tag);
// 如果没有找到空的cache,就需要LRU替换
if (have_empty_cache == 0) {
printf("evictions \n");
*evications = *evications + 1;
//LRU替换
LRU(cache[set_index], E, tag);
}
}
// 更新全局时间戳
timestamp++;
return 0;
}
int main(int argc, char** argv)
{
// 接受参数 getopt
int opt,v,s,E,b,S,B;
// 文件
FILE * pFile;
while(-1 != (opt = getopt(argc, argv, "h?v?s:E:b:t:"))){
// opt is h,v,s,E,b,t的ASCII码值
// 通过switch对不同的参数进行不同的处理
switch(opt) {
case 'h':
printf("./csim: Missing required command line argument \n Usage: ./csim-ref [-hv] -s <num> -E <num> -b <num> -t <file> \n Options: \n -h Print this help message. \n -v Optional verbose flag. \n -s <num> Number of set index bits. \n -E <num> Number of lines per set. \n -b <num> Number of block offset bits. \n -t <file> Trace file. \n\n Examples: \n ./csim -s 4 -E 1 -b 4 -t traces/yi.trace \n ./csim -v -s 8 -E 2 -b 4 -t traces/yi.trace \n");
// h参数输出帮助内容
break;
case 'v':
// v参数输出详细信息
v = 1;
printf("v:%d \n",v);
break;
case 's':
// S is set 2^s 的数量
// s is Number of set index bits
s = atoi(optarg);
S = 1 << s;
printf("s:%d, S:%d \n",s,S);
break;
case 'E':
// E is cache line 的数量
// Number of lines per set
E = atoi(optarg);
printf("E:%d \n",E);
break;
case 'b':
// B is block data 的字节
// b is Number of block offset bits
b = atoi(optarg);
B = 1 << b;
printf("b:%d, B:%d \n",b,B);
break;
case 't':
// t is Trace file
// 读取文件
//t = atoi(optarg);
pFile = fopen(optarg,"r");
printf("t:%s, file:%p \n",optarg,pFile);
break;
default:
printf("非法参数 \n");
break;
}
}
if(s == 0 || E == 0 || b == 0) {
return 0;
}
// cache存储
cache_line **cache = (cache_line **)malloc(S * sizeof(cache_line *));
if (cache == NULL) {
printf("内存分配失败 \n");
}
for(int i = 0; i < S; i++) {
cache[i] = (cache_line *)malloc(E * sizeof(cache_line));
if(cache[i] == NULL) {
printf("内存分配失败,开始回滚 \n");
// 在这里需要释放已分配的内存,然后退出
for (int j = 0; j < i; ++j) {
free(cache[j]);
}
free(cache);
}
}
int u_max = 1;
for(int i = 0; i < s - 1; i++) {
u_max = (u_max << 1) | 1;
}
// 读取文件
char identifier;
unsigned address;
int size;
int hits,misses,evictions;
// Reading lines like " M 20,1" or "L 19,3"
while(fscanf(pFile," %c %x,%d",&identifier,&address,&size)>0)
{
// Do stuff
// 开始计算 hits,misses,evictions, hits:0 misses:0 evictions:0
//printf("identifier %c, addr:%x, size:%d \n",identifier,address,size);
//根据identifier来判断动作L load S store M = 一次L 一次S
if (identifier == 'L' || identifier == 'S'){
printf("identifier %c, addr:%x, size:%d \n",identifier,address,size);
load_and_store(address,b,s,u_max,E,cache,&hits,&misses,&evictions);
} else if (identifier == 'M') {
// 一次L 一次S
printf("identifier %c, addr:%x, size:%d \n",identifier,address,size);
load_and_store(address,b,s,u_max,E,cache,&hits,&misses,&evictions);
load_and_store(address,b,s,u_max,E,cache,&hits,&misses,&evictions);
}
}
fclose(pFile); //remember to close file when done
printSummary(hits, misses, evictions);
// 释放数组内存
for(int i = 0; i< S; i++) {
free(cache[i]);
}
free(cache);
return 0;
}