5. CacheLab
Part A
code:
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <inttypes.h>
#include "cachelab.h"
void usage()
{
puts("Usage: ./csim [-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"
" linux> ./csim -s 4 -E 1 -b 4 -t traces/yi.trace\n"
" linux> ./csim -v -s 8 -E 2 -b 4 -t traces/yi.trace");
}
#define STRING_LEN 256
int verbose = 0; // verbose flag
int s = 0; // number of set index bits
int S = 0; // number of sets
int E = 0; // number of lines per set
int b = 0; // number of block offset bits
char traceFile[STRING_LEN] = {0}; // trace file name
char traceLine[STRING_LEN] = {0}; // line of trace file
int hits = 0;
int misses = 0;
int evictions = 0;
unsigned int cache_counter = 0;
unsigned int cnt = 0;
typedef struct {
unsigned int valid;
unsigned long long tag;
uint8_t* data;
unsigned int last_cc; // last cache counter
} cacheLine_t;
typedef struct {
cacheLine_t *lines;
} cacheSet_t;
typedef struct {
cacheSet_t *sets;
} cache_t;
void verbose_log(char *str)
{
if (verbose)
{
printf("%s", str);
}
}
void handle_operation(cache_t cache, char op, unsigned int tag, unsigned int set_index, unsigned int offset)
{
// initial
int i;
int flag;
cacheLine_t* line;
// get target set
cacheSet_t set = cache.sets[set_index];
// check tag in cache && valid
flag = 0;
for (i = 0; i < E; i++)
{
if (set.lines[i].valid && set.lines[i].tag == tag)
{
line = &set.lines[i];
flag = 1;
break;
}
}
if (flag)
{
// hit
hits++;
verbose_log("hit ");
line->last_cc = cache_counter++;
return;
}
else
{
// miss
misses++;
verbose_log("miss ");
// check is there any invalid line
for (i = 0; i < E; i++)
{
if (0 == set.lines[i].valid)
{
line = &set.lines[i];
line->valid = 1; // load line from memory
line->tag = tag;
line->last_cc = cache_counter++; // set last cache counter
return;
}
}
// no invalid line, evict a line whose last cache counter is the smallest
line = &set.lines[0];
for (i = 1; i < E; i++)
{
if (set.lines[i].valid)
{
line = (set.lines[i].last_cc < line->last_cc) ? &set.lines[i] : line;
// break;
}
}
evictions++;
verbose_log("eviction ");
line->tag = tag;
line->last_cc = cache_counter++;
return;
}
}
void load_data(cache_t cache, unsigned int tag, unsigned int set_index, unsigned int offset)
{
char op = 'L';
handle_operation(cache, op, tag, set_index, offset);
#ifdef DEBUG
cacheLine_t line;
line = cache.sets[3].lines[0];
printf("cc: %u, op: %c, valid: %u, tag: %llu, last_cc: %u\n",
cache_counter, op, line.valid, line.tag, line.last_cc);
line = cache.sets[3].lines[1];
printf("cc: %u, op: %c, valid: %u, tag: %llu, last_cc: %u\n",
cache_counter, op, line.valid, line.tag, line.last_cc);
line = cache.sets[3].lines[2];
printf("cc: %u, op: %c, valid: %u, tag: %llu, last_cc: %u\n",
cache_counter, op, line.valid, line.tag, line.last_cc);
line = cache.sets[3].lines[3];
printf("cc: %u, op: %c, valid: %u, tag: %llu, last_cc: %u\n",
cache_counter, op, line.valid, line.tag, line.last_cc);
#endif
}
void store_data(cache_t cache, unsigned int tag, unsigned int set_index, unsigned int offset)
{
char op = 'S';
handle_operation(cache, op, tag, set_index, offset);
#ifdef DEBUG
cacheLine_t line;
line = cache.sets[3].lines[0];
printf("cc: %u, op: %c, valid: %u, tag: %llu, last_cc: %u\n",
cache_counter, op, line.valid, line.tag, line.last_cc);
line = cache.sets[3].lines[1];
printf("cc: %u, op: %c, valid: %u, tag: %llu, last_cc: %u\n",
cache_counter, op, line.valid, line.tag, line.last_cc);
line = cache.sets[3].lines[2];
printf("cc: %u, op: %c, valid: %u, tag: %llu, last_cc: %u\n",
cache_counter, op, line.valid, line.tag, line.last_cc);
line = cache.sets[3].lines[3];
printf("cc: %u, op: %c, valid: %u, tag: %llu, last_cc: %u\n",
cache_counter, op, line.valid, line.tag, line.last_cc);
#endif
}
void simulate_cache()
{
int i, j;
// initial cache
cache_counter = 0;
cache_t cache;
cache.sets = calloc(S, sizeof(cacheSet_t));
for (i = 0; i < S; i++)
{
cache.sets[i].lines = calloc(E, sizeof(cacheLine_t));
for (j = 0; j < E; j++)
{
cache.sets[i].lines[j].valid = 0;
cache.sets[i].lines[j].data = calloc(1 << b, sizeof(uint8_t));
}
}
// start simulation
// read trace file
FILE *fp = fopen(traceFile, "r");
while (fgets(traceLine, STRING_LEN, fp) != NULL)
{
if ('I' == traceLine[0])
{
continue;
}
char op;
unsigned long long addr;
unsigned int size;
sscanf(traceLine, " %c %llx,%x", &op, &addr, &size);
if (verbose)
{
printf("%c %llx,%x ", op, addr, size);
}
if (0x7ff000370 == addr)
{
cnt++;
}
// handle address
unsigned int tag;
unsigned int set_index;
unsigned int offset;
offset = addr & ((1 << b) - 1);
addr >>= b;
set_index = addr & (S - 1);
addr >>= s;
tag = addr;
// handle operation
switch (op) {
case 'L':
{
load_data(cache, tag, set_index, offset);
break;
}
case 'S':
{
store_data(cache, tag, set_index, offset);
break;
}
case 'M':
{
load_data(cache, tag, set_index, offset);
store_data(cache, tag, set_index, offset);
break;
}
default:
{
printf("error operation\n");
}
}
verbose_log("\n");
}
// free cache
for (i = 0; i < S; i++)
{
for (j = 0; j < E; j++)
{
free(cache.sets[i].lines[j].data);
}
free(cache.sets[i].lines);
}
free(cache.sets);
}
int main(int argc, char* argv[])
{
int opt;
while ((opt = getopt(argc, argv, "hvs:E:b:t:")) != -1)
{
switch (opt)
{
case 'h':
{
usage();
break;
}
case 'v':
{
verbose = 1;
#ifdef DEBUG
verbose = 0;
#endif
break;
}
case 's':
{
s = (int) strtol(optarg, NULL, 10);
S = 1 << s;
break;
}
case 'E':
{
E = (int) strtol(optarg, NULL, 10);
break;
}
case 'b':
{
b = (int) strtol(optarg, NULL, 10);
break;
}
case 't':
{
sscanf(optarg, "%s", traceFile);
break;
}
default:
{
usage();
break;
}
}
}
if (s == 0 || E == 0 || b == 0)
{
puts("Missing required parameters!");
return 0;
}
simulate_cache();
printSummary(hits, misses, evictions);
return 0;
}
Note:
在编写代码时,遇到了一些问题,这里记录下来
-
Modify operation指的是一次data load+一次data store,最开始是在handle_operation中用if判断如果为Modify operation则进行额外的操作,这样写的代码可读性很差,后来将操作封装成了load_data和store_data两个函数,在simulate_cache中,通过operation调用对应的接口,Modify则是调用一次load_data后再调用一次store_data
-
evict某个块时,忘了给新块设置last_cc,导致每次evict的都是该组数据块中第0个块,是通过在这俩个operation接口处理完操作后,打印某个组的所有块的last_cc和tag等属性才发现的这个bug(感觉还是测试的数据集太弱了)
-
evict某个块时,我的代码写成了
// no invalid line, evict a line whose last cache counter is the smallest line = &set.lines[0]; for (i = 1; i < E; i++) { if (set.lines[i].valid) { line = (set.lines[i].last_cc < line->last_cc) ? &set.lines[i] : line; break; } }
明显的逻辑错误,由break语句引起的,注释掉break;就可以了
这三个问题下来,代码结构其实是有点乱了的,不过也是懒得再重构了(…