IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> cachelab题解 -> 正文阅读

[数据结构与算法]cachelab题解

前言

如果存在什么奇怪的问题

1.压缩包放到linux环境下再解压,并且不要放共享文件夹里做

2.不建议使用wsl1和wsl2

英文水平有限以及对cache理解命中率优化有限参考了现有网上很多大佬的题解

以及这个实验是有一份ppt的,这份ppt里面介绍了两个模拟要用的库函数。

ppt链接:http://www.cs.cmu.edu/afs/cs/academic/class/15213-f15/www/recitations/rec07.pdf

PartA

要求我们模拟cache的行为,函数写在csim.c里面。

实现以下功能。

Usage: ./csim-ref [-hv] -s <s> -E <E> -b <b> -t <tracefile>
? -h: Optional help flag that prints usage info
? -v: Optional verbose flag that displays trace info
? -s <s>: Number of set index bits (S = 2s
is the number of sets)
? -E <E>: Associativity (number of lines per set)
? -b <b>: Number of block bits (B = 2b
is the block size)
? -t <tracefile>: Name of the valgrind trace to replay

刚看到是有点懵逼。

这就是一个字符串的读入,读入的内容在文件里面,所以要main要重定向进行流输入。(getopt)

数据的读入

上面的代码部分就是界面。

1.这次实验只要求我们测试hit/miss/eviction的次数,并没有实际的数据存储?,所以我们不用实现line中的block部分。

2.实验要求使用LRU替换策略

考虑模拟的结构:

1.cache的结构体:组,行,块。动态分配空间,结束时候释放。

2.读入二进制流后获得本次操作的信息,是否打印输出,cache的信息,每次输入数据的文件

3.初始化完cache后准备输入数据的文件进行cache的处理

4.cache的处理部分有查找,命中与不命中,以及不命中时候使用LRU替换策略

#include "cachelab.h"
#include <unistd.h>
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
/*要打印的答案*/
long long hit_count,miss_count,eviction_count;
int output;
/*定义cache的结构*/
struct Line{
    int valid;
    long long s_add;
    unsigned long LRU_counter;
};
struct Group{
    struct Line* lines;
};
struct Cache{
    long long s;
    long long E;
    long long b;
    struct Group* groups;
};
/*初始化cache*/
struct Cache initCache(long long s,long long E,long long b){

     struct Cache cache;
     cache.s=s;cache.E=E;cache.b=b;
     long long S=1<<s;
     cache.groups=(struct Group*)malloc( S*sizeof(struct Group) );
     for(int i=0;i<S;i++){
            cache.groups[i].lines=(struct Line*)malloc(E *sizeof(struct Line) );
     }
     return cache;
}
void release(struct Cache cache){
     long long S=1<<cache.s;
     for(long long i=0;i<S;i++){
         free(cache.groups[i].lines);
     }
     free(cache.groups);
}
/*LRU处理cache数据块的读*/
void search(struct Group* group,long long vis,long long E,int flag2){
     int ok=0;
     int full=1;
     int pid=0;
     /*找cache中是否存在*/
     for(long long i=0;i<E;i++){
        if(group->lines[i].valid==1&&vis==group->lines[i].s_add) {
            ok=1;
            pid=i;
            group->lines[i].LRU_counter=0;
        }
        if(group->lines[i].valid==0) full=0;
     }
     if(ok){
        if(output) printf("hit ");
        hit_count++;
        for(long long i=0;i<E;i++){
            if(i==pid) continue;
            group->lines[i].LRU_counter++;
        }
        return;
     }
     else{
        if(output) printf("miss ");
        miss_count++;
     }
     if(full){   /*当前满*/
            if(output) printf("eviction ");
            eviction_count++;
            long long mx=0;
            long long id=0;
            /*找到要踢出的块*/
            for(long long i=0;i<E;i++){
                if(mx<group->lines[i].LRU_counter){
                    mx=group->lines[i].LRU_counter;
                    id=i;
                }
            }
            group->lines[id].valid=1;
            group->lines[id].s_add=vis;
            group->lines[id].LRU_counter=0;
            /*更新其他块*/
            for(long long i=0;i<E;i++){
                if(i==id) continue;
                group->lines[i].LRU_counter++;
            }
     }
     else{
        /*新增加的块的dfn=0*/
        int flag=1;
        for(long long i=0;i<E;i++){
            if(flag&&group->lines[i].valid==0){
                group->lines[i].valid=1;
                group->lines[i].s_add=vis;
                group->lines[i].LRU_counter=0;
                flag=0;
            }
            else if(group->lines[i].valid==1){
                group->lines[i].LRU_counter++;
            }
        }
     }
     if(output&&flag2) printf("\n");
}
void solve(FILE* fp,struct Cache cache){

     unsigned long address;
     int siz;
     /*读取文件*/
     long long S=1<<cache.s;
     char oper;
     while( (fscanf(fp,"%c %lx,%d",&oper,&address,&siz) )!=-1 ){
          if(oper=='I') continue;
          long long ad=(address>>cache.b)&(S-1);/*该数据块的组索引*/
          long long vis=(address>>cache.b)>>(cache.s);/*该数据块的标记位*/
          if(oper=='S'||oper=='L'){
                if(output){
                   printf("%c %lx %d",oper,address,siz);
                }

                search(&cache.groups[ad],vis,cache.E,0);/*cache中找块*/

          }
          if(oper=='M'){
                if(output){
                   printf("%c %lx %d",oper,address,siz);
                }
                search(&cache.groups[ad],vis,cache.E,0);
                search(&cache.groups[ad],vis,cache.E,1);
          }
     }

}
int main(int argc,char **argv)
{

    long long s,E,b;
    output=0;
    FILE* fp=NULL;
    char opt;
    while( (opt = getopt(argc,argv,"hvs:E:b:t:") ) !=-1){
            switch(opt){
                case 'h':
                    printf("Usage: ./csim-ref [-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("Examples:\n");
                    printf("  linux>  ./csim-ref -s 4 -E 1 -b 4 -t traces/yi.trace\n");
                    printf("  linux>  ./csim-ref -v -s 8 -E 2 -b 4 -t traces/yi.trace\n");
                    exit(EXIT_SUCCESS);
                case 'v':
                    output=1;/*是否输出*/
                    break;
                case 's':
                    s=atoll(optarg);
                    break;
                case 'E':
                    E=atoll(optarg);
                    break;
                case 'b':
                    b=atoll(optarg);
                    break;
                case 't':
                    fp=fopen((const char*)optarg,"r");
                    if(fp==NULL){
                        printf("Open file wrong");
                        exit(EXIT_FAILURE);
                    }
                    break;
                default:
                    exit(EXIT_FAILURE);
            }
    }
    struct Cache cache=initCache(s,E,b);
    solve(fp,cache);
    printSummary(hit_count, miss_count, eviction_count);
    release(cache);
    return 0;
}

?PartB

挺魔鬼的..感觉没有彻底吃透

图片参考:https://www.bilibili.com/read/cv2955433/

首先做这个题,我们需要两张图,方便思考。

cache的s=5,E=1,b=5.

因此这是32X32时候每个int对应到cache中的组索引?

因此这是64X64时候的组索引?。

?

?先来考虑32X32.

我们发现,一次读掉一行,然后进行转置,如此进行第一个8X8之后。此时cache【0】存A【0】【0】~A【0】【7】。

再进行处理的时候,此时cache【0】,cache【8】,cache【16】...cache【28】已经存下来了。我们想让命中尽可能多,此时可以尝试把第一个8X8的矩阵完成转置。

但是你看到当cache【0】被B覆盖的时候,A【0】【0】~A【0】【7】此时就只赋值了一个,剩下的抖动掉了。

于是我们尝试用12个变量中的8个,来保存这次的A【0】【0】~A【0】【7】,这样就不用再去读导致miss了。

因此我们采取8个一块的策略。

可以观察到,除了对角线上存在多一点的抖动,其他地方主要一次冷不命中。

其结果命中图如下:

?可以计算出A的miss总数为32*4=128

B的miss总数为32*4+7*4=156.

总共为284。

测验得287.说明有函数开销。

void transpose_submit(int M, int N, int A[N][M], int B[M][N])
{
    if(M==32&&N==32){
        for (int i = 0; i < N; i +=8 )
        {
            for (int j = 0; j < M; j +=8 ){

                    for(int k=0;k<8;k++){

                        int temp_value0 = A[i+k][j];
                        int temp_value1 = A[i+k][j+1];
                        int temp_value2 = A[i+k][j+2];
                        int temp_value3 = A[i+k][j+3];
                        int temp_value4 = A[i+k][j+4];
                        int temp_value5 = A[i+k][j+5];
                        int temp_value6 = A[i+k][j+6];
                        int temp_value7 = A[i+k][j+7];

                        B[j][i+k] = temp_value0;  
                        B[j+1][i+k] = temp_value1;
                        B[j+2][i+k] = temp_value2;
                        B[j+3][i+k] = temp_value3;
                        B[j+4][i+k] = temp_value4;
                        B[j+5][i+k] = temp_value5;
                        B[j+6][i+k] = temp_value6;
                        B[j+7][i+k] = temp_value7;
                    }
                }
        }
    }

?输入./test-trans -M 32?-N 32 测验得287

64X64的情况

?我们发现这次4行就是一轮回了,沿用上次代码发现miss率非常高。模拟一下第一个8x8的块就发现存在反复抖动的现象。

我们尝试4X4的分块,发现将刚才的代码改成4X4,能到1699次。其实还是可以的。但是不够优秀。因为我们没有充分利用每次的cache块里面的后面空间。

这里想了挺久不会,参考了网上的题解。

图片来源:https://blog.csdn.net/weixin_43821874/article/details/86481338

? 尝试一个折中的办法,就是将8*8和4*4相结合。虽然不能直接移动到目的,我们可以缓存在一个地方,再放进去。

具体如下:

首先将红色块移至目的地,再将黄色块移至红色块左边“暂存”一下。此时移动是伴随着转置的。

?再实现图中的移动即可,将黄色块移到红色块的下面,在将绿色快移到之前“暂存”黄色块的地方,最后将灰色块移动到目的地即可。

?模拟这个过程还有一个很重要的细节问题。

?结合这张图就能发现。

“再实现图中的移动即可,将黄色块移到红色块的下面,在将绿色快移到之前“暂存”黄色块的地方,最后将灰色块移动到目的地即可。”过程中,我们移动黄色(上一次已经转置)的时候是横着一次性移动过来的,而不是竖着对应竖着。因为横着一次就能把此时的cache【0】用完不用再取黄色块了。而如果竖着每次回来还要接着抖动更多的次数。

因此由黄色块横着移过去,所以绿色块移动方式也确定下来了。用竖着的一列来填补cache[0]。

这样一来就节省很多了。

最后再把灰色的横着转置过来就行,灰色部分提前一步已经在cache中了,黄块转移好的时候cache[0],[8],[16],[24]也好了。因此命中率杠杠的。

    if(M==64&&N==64){

        for(int i=0;i<N;i+=8){
            for(int j=0;j<M;j+=8){
                for(int k=i;k<i+4;k++){
                    int t0=A[k][j];
                    int t1=A[k][j+1];
                    int t2=A[k][j+2];
                    int t3=A[k][j+3];
                    int t4=A[k][j+4];
                    int t5=A[k][j+5];
                    int t6=A[k][j+6];
                    int t7=A[k][j+7];

                    B[j][k]=t0;
                    B[j+1][k]=t1;
                    B[j+2][k]=t2;
                    B[j+3][k]=t3;

                    B[j][k+4]=t4;
                    B[j+1][k+4]=t5;
                    B[j+2][k+4]=t6;
                    B[j+3][k+4]=t7;

                }
                for(int k=j;k<j+4;k++){
                    int t0=A[i+4][k];
                    int t1=A[i+5][k];
                    int t2=A[i+6][k];
                    int t3=A[i+7][k];

                    int t4=B[k][i+4];
                    int t5=B[k][i+5];
                    int t6=B[k][i+6];
                    int t7=B[k][i+7];

                    B[k][i+4]=t0;
                    B[k][i+5]=t1;
                    B[k][i+6]=t2;
                    B[k][i+7]=t3;


                    B[k+4][i]=t4;
                    B[k+4][i+1]=t5;
                    B[k+4][i+2]=t6;
                    B[k+4][i+3]=t7;

                }

                for(int k=i;k<i+4;k++){
                    int t1=A[k+4][j+4];
                    int t2=A[k+4][j+5];
                    int t3=A[k+4][j+6];
                    int t4=A[k+4][j+7];

                    B[j+4][k+4]=t1;
                    B[j+5][k+4]=t2;
                    B[j+6][k+4]=t3;
                    B[j+7][k+4]=t4;
                }
            }
        }
    }

?61X67不能被完整地分块。网上的思路是暴力多次实验块的大小,测得17X17的无脑方式是够的。

因此我们按照17X17的方式分块。

看来不能完整分块的时候还是要多暴力尝试各种分块方式。

 if(N==67&&M==61){
        for(int i=0;i<N;i+=17){
            for(int j=0;j<M;j+=17){
                for(int k=i;k<i+17&&k<N;k++){
                    for(int p=j;p<j+17&&p<M;p++){
                        int temp=A[k][p];
                        B[p][k]=temp;
                    }
                }
            }
        }
    }

结语

这个lab还是比较折磨人的。partA部分只是个开胃小菜,让你手动来一个cache。对于LRU的策略优化的时间复杂度还可以优化。不过这是算法部分的策略了,不是这个lab的目的。

partB部分是感觉比较难,难在考虑的情况不好考虑,具体的不命中策略不好计算,当然可以用自己做的partA部分和后期拿到的数据来找哪里的不命中来分析。但是感觉过于细节。难度还是在于不命中率的整体思考角度和想法。

同时其中的转移模拟可以手动模拟,如果容易晕并且麻烦可以代码输出路径,这样更方便模拟,更好找路径以及发现时候存在漏移错移。

起码知道几点:

1.能整块分的尽量按照整块的大小去分。

2.不能整块的暴力尝试分块大小优化。

3.直接转移的命中率高,可以尝试间接转移,找到“缓存区”来转移

4.wsl2很坑

以及对cache有了进一步的理解。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-12 16:53:24  更:2021-08-12 16:54:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:55:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码