实验准备知识
1.寄存器等级介绍
2. 主存地址
- 按字节编址,块内地址
block offset
有b位,cache组号set index
有s位,标记tag
位有address size-b-s位。
3. 缓存Cache
- cache有 2 s 2^s 2s行,一行有E组(当E等于1时为全相联映射)
- 每个cache块中有 2 b 2^b 2b字节个数据
- 总容量有 s ∗ b ∗ e s*b*e s∗b∗e
4. 图示
5. Cache中的命中率和缺失率
Hit:要访问的信息在cache中
- Hit Rate:在cache中的概率
- Hit Time:在cache中访问的时间:判断时间+cache访问
Miss:要找的信息不在cache中 - Miss Rate:1-Hit Rate
- Miss Penalty(缺失损失):访问一个主存块所花的时间
6. LRU算法
总是把最先进入的那一块淘汰掉。
- LRU是栈算法,命中率随组的增大而提高。
- 当分块局部化范围(即某段时间集中访问的存储区)超过了cache存储容量时,命中率变得很低。极端情况下,假设地址流是1,2,3,4,1,2,3,4,1……,而cache每组只有3行,那么不管是FIFO算法还是LRU算法,其命中率都是0。这种现象为颠簸现象。
- LRU具体实现是,并不是通过移动块来实现的,而是通过给每个cache行设定一个计数器,根据计数器的值来记录这些主存块的使用情况。这个计数位成为LRU位。
- 计数器变化规则如下图。
part A
实验A要求模拟一个cache存储器。需要识别不同的s,b,E
即不同的set index,block offset,lines
来完成数据的存储和释放,如果当cache满了的时候需要使用LRU(最近最少用算法)对数据进行丢弃和存储。
- 定义结构体
typedef struct{
int valid_bits; //有效位
unsigned tag; //标记位
int stamp; //时间戳
}cache_line;
- 定义cache[S][E] 大小的二维数组
- cache运行的模拟
读入要访问的地址,查找是否在cache中,若在读内容,若不在在主存中找到后写入cache
operator | what | do what |
---|---|---|
I | instruction | |
L | data load | 读access cache |
S | data store | 写access cache |
M | data modify | 又读又写access cache两次 |
while((opt = getopt(argc,argv,"s:E:b:t:")) !=-1){ //parse command line arguments
switch(opt){
case 's':
s=atoi(optarg);
break;
case 'E':
E=atoi(optarg);
break;
case 'b':
b=atoi(optarg);
break;
case 't':
filepath = optarg;
break;
}
}
- 更新cache
若命中hit++
若没有命中miss
void update(unsigned address){
unsigned s_address =(address>>b) & ((0xffffffff)>>(32-s)); //set`s index
unsigned t_address = address>>(s+b); //tag`s index
//判断tag为是否相等,是否命中
for(int i=0;i<E;i++){
if((*(cache+s_address)+i)->tag ==t_address){
cache[s_address][i].stamp = 0; //now ,this is used
hit++;
return;
}
}
//更新高速缓存cache
for(int i=0;i<E;i++){
if(cache[s_address][i].valid_bits == 0){
cache[s_address][i].tag = t_address;
cache[s_address][i].valid_bits = 1;
cache[s_address][i].stamp = 0; //now ,this is load
miss++;
return;
}
}
//暴力实现LRU策略
int max_stamp=0;
int max_i;
for(int i=0;i<E;i++){
if(cache[s_address][i].stamp > max_stamp){
max_stamp = cache[s_address][i].stamp;
max_i = i;
}
}
eviction++;
miss++;
cache[s_address][max_i].tag = t_address;
cache[s_address][max_i].stamp = 0;
}
- 时间戳
若使用cache,stamp为0
void time(){
for(int i=0;i<S;i++){
for(int j=0;j<E;j++){
if(cache[i][j].valid_bits == 1)
cache[i][j].stamp++;
}
}
}
完整代码实现
#include "cachelab.h"
#include <getopt.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <stddef.h>
typedef struct{
int valid_bits;
unsigned tag;
int stamp;
}cache_line;
char* filepath = NULL;
int s,E,b,S; // s is set ,E is line,each line have 2^b bits ,S is 2^s set
int hit=0,miss=0,eviction=0;
cache_line** cache = NULL;
void init(){
cache = (cache_line**)malloc(sizeof(cache_line*)*S); //malloc cache[S][E]
for(int i=0;i<S;i++)
*(cache+i) = (cache_line*)malloc(sizeof(cache_line)*E);
for(int i=0;i<S;i++){
for(int j=0;j<E;j++){
cache[i][j].valid_bits = 0; // set all valid_bits is zero
cache[i][j].tag = 0xffffffff; //no address
cache[i][j].stamp = 0; //time is 0;
}
}
}
void update(unsigned address){
unsigned s_address =(address>>b) & ((0xffffffff)>>(32-s)); //set`s index
unsigned t_address = address>>(s+b); //tag`s index
//判断tag为是否相等,是否命中
for(int i=0;i<E;i++){
if((*(cache+s_address)+i)->tag ==t_address){
cache[s_address][i].stamp = 0; //now ,this is used
hit++;
return;
}
}
//更新高速缓存cache
for(int i=0;i<E;i++){
if(cache[s_address][i].valid_bits == 0){
cache[s_address][i].tag = t_address;
cache[s_address][i].valid_bits = 1;
cache[s_address][i].stamp = 0; //now ,this is load
miss++;
return;
}
}
//暴力实现LRU策略
int max_stamp=0;
int max_i;
for(int i=0;i<E;i++){
if(cache[s_address][i].stamp > max_stamp){
max_stamp = cache[s_address][i].stamp;
max_i = i;
}
}
eviction++;
miss++;
cache[s_address][max_i].tag = t_address;
cache[s_address][max_i].stamp = 0;
}
void time(){
for(int i=0;i<S;i++){
for(int j=0;j<E;j++){
if(cache[i][j].valid_bits == 1)
cache[i][j].stamp++;
}
}
}
int main(int argc,char *argv[])
{
int opt;
while((opt = getopt(argc,argv,"s:E:b:t:")) !=-1){ //parse command line arguments
switch(opt){
case 's':
s=atoi(optarg);
break;
case 'E':
E=atoi(optarg);
break;
case 'b':
b=atoi(optarg);
break;
case 't':
filepath = optarg;
break;
}
}
S = 1<<s;
init();
FILE* file=fopen(filepath,"r");
if(file == NULL){ // read trace file
printf("Open file wrong");
exit(-1);
}
char operation;
unsigned address;
int size;
while(fscanf(file," %c %x,%d",&operation,&address,&size)>0){
switch(operation){
case 'L':
update(address);
break;
case 'M':
update(address);
case 'S':
update(address);
break;
}
time();
}
for(int i=0;i<S;i++) //free cache[S][E]
free(*(cache+i));
free(cache);
fclose(file); //close file
printSummary(hit,miss,eviction);
return 0;
}
part B
实现矩阵的转置,采用分治(分块)的思想:一块一块搬乘转置。
void transpose_submit(int M, int N, int A[N][M], int B[M][N])
{
int rr,cc,r,c;
int bsize;
if(M==32) bsize=8;
else if(M==64) bsize=4;
else if(M==61) bsize=16;
for(rr=0;rr<N;rr+=bsize){
for(cc=0;cc<M;cc+=bsize){
for(r=rr;r<N&&r<rr+bsize;r++){
for(c=cc;c<M&&c<cc+bsize;c++){
B[c][r]=A[r][c];
}
}
}
}
return;
}
参考文章
https://www.cnblogs.com/kangyupl/p/13263687.html
https://cloud.tencent.com/developer/article/1826689
https://blog.csdn.net/weixin_41256413/article/details/81388351
https://zhuanlan.zhihu.com/p/33846811
https://zhuanlan.zhihu.com/p/42754565