CSCI 2122任务5
截止日期:2024年4月9日星期二晚上11:59,通过git提交
目标
本课业的目的是练习用C进行编码,并强化中讨论的概念类的指针、缓存和内存层次结构。在这项任务中,您将实现一个使用有限内存的缓存模拟器。
准备
1.完成工作分配0,或确保已安装完成工作分配所需的工具。
2.克隆您的课业存储库:
您的CSID在哪里。如果需要,请参阅课业0中的说明和Brightspace教程
你不知道怎么做。在存储库中有一个目录:cachex,用于编写代码。你应该设置一个此目录的CLion项目。目录内是一个测试目录,其中包含将每次提交代码时执行。请不要修改测试目录或根目录中的.gitlabci.yml文件。修改这些文件可能会破坏测试。这些文件将当课业被评分时,用原件代替。您将获得一个示例Makefile可用于构建程序的文件。如果您正在使用CLion,将从生成Makefile
CLion生成的CMakeLists.txt文件。
背景
快速内存非常昂贵。缓存设计器受其可使用的快速内存量的限制。此外快速存储器不仅必须存储被缓存的数据而且还必须存储所有元数据,
例如标签、有效位和时间戳。自然地,缓存设计者在硬件中实现之前先在软件中模拟他们的设计。在这项课业中,你也会这样做。您的任务是实现一个模拟缓存的缓存模块。缓存类型的选择取决于你您的缓存模块将提供两个参数:代 写CSCI 2122任缓存和内存层次结构 解析,您的缓存可以使用和M,模拟系统中的内存量,从中缓存数据。此外,您的模块将被提供一个指向大小为F的“快速”内存的指针。您的缓存模块可能只使用这个“快速”内存(除了局部变量)来实现缓存。简而言之,任何数据
需要管理缓存,以及缓存的数据必须存储在“快速”内存中。
缓存
回想一下,缓存是由几个参数定义的:
S: 集的数量
E: 每套的行数
B: 每行缓存的字节数缓存的大小为C=S x E x B。缓存的类型取决于以下参数:
•在直接映射缓存中,E=1,即每组有一(1)行,
•在全关联缓存中,S=1,即所有行都在一个集合中,以及
•在集合关联缓存中,S>1和E>1,即有多个集合,每个集合有多行。当缓存接收到内存引用时
1.将地址分解为标记、集合索引和偏移量。
2.使用索引来标识可以缓存引用内存的集合
3.使用标记来确定集合中的行是否正在缓存引用的内存
4.如果引用的存储器没有被高速缓存,
a.缓存确定集合是否包含未使用的行
b.如果没有未使用的行,缓存将收回已使用的行。
c.然后用包含参考的存储器块加载该行。
5.此时,所选集合中的一行正在缓存被引用的内存。缓存返回所引用的数据。使用何种类型的缓存完全取决于您。唯一的限制是高速缓存必须适合“快速”存储器的F字节,并且行大小B≥32字节。
参考流
引用流只是一系列内存引用(地址),表示程序运行并在运行时访问内存。第一个整数R表示后面的内存引用数。接下来的R个整数是内存地址。
缓存模拟器您的缓存模拟器将(i)系统配置作为输入,该系统配置包括:
•F_size:F_size≥256的“快速”内存的大小
•F_memory:指向“快速”内存的指针
•M_size:主存储器的大小
(ii)参考流,以及(iii)可选的“stats”命令,该命令使模拟器打印出
缓存命中/未命中计数和命中率。模拟器实例化被模拟的系统,然后通过将每个引用发送到缓存来处理引用流。缓存将转发请求到主存储器,如果请求导致未命中,则从存储器加载一行。一旦请求数据在缓存中,缓存返回请求的数据。模拟器计算命中和未命中的次数并且可以在参考流完成之后输出命中率。您的任务将是在缓存模拟器中实现缓存模块。任务:实现模拟器的cache.c您的任务是通过实现一个函数来实现cache.c模块。函数已声明
在cache.h中,并从main.c调用。函数为:int cache_get(无符号长地址,无符号长值)
此函数获取一个内存地址和一个指向值的指针,并加载一个位于并将其复制到值所指向的位置。也就是说,这就是CPU当它需要从内存加载一个单词时,它会从缓存中请求它。函数需要两(2)个参数:
•地址:要加载的值的位置。地址是内存引用来自参考流。
•值:指向单词要复制到的缓冲区的指针。函数在成功时返回1,在失败时返回0。该功能执行两个步骤:
1.检查缓存系统是否已初始化。如果没有,则初始化缓存。
2.通过返回指定内存地址的值来处理请求。
步骤1:检查和初始化缓存该函数可以访问在cache.h中定义的名为c_info的全局结构。该结构为
结构缓存信息{
voidF_memory;/指向“快速”内存的指针/
无符号int F_size;/“快速”内存的大小(字节)/
unsigned int M_size;/主内存大小(字节)/
};
指针c_info。F_memory指向大小为c_info的内存块。F_size。记忆是初始化为全部0。这是唯一的内存,除了在实现时可能使用的局部变量缓存。您不能使用calloc()或malloc(),也不能创建任何额外的静态或全局变量。建议的方法是定义一个结构,并将其放置在指向“快速”内存的开头到F_memory。结构可以指向表示集合或行的结构数组,这些集合或行也位于“快速”记忆。这些结构可包含指针,指向(存储数据的)行,并且保存在“快速”记忆中。在“快速”内存的开头的结构中有一个“initialized”标志,如果缓存为已初始化,否则为0。提示:在cache.c中创建一个从调用的静态init()函数
cache_get(),如果“initialized”标志为0。init()函数可以设置所有指针
结构。注意:由您决定缓存的集和行数。唯一的
限制是(1)行(B)的最小大小必须为32字节。以及(ii)一切都必须符合
F_size字节的内存。F_size将大于或等于256。提醒:其中一件事
init()应该做的是将initialized标志设置为1。
步骤2:处理请求
要处理请求,cache_get()函数应该:
1.将地址分解为标记、索引和偏移量。
2.使用索引查找正确的集合。
3.使用标记来确定包含地址的内存块是否在中的一行中
这套。
4.如果是(缓存命中),偏移量用于定位该行中的单词,则应复制该单词
进入值所指向的缓冲区,然后函数返回。
5.否则,这是缓存未命中。在这种情况下,将选择一个受害者行,并使用的标记进行初始化
所需的内存块,并通过调用函数加载
int memget(无符号int地址,void*缓冲区,无符号int大小)
它在cache.h中声明,在main.c中定义。此函数将地址作为
第一个参数,指向应该加载块的缓冲区的指针,以及的大小要得到的块。提示,缓冲区应该指向行中存储块的部分。这个函数在成功时返回1,在失败时返回0。对memget()的每次调用都算作未命中。缓存模拟器的其余部分已经为您实现了!J
高速缓存主线cachex的main.c已经为您实现。以下是对其功能的简要描述。输入
cachex从stdin读取输入。输入包括三个部分:(i)系统配置;(ii)a参考流;以及(iii)可选的“stats”命令。系统配置由两个整数组成:
•F:“快速”内存大小
•M:内存大小引用流由表示引用数量的整数N和后面的N个引用组成。每个引用都是一个介于0和M–8之间的整数,表示被引用的内存中的地址。在N个内存引用之后,可能会出现可选的“stats”命令。此命令包括一个单词“stats”,并使模拟器打印出命中率、未命中率和命中率。
处理
当cachex开始运行时,它读取系统配置,分配系统中的内存并初始化c_info结构。主存储器被初始化为一系列伪随机数(这些数字看起来是随机的,但实际上不是)。然后,它进入主循环并处理参考流:
•对于每个引用,都会调用cache_get()。
•将加载值与预期值进行比较
Example
Input Output
1024 65536
Loaded value [0xb9cb17b29e5109d2] @ address 0x00000016
Loaded value [0x0394fee63984c8dc] @ address 0x00000030
Loaded value [0x8eba29a6bb1465ff] @ address 0x00000046
Loaded value [0x3ce65cc676176add] @ address 0x00001016
Loaded value [0xb9cb17b29e5109d2] @ address 0x00000016
Loaded value [0x3ce65cc676176add] @ address 0x00001016
Loaded value [0x425a273223d06058] @ address 0x00000816
Loaded value [0x3ce65cc676176add] @ address 0x00001016
Loaded value [0xb9cb17b29e5109d2] @ address 0x00000016
Cache hits: 4, misses: 5
提示和建议
•您将需要两个structs,一个用于缓存,另一个用于行。您可能还需要一个作为套装。
•从根本上讲,缓存是集合的数组,集合是行的数组。
•您只需要修改一个文件:cache.c。
•没有太多代码可写(我的解决方案不到100行)。
分级
如果你的程序没有编译,它将被视为不起作用且质量极差,这意味着你将收到0的解决方案。
课业将根据三个标准进行评分:
功能:“它是否按照规范工作?”。这是通过以下自动方式确定的
在多个输入上运行程序,并确保输出与预期输出匹配。这个
分数是根据你的程序通过的测试次数来确定的。所以,如果你的程序通过了
t/t测试,您将获得该比例的分数。
表演:“它演得好吗?”。这是通过运行您的
在几个输入上编程,并将缓存的基准与解决方案的基准进行比较。
解决方案质量:“这是一个好的解决方案吗?”这考虑了您的方法和算法
解决方案是正确的。这是通过目视检查代码来确定的。有可能取得好成绩
在这方面,即使您有导致代码无法通过某些测试的错误。
代码清晰度:“它写得好吗?”这考虑了解决方案是否正确格式化、文档记录良好以及是否遵循编码风格准则。为了清晰起见,将指定一个整体标记。请参阅
Brightspace课程课业部分的风格指南。
课业提交
提交和测试是使用Git、Gitlab和Gitlab CI/CD完成的。您可以多次提交
愿望,直到最后期限。每次提交时,都会执行功能测试,您可以查看
测试结果。若要提交,请使用与分配0相同的过程。
未提交课业测试
通过提交进行测试可能需要一些时间,尤其是在加载了服务器的情况下。您可以在没有
使用提供的runtests.sh脚本提交代码。运行不带参数的脚本
将运行所有测试。运行测试编号为00、01、02、03…09的脚本将运行特定的
测验请参阅下面的脚本运行方式。
准备好程序运行
如果您直接在unix服务器上进行开发,
1.通过SSH连接到远程服务器,并确保您在cachex目录中。
2.确保程序是通过运行make编译的。
如果您正在使用CLion
1.如CLion教程中所述,在远程服务器上运行程序。
2.通过工具打开远程主机终端→ 打开远程主机终端
如果您正在使用VSCode
1.如VSCode教程中所述,在远程服务器上运行程序。
2.单击窗口下半部分的“终端”窗格或通过“终端”→ 新建终端运行测试脚本
3.使用以下命令在终端中运行脚本:
./runtest.sh
运行所有测试,或指定运行特定测试的测试编号,例如:
./runtest.sh 07
运行基准测试脚本
3.使用以下命令在终端中运行脚本:
./runbench.sh
运行所有测试,或指定运行特定测试的测试编号,例如:
./runbench.sh 03
您将在终端窗口中看到台架运行。