源文件及解决方法: https://github.com/xuzhezhaozhao/CSAPP-Labs
实验分为两部分
Part A:
这一部分是写一个小的C语言程序, 通过命令行输入参数s, b, E和traces文件, traces文件为内存读取过程
文件, 分析内存读取过程中hit, miss和eviction的次数.
看完文档之后发现文档中没有提及eviction的策略, 就是如果set中lines是满的的话应该选择替换哪一行.
于是修改了一个traces文件进行测试, 修改文件如下:
L 10,1
M 20,1
L 22,1
S 18,1
L 110,1
L 210,1
M 12,1
L 13,1
L 211,1
L 123,1
L 223,1
L 21,1
L 22,1
L 223,1
L 123,1
L 22,1
测试输入参数:
# ./csim-ref -v -s 4 -E 2 -b 4 -t traces/yi.trace
输出为:
L 10,1 miss
M 20,1 miss hit
L 22,1 hit
S 18,1 hit
L 110,1 miss
L 210,1 miss eviction
M 12,1 miss eviction hit
L 13,1 hit
L 211,1 hit
L 123,1 miss
L 223,1 miss eviction
L 21,1 miss eviction
L 22,1 hit
L 223,1 hit
L 123,1 miss eviction
L 22,1 miss eviction
hits:8 misses:10 evictions:6
分析之后得出所用的策略为替换最久没有读取过的行.
因为是模拟cache存储器的工作过程, 我们也可以把程序分为几个步骤:
1. 从地址参数中提取出set bits并转换为十进制以选择set;
2. 从地址参数中提取出tag bits并转换为十进制以选择line;
3. 判断该地址块中的内存是否在cache中, 当miss的时候如果line已经满了还要选择要被替换的行.
先定义数据结构:
- typedef struct {
- int valid; /* valid bit per line */
- int tag; /* tag of a line */
- int access_time; /* 取值大小为1~line_num, 为1时说明是要被替换的 */
- }line;
- typedef struct {
- line *lines;
- }set;
- typedef struct {
- int set_num; /* cache sets num */
- int line_num; /* lines per set */
- set *sets;
- }simu_cache;
前两步可以用下面两个函数实现:
- /**
- * 返回地址addr中的set bits的十进制形式
- */
- int getSetBits(int addr, int s, int b)
- {
- int mask;
- mask = 0x7fffffff >> (31 - s);
- addr = addr >> b;
- return (mask & addr);
- }
- /**
- * 返回addr的tag位十进制形式
- */
- int getTagBits(int addr, int s, int b)
- {
- int mask;
- mask = 0x7fffffff >> (31 - s - b);
- addr = addr >> (s + b);
- return (mask & addr);
- }
具体实现如下:
- /**
- * 更新lines的访问时间
- */
- int updateAccessTime(simu_cache *cache, int selset, int cl)
- {
- int i;
- for (i = 0; i < cache->line_num; i++) {
- if (1 == cache->sets[selset].lines[i].valid &&
- cache->sets[selset].lines[i].access_time >
- cache->sets[selset].lines[cl].access_time) {
- --cache->sets[selset].lines[i].access_time;
- }
- }
- cache->sets[selset].lines[cl].access_time = cache->line_num;
- return 0;
- }
最后的解析函数如下:
- int parse_traces(simu_cache *cache, char *line_buf, int s, int E, int b, int flag)
- {
- int i;
- char opt;
- int addr;
- int selset, tag;
- sscanf(line_buf, " %c %x", &opt, &addr);
- selset = getSetBits(addr, s, b);
- tag = getTagBits(addr, s, b);
- for (i = 0; i < cache->line_num; i++) {
- if (1 == cache->sets[selset].lines[i].valid &&
- tag == cache->sets[selset].lines[i].tag) {
- /* Hit immediately */
- if ('M' == opt) {
- ++hits;
- ++hits;
- } else {
- ++hits;
- }
- updateAccessTime(cache, selset, i);
- return HIT;
- }
- }
- /* Not Hit */
- ++misses;
- for (i = 0; i < cache->line_num; i++) {
- if (0 == cache->sets[selset].lines[i].valid) {
- /* there is a empty line, No eviction */
- cache->sets[selset].lines[i].valid = 1;
- cache->sets[selset].lines[i].tag = tag;
- updateAccessTime(cache, selset, i);
- if ('M' == opt) {
- ++hits;
- return MISS_AND_HIT;
- } else {
- return MISS;
- }
- }
- }
- /* Need eviction */
- ++evictions;
- for (i = 0; i < cache->line_num; i++) {
- if (1 == cache->sets[selset].lines[i].access_time) {
- cache->sets[selset].lines[i].valid = 1;
- cache->sets[selset].lines[i].tag = tag;
- updateAccessTime(cache, selset, i);
- if ('M' == opt) {
- ++hits;
- return MISS_EVICTION_AND_HIT;
- } else {
- return MISS_AND_EVICTION;
- }
- }
- }
- return 0;
- }
Part B:
这部分的任务是根据给定的cache对给定大小的矩阵相乘运算进行优化, 以减少misses的次数. 由于在性能分析的时候要用到valgrind软件生成traces文件, 所以要先下载安装valgrind包, ubuntu下的安装命令为:
sudo apt-get valgrind.