CSAPP fall2015 深入理解计算机系统 Cache lab详解

Cache Lab

cache lab 缓存实验

代码下载

从CSAPP上面下载对应的lab代码

http://csapp.cs.cmu.edu/3e/labs.html

环境准备

需要安装 valgrind。可以参考文章Valgrind centos。

安装好以后执行valgrind --version可以看到版本号。

Cache simulator

  • cache simulator not a cache。我们不是实现一个真正的缓存,只是实现一个模拟器。
  • 不存储内容
  • 不使用block offset
  • 只计算命中数,不命中数和驱逐数(hit count, miss count,eviction count)
  • 缓存模拟器需要在不同的s,b,E下运行。
  • 使用LRU替换策略

Hints

  • 使用二维数组 struct cache_line cache[S][E];
  • S = 2^s
  • cache_line //上面说了不需要block offset,所以可以忽略block的内容
    • Valid bit
    • Tag
    • LRU counter
  • 通过getopt获取命令行输入
    • 返回-1表示没有输入了
    • 通常在循环里面接收参数
    • 需要包含#include <unistd.h>,#include <getopt.h>
    • 通常使用switch来处理不同的输入
    • 考虑如何处理无效输入
    • 更多信息 man 3 getopt
  • fscanf可以指定要读的流(scanf只能读标准输入流),用来读取trace file
    • 参数
      • 一个流的指针
      • 如何解析文件的信息的格式化字符串
      • 其余部分是指向存储解析数据的变量的指针
    • 通常在循环里使用
    • 当命中EOF或者没有匹配到格式化字符串的时候返回-1
    • 更多信息 man fscanf
  • Malloc/free
    • malloc分配数据到heap
    • 记得 free 掉malloc的数据
    • 不要 free 你没有分配的内存

在这里插入图片描述

在这里插入图片描述

要求我们实现csim.c文件,给了一个示例csim-ref文件。

输入./csim-ref -h可以看到我们要实现的东西。

在这里插入图片描述

首先需要接受参数,参数有

  • -h 输出帮助信息
  • -v 可选详细标志,根据示例程序来,就是输出 L 10,1 miss这些信息
  • -s [num] set index bit 数
  • -E [num] 每个set的行数
  • -b [num] block offset bit数
  • -t [file] Trace file文件路径

根据上面的提示可以知道,通过getopt函数来接收参数,并通过switch来处理。读取文件则通过fscanf函数,来读取-t传的文件。

下面是./traces/yi.trace文件的内容

 L 10,1
 M 20,1
 L 22,1
 S 18,1
 L 110,1
 L 210,1
 M 12,1
  • L 代表数据载入
  • S 代表数据存储
  • M 代表数据修改,需要一次载入 + 一次存储
  • 后面的10,20,22这些代表地址
  • 最后的1代表操作内存访问的字节数

完整代码

#include "cachelab.h"
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct{
        int valid;
        int tag;
        int time_stamp;
} cache_line;

int timestamp = 0;

// 开始匹配到合适的set 找到命中的cache,如果命中返回1,如果没有命中返回0
int find_hit_cache(cache_line *cache_line,int E,int tag, int*hits) {
    int isHit = 0;
    // 循环set中的cache_line 找到是否有匹配tag && valid
    for(int i = 0; i < E; i++) {
        if (cache_line[i].valid == 1 && cache_line[i].tag == tag ) {
            //hit
            printf("hit \n");
            *hits = *hits + 1;
            isHit = 1;
            //刷新时间
            cache_line[i].time_stamp = timestamp;
            return isHit;
        }
    }
    return isHit;
}

// 找到一个空的cache line放进去,找到了就返回1,没有找到就返回0
int find_empty_cache(cache_line *cache_line,int E,int tag) {
    int have_empty_cache = 0;
    for (int i = 0; i< E; i++) {
        if (cache_line[i].valid == 0) {
            // 空的
            // 把当前内存放入cache
            cache_line[i].valid = 1;
            cache_line[i].tag = tag;
            cache_line[i].time_stamp = timestamp;
            // 找到了就不需要替换了
            have_empty_cache = 1;
            return have_empty_cache;
        }
    }
    return have_empty_cache;
}

// 获取要替换的索引
int get_eviction_index(cache_line *cache_line, int E) {
    int max_time_stamp = timestamp;
    int eviction_index = -1;
    for (int i = 0; i< E; i++) {
        if (cache_line[i].time_stamp < max_time_stamp) {
            //找到time_stamp最小的那个
            max_time_stamp = cache_line[i].time_stamp;
            eviction_index = i;
        }
    }
    return eviction_index;
}

// LRU替换
void LRU(cache_line *cache_line, int E,int tag) {
    // 获取要替换的索引
    int eviction_index = get_eviction_index(cache_line, E);
    
    // 替换
    cache_line[eviction_index].valid = 1;
    cache_line[eviction_index].tag = tag;
    cache_line[eviction_index].time_stamp = timestamp; 
}

// L和S操作,M就调用两次这个
int load_and_store(unsigned address,int b,int s,int u_max,int E,cache_line **cache, int *hits,int *misses,int *evications) {
    // 获取set index block offset tag
    int set_index,tag;
    set_index = (address >> b) & u_max;
    tag = (address >> b) >> s;

    // 开始匹配到合适的set 找到命中的cache,如果命中返回1,如果没有命中返回0
    int isHit = find_hit_cache(cache[set_index], E, tag, hits);
    if (isHit == 0) {
        // miss
        printf("miss \n");
        *misses = *misses + 1;
        // 找到一个空的cache line放进去,找到了就返回1,没有找到就返回0
        int have_empty_cache = find_empty_cache(cache[set_index], E, tag);
        // 如果没有找到空的cache,就需要LRU替换
        if (have_empty_cache == 0) {
            printf("evictions \n");
            *evications = *evications + 1;
            //LRU替换
            LRU(cache[set_index], E, tag);
        }
    }
    // 更新全局时间戳
    timestamp++;
    return 0;
}

int main(int argc, char** argv)
{
    // 接受参数 getopt
    int opt,v,s,E,b,S,B;
    // 文件
    FILE        *       pFile;
    while(-1 != (opt = getopt(argc, argv, "h?v?s:E:b:t:"))){
        // opt is h,v,s,E,b,t的ASCII码值
        // 通过switch对不同的参数进行不同的处理
        switch(opt) {
            case 'h':
                printf("./csim: Missing required command line argument \n Usage: ./csim-ref [-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 ./csim -s 4 -E 1 -b 4 -t traces/yi.trace \n ./csim -v -s 8 -E 2 -b 4 -t traces/yi.trace \n");
                // h参数输出帮助内容
                break;
            case 'v':
                // v参数输出详细信息
                v = 1;
                printf("v:%d \n",v);
                break;
            case 's':
                // S is set 2^s 的数量
                // s is Number of set index bits
                s = atoi(optarg);
                S = 1 << s;
                printf("s:%d, S:%d \n",s,S);
                break;
            case 'E':
                // E is cache line 的数量
                // Number of lines per set
                E = atoi(optarg);
                printf("E:%d \n",E);
                break;
            case 'b':
                // B is block data 的字节
                // b is Number of block offset bits
                b = atoi(optarg);
                B = 1 << b;
                printf("b:%d, B:%d \n",b,B);
                break;
            case 't':
                // t is Trace file
                // 读取文件
                //t = atoi(optarg);
                pFile   =       fopen(optarg,"r");
                printf("t:%s, file:%p \n",optarg,pFile);
                break;
            default:
                printf("非法参数 \n");
            break;
        }
    }
    if(s == 0 || E == 0 || b == 0) {
        return 0;
    }
    // cache存储
    cache_line **cache = (cache_line **)malloc(S * sizeof(cache_line *));
    if (cache == NULL) {
        printf("内存分配失败 \n");
    }
    for(int i = 0; i < S; i++) {
        cache[i] = (cache_line *)malloc(E * sizeof(cache_line));
        if(cache[i] == NULL) {
                printf("内存分配失败,开始回滚 \n");
                // 在这里需要释放已分配的内存,然后退出
            for (int j = 0; j < i; ++j) {
                free(cache[j]);
            }
            free(cache);
        }
    }

    int u_max = 1; 
    for(int i = 0; i < s - 1; i++) {
        u_max = (u_max << 1) | 1;
    }

    // 读取文件
    char        identifier;
    unsigned    address;
    int         size;
    int hits,misses,evictions;
//      Reading lines   like    "       M       20,1"   or      "L      19,3"
    while(fscanf(pFile," %c %x,%d",&identifier,&address,&size)>0)
    {
        //      Do      stuff
        // 开始计算 hits,misses,evictions, hits:0 misses:0 evictions:0
        //printf("identifier %c, addr:%x, size:%d \n",identifier,address,size);
        
        //根据identifier来判断动作L load S store M = 一次L 一次S
        if (identifier == 'L' || identifier == 'S'){
        printf("identifier %c, addr:%x, size:%d \n",identifier,address,size);
            load_and_store(address,b,s,u_max,E,cache,&hits,&misses,&evictions);
        } else if (identifier == 'M') {
            // 一次L 一次S
        printf("identifier %c, addr:%x, size:%d \n",identifier,address,size);
            load_and_store(address,b,s,u_max,E,cache,&hits,&misses,&evictions);
            load_and_store(address,b,s,u_max,E,cache,&hits,&misses,&evictions);
        }
    }
    
    fclose(pFile);  //remember      to      close   file    when    done
    printSummary(hits, misses, evictions);

    // 释放数组内存
    for(int i = 0; i< S; i++) {
        free(cache[i]);
    }
    free(cache);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/343004.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

苹果眼镜(Vision Pro)的开发者指南(5)-主要工具

主要工具有:Xcode、Reality Composer Pro、Unity 第一部分:【用Xcode进行开发】 开始使用Xcode为visionOS进行开发。将向你展示如何在你现有的项目中添加一个visionOS目标,或者构建一个全新的应用,在Xcode预览中创建原型,以及从Reality Composer Pro中导入内容。还将分享…

c++:string相关的oj题(415. 字符串相加、125. 验证回文串、541. 反转字符串 II、557. 反转字符串中的单词 III)

文章目录 1. 415. 字符串相加题目详情代码1思路1代码2思路2 2. 125. 验证回文串题目详情代码1&#xff08;按照要求修改后放到新string里&#xff09;思路1代码2(利用双指针/索引)思路2 3. 541. 反转字符串 II题目详情代码1思路1 4. 557. 反转字符串中的单词 III题目详情代码1&…

[足式机器人]Part3 机构运动学与动力学分析与建模 Ch00-3(3) 刚体的位形 Configuration of Rigid Body

本文仅供学习使用&#xff0c;总结很多本现有讲述运动学或动力学书籍后的总结&#xff0c;从矢量的角度进行分析&#xff0c;方法比较传统&#xff0c;但更易理解&#xff0c;并且现有的看似抽象方法&#xff0c;两者本质上并无不同。 2024年底本人学位论文发表后方可摘抄 若有…

机器学习笔记:地理加权回归(GWR)

1 传统的线性回归 机器学习笔记&#xff1a;线性回归_线性回归的读书笔记-CSDN博客 最优的β为&#xff1a; 2 地理加权回归&#xff08;GWR&#xff09; 2.1 模型概述 地理加权回归&#xff08;Geographically Weighted Regression&#xff0c;GWR&#xff09;是传统回归分…

【算法小记】——机器学习中的概率论和线性代数,附线性回归matlab例程

内容包含笔者个人理解&#xff0c;如果错误欢迎评论私信告诉我 线性回归matlab部分参考了up主DR_CAN博士的课程 机器学习与概率论 在回归拟合数据时&#xff0c;根据拟合对象&#xff0c;可以把分类问题视为一种简答的逻辑回归。在逻辑回归中算法不去拟合一段数据而是判断输入…

5G-A:“繁花”盛开在2024

2019年&#xff0c;我国正式发牌5G&#xff0c;开启5G商用新时代。通信技术十年一代&#xff0c;五年过去了&#xff0c;5G是否要进入“半代更迭”阶段&#xff1f; 2024年被视为5G-A商用元年&#xff0c;是5G走向6G的关键一跃。5G-A以R18为演进起点&#xff0c;在连接速率、网…

【Linux配置yum源以及基本yum指令】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、yum是什么&#xff1f; 二、什么是软件包&#xff1f; 三、三种安装软件包的方式 四、yum的相关操作 4.1、搜索软件 4.2、安装软件 4.3、卸载软件 4.4、那…

龟兔再跑

欢迎来到程序小院 龟兔再跑 玩法&#xff1a;乌龟跳绳&#xff0c;点击鼠标左键乌龟跳跃&#xff0c;两只乌龟一直不停的甩绳子&#xff0c;另外一只乌龟从中跳过&#xff0c;赶快去跳绳吧^^。开始游戏https://www.ormcc.com/play/gameStart/255 html <div class"mai…

vue中keep-alive的理解和使用

简要说明&#xff1a; keep-alive&#xff1a;保留状态。在项目中我们经常将keep-alive和router-view结合使用&#xff0c;实现切换路由后仍然保留之前的路由页面的状态&#xff0c;路由切换回来后不会 重新初始化&#xff0c;而是保留之前的状态。但keep-alive是vue本身提供的…

七八分钟快速用k8s部署springboot前后端分离项目

前置依赖 k8s集群&#xff0c;如果没有安装&#xff0c;请先安装 kubectl &#xff0c;客户端部署需要依赖 应用镜像构建 应用镜像构建不用自己去执行&#xff0c;相关镜像已经推送到docker hub 仓库&#xff0c;如果要了解过程和细节&#xff0c;可以看一下&#xff0c;否…

openEuler操作系统的安装及免密远程连接(超详细版)

一、下载地址 注意&#xff1a;可以先注册华为账号&#xff0c;注册后可享1倍加速 mirrors.huaweicloud.com/openeuler/openEuler-22.03-LTS-SP3/ISO/x86_64/ 二、创建虚拟机步骤 ①选择自定义 ② 根据自己的VMware选择版本 ③选择稍后安装操作系统 ④没有openEuler可以选择…

「 网络安全术语解读 」通用攻击模式检举和分类CAPEC详解

引言&#xff1a;在网络安全领域&#xff0c;了解攻击者的行为和策略对于有效防御攻击至关重要。然而&#xff0c;攻击模式的描述和分类方式缺乏统一性和标准化。为了解决这个问题&#xff0c;MITRE公司创建了CAPEC标准&#xff0c;以提供一个共享和统一的攻击模式分类框架。 1…

给主机双网卡配置双网关,修改Windows路由表

问题背景&#xff1a; 1 一般情况下&#xff0c;Windows主机就算有多个网卡&#xff0c;在默认情况下&#xff0c;只能有一个网卡可以配置网关。 2 在双网卡只配置一个网关的情况下&#xff0c;如果每个网卡值负责访问自己网段内的IP地址&#xff0c;这样是不会出现什么异常现…

C语言爬虫采集图书网站百万数据

最近需要查阅一些资料&#xff0c;只给到相关项目名称以及关键词&#xff0c;想通过图书文库找到对应书籍&#xff0c;那么怎么才能在百万数据库中找到自己需要的文献呢&#xff1f; 今天我依然用C语言写个爬虫程序&#xff0c;从百万数据库中查找到适合的文章&#xff0c;能节…

软考14-上午题-编译、解释程序翻译阶段

一、编译、解释程序【回顾】 目的&#xff1a;高级程序设计语言&#xff08;汇编语言、高级语言&#xff09;—【翻译】—>机器语言 1-1、编译方式 将高级语言书写的源程序——>目标程序&#xff08;汇编语言、机器语言&#xff09; 包含的工作阶段&#xff1a;词法分…

ubuntu 20.04 aarch64 平台交叉编译 libffi 库

前言 由于打算交叉编译 python&#xff0c;但是依赖 libffi 库&#xff0c;也就是 libffi 库也需要交叉编译 环境&#xff1a; ubuntu 20.04 交叉编译工具链&#xff1a;这里使用 musl libc 的 gcc 交叉编译工具链&#xff0c;aarch64-linux-musleabi-gcc&#xff0c;gcc 版本…

字符串冲刺题(算法村第十二关黄金挑战)

最长公共前缀 14. 最长公共前缀 - 力扣&#xff08;LeetCode&#xff09; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 示例 1&#xff1a; 输入&#xff1a;strs ["flower","flow"…

安泰ATA-2082高压放大器如何驱动超声探头进行无损检测

无损检测技术是一种在不破坏或影响被检测物体性能的前提下&#xff0c;通过物理或化学方法对其内部或表面的缺陷进行检测的技术。在无损检测领域&#xff0c;超声检测是一种广泛应用的方法&#xff0c;而ATA-2082高压放大器则是实现高效、精确超声检测的关键设备之一。本期内容…

钉钉互动卡片对接-普通互动卡片接入流程

这里写目录标题 一、创建内部应用二、搭建普通卡片模板三、调用互动卡片服务端接口接口报文一、发送卡片二、更新卡片三、获取token 一、创建内部应用 登录开发者后台&#xff0c;创建内部应用。 例如 百度-内部测试获取AppKey和AppSecret&#xff0c; 获取应用访问凭证获取企…

计组与原理:系统总线

大家好啊&#xff0c;这里来到计组第二部分内容&#xff1a;系统总线 跳转上一篇&#xff1a;计组原理&#xff1a;系统概论与基本组成 系统总线 1.总线的基本概念单总线结构框图面向 CPU 的双总线结构框图以存储器为中心的双总线结构框图 2.总线的分类片内总线系统总线通信总线…