CSAPP: cache lab
来源:互联网 发布:周恩来书法 知乎 编辑:程序博客网 时间:2024/06/04 21:03
CSAPP: cache lab
Part A
自制一个符合以下要求的 cache:
- 以
−s,−E,−b 为选项的参数来建立一个2s 组,每组E 行,每个块2b 字节的 cache - 以
−t 为选项的参数作为输入文件路径,逐条读入指令 - 以
LRU 作为驱逐策略,模拟出 cache 命中、不命中与驱逐行为,并统计各种行为的总数 - 以
−v 选项的存在与否,决定每条指令的命中情况是否显示 - 以
−h 选项的存在与否,决定是否显示选项的用法信息
数据结构
链表
用链表来实现 cache 中,每一组的 LRU 驱逐策略
/* 节点结构定义 */typedef struct Node{ int data; struct Node* next;} Node;/* 新建节点函数 */Node* newNode(int data);/* 链表定义 */typedef struct{ Node* head; Node* tail;} LList;/* 链表初始化函数 */void initLList(LList* list);/* 尾部插入包含指定数据的节点 */void insertTail(LList* list, int d);/* 删除表头节点,并返回其数据元素 */int deleteFront(LList* list);/* 删除包含指定数据的节点 */void deleteData(LList* list, int d);
行
/* 行结构定义 */typedef struct{ int v; // 有效位 int tag; // 标志} Line;/* 行初始化函数 */void initLine(Line* ln){ ln -> v = 0; ln -> tag = 0;}/* 行定位函数 */int locateLine(Line* ln,int tag){ if(!ln -> v) return 0; if(ln -> tag == tag) return 1; else return 0;}/* 加载块函数 */void loadBlock(Line* ln,int tag){ ln -> v = 1; // 有效位置1 ln -> tag = tag; // 覆盖标志}
组
用 LRUList 链表记录组内各行的 LRU 信息
每一次命中或是驱逐,就将相应的行索引号置于表尾
冷不命中,则将新的行索引号插入表尾
驱逐时,被驱逐行的索引号为表头的数据元素,然后调整该链表
/* 组结构定义 */typedef struct{ int E; // 行数 Line* ln; // 首行指针 bool isFull; // 组是否已满 LList* LRUList; // LRU表 // 表头元素的值总是组内最近最少使用的行的索引号} Set;/* 组初始化函数 */void initSet(Set* st, int E){ Line* ln = (Line*)malloc(sizeof(Line) * E); // 一组E行 for(int i = 0;i < E;i++){ // 逐行初始化 initLine(ln + i); } // 用链表保存各行的最近访问时序 LList* list = (LList*)malloc(sizeof(LList)); initLList(list); st -> isFull = false; st -> ln = ln; st -> E = E; st -> LRUList = list;}/* 组内空行查找函数 */int findInvalidLine(Set* st){ int n = st -> E; Line* ln = st -> ln; for(int i = 0;i < n;i++) if((ln + i) -> v == 0) return i; return -1;}/* 组定位函数 命中,返回1; 冷不命中,返回0 冲突不命中,返回-1;*/int locateSet(Set* st,int tag){ // 命中 int n = st -> E; for(int i = 0;i < n;i++) // 是否成功定位在某行 if(locateLine(st -> ln + i, tag)){ // 在LRU表中,删除数据元素i deleteData(st -> LRUList, i); // 将行索引号i插入LRU表的尾部 insertTail(st -> LRUList, i); return 1; } // 冷不命中 if(!st -> isFull){ // 查找第一个无效行的索引 int idx = findInvalidLine(st); // 向该行加载数据块 loadBlock(st -> ln + idx, tag); // 将行索引号idx插入到LRU表的尾部 insertTail(st -> LRUList, idx); if(idx == n - 1) // 组满,更新isFull st -> isFull = true; return 0; } // 冲突不命中 // 删除LRU表的表头元素,并返回其值给idx int idx = deleteFront(st -> LRUList); // 向该驱逐行中,加载新的数据块 loadBlock(st -> ln + idx, tag); // 将行索引号idx插入到LRU表的尾部 insertTail(st -> LRUList, idx); return -1;}
Cache
主要被外部调用的函数是 locateCache
接受一个 Cache 的指针和一个 unsigned 地址,对指令中的字节数不做处理,即:默认每次单字节访问
命中,返回 1
冷不命中,返回 0
冲突不命中,返回 -1
/* Cache结构定义 */typedef struct{ int B; // 块的字节数 int S; // 组数 Set* st; // 首组指针} Cache;/* Cache初始化函数 */void initCache(Cache* cache,int S,int E,int B){ Set* st = (Set*)malloc(sizeof(Set) * S); // S组 for(int i = 0;i < S;i++) // 初始化每组 initSet(st + i, E); cache -> st = st; cache -> S = S; cache -> B = B;}/* Cache定位函数 */int locateCache(Cache* cache,unsigned addr){ unsigned indexOfBlocks = addr / cache -> B; // 块索引 int CI = indexOfBlocks % cache -> S; // 组索引 int CT = indexOfBlocks / cache -> S; // 标记 return locateSet(cache -> st + CI, CT);}
对于行、组、Cache,
每一个层次都定义了其结构和处理函数;
每一层的处理函数,只调用下一层的处理函数;
main函数
选项参数处理
isVisible 对应 -v 选项,为 true时 ,则后续读指令时,输出每一条指令的 cache 行为
hasOpt 为 false,表示不带选项运行,后续输出选项参数的用法,然后返回
S,E,B 对应 -s,-E,-b 选项,解释为 cache 的参数
pFile 对应 -t 选项,接收一个字符串,建立对特定文件的读管道
bool isVisible = false; // 是否可追踪每条指令的命中情况 bool hasOpt = false; // 是否有选项 int opt, S, E, B; FILE* pFile; while(-1 != (opt = getopt(argc, argv, "s:E:b:t:vh"))){ hasOpt = true; // 附带选项运行 switch(opt) { case 's': S = 1 << atoi(optarg); break; case 'E': E = atoi(optarg); // 字符串转整数函数 break; case 'b': B = 1 << atoi(optarg); break; case 'v': isVisible = true; break; case 't': pFile = fopen(optarg,"r"); break; case 'h': printUsage(); // 输出选项用法 return 0; default: // 无效选项 printf("Missing required command line argument"); printUsage(); // 输出选项用法 return 0; } } if(!hasOpt){ // 无选项情况 printf("./csim: Missing required command line argument\n"); printUsage(); // 输出选项用法 return 0; }
读指令,模拟缓存行为
fscanf 将指令操作符、指令地址、操作字节数读入变量 ident,addr,size
t 根据操作符,确定访问 cache 的次数,即:locateCache函数的调用次数
根据 locateCache 的返回值,进行计数
// 创建cache,并初始化 Cache *c = (Cache*)malloc(sizeof(Cache)); initCache(c,S,E,B); hit_count = miss_count = eviction_count = 0; while(fscanf(pFile," %c %x,%d",&ident, &addr, &size) > 0){ int t; // locateCache函数调用次数 if(ident == 'L' || ident == 'S') t = 1; else if(ident == 'M') t = 2; else t = 0; if(t == 0) continue; if(isVisible) printf("%c %x,%d",ident, addr, size); while(t--){ int msg = locateCache(c,addr); if(msg == 1) hit_count += 1; else if(msg == 0) miss_count += 1; else{ miss_count += 1; eviction_count += 1; } if(isVisible) show(msg); } if(isVisible) printf("\n"); }
Part B
编写一个函数,进行矩阵转置
void transpose_submit(int M, int N, int A[N][M], int B[M][N]);
满足:
- 最多只能定义 12 个局部变量
- 不使用任何的数组,不调用任何类似于 malloc 的开辟内存的函数
- 测试矩阵的规模为 32 × 32,64 × 64,61 × 67
- 测试 cache 的组成:32 组, 每组一行,每个块 32 字节。对于编写的函数,miss 个数越少越好
Version 1
分块处理,尝试 32 × 32,16 × 16,8 × 8,4 × 4 地分块处理
效果最好的是 8×8,大于 8×8 的分块没有效果
其原因是:
测试 cache,每块字节数是 32,也就最多存储 8 个 int;
而低于 8×8 的,则没有有效利用 cache 的块容量大小 。
void transpose_submit_1(int M, int N, int A[N][M], int B[M][N]){ int i, j, ii, jj, tmp; int n = N / 8 * 8; int m = M / 8 * 8; // 分块后可处理的部分:[0,0]~[n-1,m-1] for (i = 0;i < n; i += 8) { for (j = 0;j < m;j += 8) for(ii = 0;ii < 8;ii++) for(jj = 0;jj < 8;jj++){ tmp = A[i+ii][j+jj]; B[j+jj][i+ii] = tmp; } } // 分块后,剩余部分的处理 for(i = n;i < N;i++) for(j = m;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = 0;i < N;i++) for(j = m;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = n;i < N;i++) for(j = 0;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; }}
Version 2
在 8 × 8 的基础上,对其平均四分
先处理左上角,再处理右上角,然后处理右下角,最后处理左下角
void transpose_submit_2(int M, int N, int A[N][M], int B[M][N]){ int i, j, ii, jj, tmp; int n = N / 8 * 8; int m = M / 8 * 8; // 分块后可处理的部分:[0,0]~[n-1,m-1] for (i = 0; i < n; i += 8) { for (j = 0; j < m; j += 8) { // 左上角 for(ii = 0;ii < 4;ii++){ for(jj = 0;jj < 4;jj++){ tmp = A[i+ii][j+jj]; B[j+jj][i+ii] = tmp; } } // 右上角 for(ii = 0;ii < 4;ii++){ for(jj = 4;jj < 8;jj++){ tmp = A[i+ii][j+jj]; B[j+jj][i+ii] = tmp; } } // 右下角 for(ii = 4;ii < 8;ii++){ for(jj = 4;jj < 8;jj++){ tmp = A[i+ii][j+jj]; B[j+jj][i+ii] = tmp; } } // 左下角 for(ii = 4;ii < 8;ii++){ for(jj = 0;jj < 4;jj++){ tmp = A[i+ii][j+jj]; B[j+jj][i+ii] = tmp; } } } } // 分块后,剩余部分的处理 for(i = n;i < N;i++) for(j = m;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = 0;i < N;i++) for(j = m;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = n;i < N;i++) for(j = 0;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; }}
64 × 64 有了分数,但 32 × 32 和 61 × 67 规模的略有下降
Version 3
在 Version 2 的基础上,引入 4 个局部变量直接处理 A矩阵 4 × 4 规模中的一行
void transpose_submit_3(int M, int N, int A[N][M], int B[M][N]){ int i, j, ii, jj, tmp, tmp1, tmp2, tmp3; int n = N / 8 * 8; int m = M / 8 * 8; // 分块后可处理的部分:[0,0]~[n-1,m-1] for (i = 0; i < n; i += 8) { for (j = 0; j < m; j += 8) { // 左上角 jj = 0; for(ii = 0;ii < 4;ii++){ tmp = A[i+ii][j+jj]; tmp1 = A[i+ii][j+jj+1]; tmp2 = A[i+ii][j+jj+2]; tmp3 = A[i+ii][j+jj+3]; B[j+jj][i+ii] = tmp; B[j+jj+1][i+ii] = tmp1; B[j+jj+2][i+ii] = tmp2; B[j+jj+3][i+ii] = tmp3; } // 右上角 jj = 4; for(ii = 0;ii < 4;ii++){ tmp = A[i+ii][j+jj]; tmp1 = A[i+ii][j+jj+1]; tmp2 = A[i+ii][j+jj+2]; tmp3 = A[i+ii][j+jj+3]; B[j+jj][i+ii] = tmp; B[j+jj+1][i+ii] = tmp1; B[j+jj+2][i+ii] = tmp2; B[j+jj+3][i+ii] = tmp3; } // 右下角 for(ii = 4;ii < 8;ii++){ tmp = A[i+ii][j+jj]; tmp1 = A[i+ii][j+jj+1]; tmp2 = A[i+ii][j+jj+2]; tmp3 = A[i+ii][j+jj+3]; B[j+jj][i+ii] = tmp; B[j+jj+1][i+ii] = tmp1; B[j+jj+2][i+ii] = tmp2; B[j+jj+3][i+ii] = tmp3; } // 左下角 jj = 0; for(ii = 4;ii < 8;ii++){ tmp = A[i+ii][j+jj]; tmp1 = A[i+ii][j+jj+1]; tmp2 = A[i+ii][j+jj+2]; tmp3 = A[i+ii][j+jj+3]; B[j+jj][i+ii] = tmp; B[j+jj+1][i+ii] = tmp1; B[j+jj+2][i+ii] = tmp2; B[j+jj+3][i+ii] = tmp3; } } } // 分块后,剩余部分的处理 for(i = n;i < N;i++) for(j = m;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = 0;i < N;i++) for(j = m;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = n;i < N;i++) for(j = 0;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; }}
各个规模的分数均有提升,局部变量的引入效果不错
Version 4
既然引入更多的局部变量,收效不错。
那么改变分块结构,引入更多的局部变量,可能得到更好的效益
题目本身限制,只能引入 12 个局部变量
而 cache 一块最多只能存储 8 个 int,故用于暂存值的局部变量数最大为 8
void transpose_submit_4(int M, int N, int A[N][M], int B[M][N]){ int i, j, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; int n = N / 8 * 8; int m = M / 8 * 8; // 分块后可处理的部分:[0,0]~[n-1,m-1] for (j = 0; j < m; j += 8) { for (i = 0; i < n; i++) { tmp = A[i][j]; tmp1 = A[i][j+1]; tmp2 = A[i][j+2]; tmp3 = A[i][j+3]; tmp4 = A[i][j+4]; tmp5 = A[i][j+5]; tmp6 = A[i][j+6]; tmp7 = A[i][j+7]; B[j][i] = tmp; B[j+1][i] = tmp1; B[j+2][i] = tmp2; B[j+3][i] = tmp3; B[j+4][i] = tmp4; B[j+5][i] = tmp5; B[j+6][i] = tmp6; B[j+7][i] = tmp7; } } // 分块后,剩余部分的处理 for(i = n;i < N;i++) for(j = m;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = 0;i < N;i++) for(j = m;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = n;i < N;i++) for(j = 0;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; }}
32 × 32,61 × 67 规模达到满分;而 64 × 64 则变成了诡异的 0 分
喜忧参半!!!
Version 5
考虑到 Version 3 处理 64 × 64 的效果很好
与 Version 4 综合,就得到了 Version 5,也是提交版本
因为局部变量使用数量的限制,那么牺牲一些可读性,用表达式代替一些中间变量的使用
void transpose_submit(int M, int N, int A[N][M], int B[M][N]){ int i, j, ii, jj, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; if(N == 64 && M == 64){ // transpos_submit_3 for (i = 0; i < N / 8 * 8; i += 8) { for (j = 0; j < M / 8 * 8; j += 8) { // 左上角 jj = 0; for(ii = 0;ii < 4;ii++){ tmp = A[i+ii][j+jj]; tmp1 = A[i+ii][j+jj+1]; tmp2 = A[i+ii][j+jj+2]; tmp3 = A[i+ii][j+jj+3]; B[j+jj][i+ii] = tmp; B[j+jj+1][i+ii] = tmp1; B[j+jj+2][i+ii] = tmp2; B[j+jj+3][i+ii] = tmp3; } // 右上角 jj = 4; for(ii = 0;ii < 4;ii++){ tmp = A[i+ii][j+jj]; tmp1 = A[i+ii][j+jj+1]; tmp2 = A[i+ii][j+jj+2]; tmp3 = A[i+ii][j+jj+3]; B[j+jj][i+ii] = tmp; B[j+jj+1][i+ii] = tmp1; B[j+jj+2][i+ii] = tmp2; B[j+jj+3][i+ii] = tmp3; } // 右下角 for(ii = 4;ii < 8;ii++){ tmp = A[i+ii][j+jj]; tmp1 = A[i+ii][j+jj+1]; tmp2 = A[i+ii][j+jj+2]; tmp3 = A[i+ii][j+jj+3]; B[j+jj][i+ii] = tmp; B[j+jj+1][i+ii] = tmp1; B[j+jj+2][i+ii] = tmp2; B[j+jj+3][i+ii] = tmp3; } // 左下角 jj = 0; for(ii = 4;ii < 8;ii++){ tmp = A[i+ii][j+jj]; tmp1 = A[i+ii][j+jj+1]; tmp2 = A[i+ii][j+jj+2]; tmp3 = A[i+ii][j+jj+3]; B[j+jj][i+ii] = tmp; B[j+jj+1][i+ii] = tmp1; B[j+jj+2][i+ii] = tmp2; B[j+jj+3][i+ii] = tmp3; } } } }else{ // transpos_submit_4 for (j = 0; j < M / 8 * 8; j += 8) { for (i = 0; i < N / 8 * 8; i++) { tmp = A[i][j]; tmp1 = A[i][j+1]; tmp2 = A[i][j+2]; tmp3 = A[i][j+3]; tmp4 = A[i][j+4]; tmp5 = A[i][j+5]; tmp6 = A[i][j+6]; tmp7 = A[i][j+7]; B[j][i] = tmp; B[j+1][i] = tmp1; B[j+2][i] = tmp2; B[j+3][i] = tmp3; B[j+4][i] = tmp4; B[j+5][i] = tmp5; B[j+6][i] = tmp6; B[j+7][i] = tmp7; } } } // 分块后,剩余部分的处理 for(i = N / 8 * 8;i < N;i++) for(j = M / 8 * 8;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = 0;i < N;i++) for(j = M / 8 * 8;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; } for(i = N / 8 * 8;i < N;i++) for(j = 0;j < M;j++){ tmp = A[i][j]; B[j][i] = tmp; }}
目前得到的最好分数 ~_~
附录
### Part A
LList.h
// LList.h#include <stdlib.h>#include <stdio.h>#ifndef LLIST_H#define LLIST_H/* 节点结构定义 */typedef struct Node{ int data; struct Node* next;} Node;/* 新建节点函数 */Node* newNode(int data){ Node* tmp = (Node*)malloc(sizeof(Node)); tmp -> data = data; tmp -> next = 0; return tmp;}/* 链表定义 */typedef struct{ Node* head; Node* tail;} LList;/* 链表初始化函数 */void initLList(LList* list){ list -> head = list -> tail = newNode(0);}/* 尾部插入包含指定数据的节点 */void insertTail(LList* list, int d){ Node* tmp = newNode(d); list -> tail -> next = tmp; list -> tail = list -> tail -> next;}/* 删除表头节点,并返回其数据元素 */int deleteFront(LList* list){ Node* tmp = list -> head -> next; if(!tmp) return -1; list -> head -> next = tmp -> next; int data = tmp -> data; if(tmp == list -> tail) list -> tail = list -> head; free(tmp); return data;}/* 删除包含指定数据的节点 */void deleteData(LList* list, int d){ Node* curr = list -> head; Node* tail = list -> tail; while(curr != tail){ if(curr -> next -> data == d) break; curr = curr -> next; } if(curr == list -> tail) return; Node* tmp = curr -> next; if(tmp == list -> tail) list -> tail = curr; curr -> next = tmp -> next; free(tmp);}/* 输出链表所有节点内的数据 */void print(LList* list){ Node* curr = list -> head; Node* tail = list -> tail; printf("< "); while(curr != tail){ printf("%d ", curr -> next -> data); curr = curr -> next; } printf(">\n");}#endif
Cache.h
// Cache.h#include <stdlib.h>#include <stdbool.h>#include "LList.h"#ifndef CACHE_E#define CACHE_E/* 行结构定义 */typedef struct{ int v; // 有效位 int tag; // 标志} Line;/* 行初始化函数 */void initLine(Line* ln){ ln -> v = 0; ln -> tag = 0;}/* 行定位函数 */int locateLine(Line* ln,int tag){ if(!ln -> v) return 0; if(ln -> tag == tag) return 1; else return 0;}/* 加载块函数 */void loadBlock(Line* ln,int tag){ ln -> v = 1; // 有效位置1 ln -> tag = tag; // 覆盖标志}/* 组结构定义 */typedef struct{ int E; // 行数 Line* ln; // 首行指针 bool isFull; // 组是否已满 LList* LRUList; // LRU表 // 表头元素的值总是组内最近最少使用的行的索引号} Set;/* 组初始化函数 */void initSet(Set* st, int E){ Line* ln = (Line*)malloc(sizeof(Line) * E); // 一组E行 for(int i = 0;i < E;i++){ // 逐行初始化 initLine(ln + i); } // 用链表保存各行的最近访问时序 LList* list = (LList*)malloc(sizeof(LList)); initLList(list); st -> isFull = false; st -> ln = ln; st -> E = E; st -> LRUList = list;}/* 组内空行查找函数 */int findInvalidLine(Set* st){ int n = st -> E; Line* ln = st -> ln; for(int i = 0;i < n;i++) if((ln + i) -> v == 0) return i; return -1;}/* 组定位函数 命中,返回1; 冷不命中,返回0 冲突不命中,返回-1;*/int locateSet(Set* st,int tag){ // 命中 int n = st -> E; for(int i = 0;i < n;i++) // 是否成功定位在某行 if(locateLine(st -> ln + i, tag)){ // 在LRU表中,删除数据元素i deleteData(st -> LRUList, i); // 将行索引号i插入LRU表的尾部 insertTail(st -> LRUList, i); return 1; } // 冷不命中 if(!st -> isFull){ // 查找第一个无效行的索引 int idx = findInvalidLine(st); // 向该行加载数据块 loadBlock(st -> ln + idx, tag); // 将行索引号idx插入到LRU表的尾部 insertTail(st -> LRUList, idx); if(idx == n - 1) // 组满,更新isFull st -> isFull = true; return 0; } // 冲突不命中 // 删除LRU表的表头元素,并返回其值给idx int idx = deleteFront(st -> LRUList); // 向该驱逐行中,加载新的数据块 loadBlock(st -> ln + idx, tag); // 将行索引号idx插入到LRU表的尾部 insertTail(st -> LRUList, idx); return -1;}/* Cache结构定义 */typedef struct{ int B; // 块的字节数 int S; // 组数 Set* st; // 首组指针} Cache;/* Cache初始化函数 */void initCache(Cache* cache,int S,int E,int B){ Set* st = (Set*)malloc(sizeof(Set) * S); // S组 for(int i = 0;i < S;i++) // 初始化每组 initSet(st + i, E); cache -> st = st; cache -> S = S; cache -> B = B;}/* Cache定位函数 */int locateCache(Cache* cache,unsigned addr){ unsigned indexOfBlocks = addr / cache -> B; // 块索引 int CI = indexOfBlocks % cache -> S; // 组索引 int CT = indexOfBlocks / cache -> S; // 标记 return locateSet(cache -> st + CI, CT);}#endif
csim.c
#include <getopt.h>#include <stdio.h>#include <stdlib.h>#include <stdbool.h>#include <stdio.h>#include "cachelab.h"#include "cache.h"void show(int msg){ if(msg == 1) printf(" hit"); else if(msg == 0) printf(" miss"); else printf(" miss eviction");}/* 选项用法打印函数 */void printUsage(){ printf("Usage: ./csim [-hv] -s <num> -E <num> -b <num> -t <file>\n"); printf("Options:\n"); printf(" -h Print this help message.\n"); printf(" -v Optional verbose flag.\n"); printf(" -s <num> Number of set index bits.\n"); printf(" -E <num> Number of lines per set.\n"); printf(" -b <num> Number of block offset bits.\n"); printf(" -t <file> Trace file.\n"); printf("\n"); printf("Examples:\n"); printf(" linux> ./csim -s 4 -E 1 -b 4 -t traces/yi.trace\n"); printf(" linux> ./csim -v -s 8 -E 2 -b 4 -t traces/yi.trace\n");}int main(int argc,char *argv[]){ // freopen("test/test.txt","w",stdout); bool isVisible = false; // 是否可追踪每条指令的命中情况 bool hasOpt = false; // 是否有选项 int opt, S, E, B; FILE* pFile; while(-1 != (opt = getopt(argc, argv, "s:E:b:t:vh"))){ hasOpt = true; // 附带选项运行 switch(opt) { case 's': S = 1 << atoi(optarg); break; case 'E': E = atoi(optarg); // 字符串转整数函数 break; case 'b': B = 1 << atoi(optarg); break; case 'v': isVisible = true; break; case 't': pFile = fopen(optarg,"r"); break; case 'h': printUsage(); // 输出选项用法 return 0; default: // 无效选项 printf("Missing required command line argument"); printUsage(); // 输出选项用法 return 0; } } if(!hasOpt){ // 无选项情况 printf("./csim: Missing required command line argument\n"); printUsage(); // 输出选项用法 return 0; } // 创建cache,并初始化 Cache *c = (Cache*)malloc(sizeof(Cache)); initCache(c,S,E,B); char ident; unsigned addr; int size; int hit_count, miss_count, eviction_count; hit_count = miss_count = eviction_count = 0; while(fscanf(pFile," %c %x,%d",&ident, &addr, &size) > 0){ int t; // locateCache函数调用次数 if(ident == 'L' || ident == 'S') t = 1; else if(ident == 'M') t = 2; else t = 0; if(t == 0) continue; if(isVisible) printf("%c %x,%d",ident, addr, size); while(t--){ int msg = locateCache(c,addr); if(msg == 1) hit_count += 1; else if(msg == 0) miss_count += 1; else{ miss_count += 1; eviction_count += 1; } if(isVisible) show(msg); } if(isVisible) printf("\n"); } fclose(pFile); printSummary(hit_count, miss_count, eviction_count);}
- CSAPP: Cache Lab
- CSAPP: cache lab
- CSAPP cache-lab
- CSAPP:cache lab
- csapp 实验 Cache Lab: Understanding Cache Memories
- CSAPP: cache lab (Part B未完成)
- CSAPP LAB---Proxy lab
- CSAPP: bomb lab
- CSAPP: buffer lab
- CSAPP: shell lab
- CSAPP: malloc lab
- CSAPP LAB---MALLOC实验
- CSAPP LAB---shlab-handout
- 【CSAPP】malloc Lab
- CSAPP Data Lab
- CSAPP: Malloc Lab 7
- CSAPP: Buffer Lab
- CSAPP: Architecture Lab
- 数字图像处理——用Java对数字图像取反
- MSP430定时器控制PWM输出 (MSP430入门)
- Python:类属性,实例属性,私有属性与静态方法,类方法,实例方法
- easyui 自定义表单内容验证(汉字、字母、数字、邮箱、电话、邮编、身份证号等)
- 定积分程序中应用
- CSAPP: cache lab
- adb使用连不上devices
- 深度学习的应用
- dovecot+mysql(数据库对邮件服务器的管理)
- 获取select下拉框选中的的值
- yii 2 框架 getErrors() 系列方法 详解
- Hibernate 03
- 1020. 月饼 (25)
- So Easy! ——————hdu4565(矩阵快速幂)