1. Cache
Cache的内部组织结构如下图所示。Cache共有S组,每组E行,每行包括一个有效位(valid bit),一个标签和B比特数据。当E=1时,称为直接映射,当E > 1时,称为E路组相连。
内存地址由标签(tag)、组索引(set index)、块偏移(block offset)三个部分组成。假设指令长度为m,则t=m-b-s,Cache存储的数据总量为S*B*E。
缓存未命中的原因有以下三种:
Cold (compulsory) miss 。首次访问cache必然导致未命中。
Conflict miss 。缓存还有大量的空余空间,但是多个数据对象被映射到相同的缓存块组,导致数据对象被交替换出。
Capacity miss。需要缓存的数据块超过了缓存的存储空间。
2. Cache 模拟程序
程序要求:
- 不存储内存数据。
- 不使用块偏移量-地址中的b位不会被置位。
- 缓存模拟器需要在运行中指定的不同s,b,E工作。
- 使用LRU算法,从缓存中逐出最近最少使用的块,以便为下一个块腾出空间。
2.1 测试数据
测试文件中的数据记载着每一次对内存的操作,前面的字母代表操作类型,统一的格式是:
[空格][操作类型][空格][内存地址][逗号][大小]
其中如果第一个不是空格而是I,则代表加载,没有实际意义。
操作类型有以下三种:
L:读取,从内存中读取
S:存储,向内存中存储
M:修改,这涉及一次读取,一次存储操作
地址指的是一个 64 位的 16 进制内存地址;大小表示该操作内存访问的字节数
其中I指令无空格,M/S/L指令前有1个空格。
1 |
|
实验指导提供了一个样例可执行文件csim-ref,我们要做的就是写出一个和它功能一样的程序。
执行一个示例,结果如下。
除此之外,实验指导还提供三个函数帮助完成实验,分别是getopt、fscanf、malloc 。
1 | //getopt |
getopt用于获取命令行参数,fscanf 读入测试文件内容,malloc 分配空间给cache。
3. 代码实现
3.1 Cache基本单元
定义Cache基本单元为struct cache_line,不要求存储内存数据,所以没有设置块偏移量。
1 | typedef struct |
3.2 解析命令行参数
1 |
|
3.3 缓存处理
- 使用位移解析内存地址中的组号和标签。
- 使用hit_or_miss函数读取缓存的数据,返回命中或者没有命中。
- 如果没有命中使用函数put_in_cache将该内存块放入缓存,并检查是否产生eviction。
- 如果缓存中有空间,将内存块直接放入,如果没有,使用LRU算法进行置换,并且标识产生了eviction。
- 统计所有的miss数量和eviction数量。
完整实验代码如下。
1 |
|
测试程序运行效果。