Cache映射、替换、写机制

在上一篇文章中介绍了存储器之间的关系和区别,接下来介绍一下高速缓存(Cache)。

引入缓存原因

对于大多数人来说Cache,是透明的、不存在的。其中一个原因是Cache是集成到CPU中,对于程序员来说是透明的。

  1. CPU通用寄存器的速度与主存之间存在这太大的差异。CPU的通用寄存器的速度一般小于1ns,主存的速度一般是65ns。速度差异近百万。
  2. 程序访问的空间局部性、时间局部性。在较短时间间隔内,程序产生的地址往往集中在一个很小范围内。比如:一个很大的循环程序段,在一段时间内一直在局部区域执行指令,故循环内指令的时间局部性好;一段连续地址的数据元素,按顺序遍历故程序空间局部性好。

如何将缓存和内存关联:

CPU在获取指令时,在主存中把一段数据,都搬到Cache中,也就把下一个执行的指令一起搬到Cache中了。

在一段时间内,CPU所执行的程序和访问的数据大部分都在某一段地址范围内,而该段范围外的地址访问很少

结构模块化

  • CPU访问cache或主存时,以字为单位;

  • Cache和主存交换信息时,以块为单位,一次读入一块或多块内容;每块由若干个字组成;

  • Cache的每行都设置有标记,CPU访问程序或数据时,先访问标记 。

分块

主存被分成若干大小相同的块,称为主存块;Cache也被分成相同大小的块,称为Cache行(line)或槽。

Cache基本构成

  • 存储体

    • 基本单位为字,若干个字构成一个数据块
  • 地址映射变换机制

    • 用于将主存地址变换为Cache地址,以利用CPU发送的主存地址访问Cache
  • 替换机制

    • 若要更新Cache中数据时使用的机制
  • 相联存储器

    • Cache的块表,快速指示所要访问的信息是否在Cache中
  • 独写控制

    1. 读操作

      • 经地址变换机制,变换为可能的Cache地址;

      • 查找块表,判断所要访问的信息是否在Cache中;

      • 若在,则CPU直接读取Cache获取数据;

      • 若不在,则CPU访问主存,并判断Cache是否已满;

        若Cache未满,将该数据所在块从主存中调入Cache;

        若Cache已满,使用某种替换机制,使用当前数据块替换掉Cache中的某些块。

    2. 写操作

      • 经地址变换机构,变换为可能的Cache地址;
      • 查找对应的相联存储器,判断所要访问的信息是否在Cache中;
      • 若不在,则使CPU直接写主存数据;
      • 若在,则使用某种写策略将数据写入Cache。

l命中率是指CPU要访问的信息在cache中的比率;

主存块和Cache之间的映射

概念

cache相对于程序员是透明的。当给出一个主存地址时,先去cache中去找这个地址,怎么找,就是一种映射。在cache中未找到时,也就是未命中,需要在主存中找,然后需要将要访问的字所在主存中的块调入到cache中,也就是s

命中率:指CPU要访问的信息在cache中的比率

image-20210502005603717

影响命中率的主要因素:

  • Cache容量
    • 过小:局部信息装不完,命中率低
    • 过大:对提高效率不明显,并且成本高
  • Cache中块的大小
    • 一般用一个主存周期所能调出的单元数(字或字节)作为一个块大小。

全相联映射

可以将一个主存块存储到任意一个Cache行。

发现V为0(无效),就把主存块放到对应的槽。并把V设置为1。也就是说,主存块放到Cache是无序的,没有规则的。看见有空位就占。所以,CPU在找对应的内存地址时,都要遍历

优点:命中率较高,Cache的存储空间利用率高
缺点:线路复杂,成本高,速度低

image-20210501235625510

直接映射(模映射)

将一个主存块存储到唯一的一个Cache行

多对一的映射关系,但一个主存块只能拷贝到cache的一个特定行位置上去。
cache的行号i和主存的块号j有如下函数关系:i=j mod m(m为cache中的总行数)
优点:硬件简单,容易实现
缺点:命中率低, Cache的存储空间利用率低。

image-20210501235450328

组相联映射

将cache分成u组,每组v行,主存块存放到哪个组是固定的,至于存到该组哪一行是灵活的,即有如下函数关系:cache总行数m=u×v 组号q=j mod u

组间采用直接映射,组内为全相联
优点:硬件较简单,速度较快,命中率较高

image-20210501235803200

Cache的替换策略

  • 先进先出(FIFO)

  • LRU(Least Recently Used) **最近最少用 **命中行不加,其余行加1。替换时看谁计数大,替换谁。

  • 最不常用(LFU)

  • 随机替换算法(Random)

Cache写机制

缓存命中写入 write-hit

  1. 直写(Write Through):同时写Cache和主存单元,但是主存写的太慢。数据多时,容易造成CPU堵塞。解决办法:post write 使用写缓冲(Write Buffer)。CPU不再等主存写 完数据。直接把数据丢到Buffer中。

    image-20210502002321784

  2. 回写(write back):在数据更新时只写入缓存Cache,而不是立即写入内存。只在数据被替换出缓存时,被修改的缓存数据才会被写到后端存储。此模式的优点是数据写入速度快,因为不需要写存储;缺点是一旦更新后的数据未被写入存储时出现系统掉电的情况,数据将无法找回。对一cache行的多次写命中都在cache中快速完成修改,只是需被替换时才写回速度较慢的主存,减少了访问主的次数从而提高了效率。为支持这种策略,每个cache行必须配置一个修改位,以反映此行是否被CPU修改过。

写缺失的处理方式 Write-misses

  1. 写分配(Write Allocate):先将写入位置的数据读入缓存Cache,然后采用write-hit(缓存命中写入)操作。这种方式下,写缺失操作与读缺失操作类似。
  2. 非写分配(No-Write Allocate):并不将写入位置的数据读入缓存Cache,而是直接将数据写入存储。这种方式下,只有读操作会被缓存。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我可以给你提供一些代码实现的思路和参考资料。 主存-Cache地址映射是计算机组成原理中的一个重要概念。在实现主存-Cache地址映射时,需要考虑以下几个方面: 1. 地址转换机制:主存-Cache地址映射需要实现地址转换机制,将主存地址转换为Cache地址。常见的地址转换机制包括直接映射、全相联映射和组相联映射。 2. 缓存替换算法:当Cache已满时,需要使用缓存替换算法,将新的数据入到Cache中。常见的缓存替换算法包括最近最少使用算法(LRU)、先进先出算法(FIFO)和随机替换算法。 3. 缓存策略:当Cache中的数据被修改后,需要选择缓存策略。常见的缓存策略包括策略直达策略。 以下是一个简单的C++程序,实现了主存-Cache地址映射的基本功能,包括地址转换、缓存替换和缓存回。缓存大小为4,使用直接映射地址转换机制和最近最少使用算法进行缓存替换,使用策略进行缓存回。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; // 定义Cache块结构体 struct CacheBlock { int tag; bool valid; bool dirty; vector<int> data; int accessTime; }; // 定义Cache结构体 struct Cache { int blockSize; int cacheSize; int blockNum; int tagBits; int indexBits; int offsetBits; vector<CacheBlock> blocks; // 构造函数 Cache(int blockSize, int cacheSize) { this->blockSize = blockSize; this->cacheSize = cacheSize; this->blockNum = cacheSize / blockSize; this->tagBits = 32 - __builtin_clz(blockSize) - __builtin_clz(blockNum); this->indexBits = __builtin_ctz(blockSize); this->offsetBits = 32 - this->tagBits - this->indexBits; this->blocks.resize(blockNum); for (int i = 0; i < blockNum; i++) { blocks[i].tag = -1; blocks[i].valid = false; blocks[i].dirty = false; blocks[i].data.resize(blockSize); blocks[i].accessTime = 0; } } // 地址转换函数 int addressToIndex(int address) { return (address >> offsetBits) & (blockNum - 1); } int addressToTag(int address) { return address >> (offsetBits + indexBits); } // 缓存替换函数 int replaceBlock(int index, int tag) { int replaceIndex = 0; int minAccessTime = INT_MAX; for (int i = index; i < blockNum; i += blockNum) { if (!blocks[i].valid) { replaceIndex = i; break; } if (blocks[i].accessTime < minAccessTime) { minAccessTime = blocks[i].accessTime; replaceIndex = i; } } if (blocks[replaceIndex].dirty) { // 缓存回 cout << "Cache block " << replaceIndex << " is dirty, write back to memory" << endl; } blocks[replaceIndex].tag = tag; blocks[replaceIndex].valid = true; blocks[replaceIndex].dirty = false; blocks[replaceIndex].accessTime = 0; return replaceIndex; } // 缓存读取函数 int read(int address) { int index = addressToIndex(address); int tag = addressToTag(address); int offset = address & (blockSize - 1); if (blocks[index].valid && blocks[index].tag == tag) { // 命中Cache blocks[index].accessTime++; return blocks[index].data[offset]; } else { // 未命中Cache cout << "Cache miss!" << endl; int replaceIndex = replaceBlock(index, tag); // 从主存中读取数据到Cache cout << "Read data from memory to cache block " << replaceIndex << endl; for (int i = 0; i < blockSize; i++) { blocks[replaceIndex].data[i] = address + i; } blocks[replaceIndex].accessTime++; return blocks[replaceIndex].data[offset]; } } // 缓存入函数 void write(int address, int value) { int index = addressToIndex(address); int tag = addressToTag(address); int offset = address & (blockSize - 1); if (blocks[index].valid && blocks[index].tag == tag) { // 命中Cache blocks[index].accessTime++; blocks[index].data[offset] = value; blocks[index].dirty = true; } else { // 未命中Cache cout << "Cache miss!" << endl; int replaceIndex = replaceBlock(index, tag); // 从主存中读取数据到Cache cout << "Read data from memory to cache block " << replaceIndex << endl; for (int i = 0; i < blockSize; i++) { blocks[replaceIndex].data[i] = address + i; } blocks[replaceIndex].accessTime++; blocks[replaceIndex].data[offset] = value; blocks[replaceIndex].dirty = true; } } }; int main() { Cache cache(4, 16); cout << "Read data from address 0: " << cache.read(0) << endl; cout << "Write data to address 8" << endl; cache.write(8, 123); cout << "Read data from address 4: " << cache.read(4) << endl; cout << "Write data to address 12" << endl; cache.write(12, 456); cout << "Read data from address 8: " << cache.read(8) << endl; return 0; } ``` 该程序实现了一个大小为16字节的Cache,每个Cache块大小为4字节,使用直接映射地址转换机制和最近最少使用算法进行缓存替换,使用策略进行缓存回。程序输出结果如下: ``` Cache miss! Read data from memory to cache block 0 Read data from address 0: 0 Write data to address 8 Cache miss! Read data from memory to cache block 2 Read data from address 4: 4 Write data to address 12 Cache miss! Read data from memory to cache block 1 Read data from address 8: 123 ``` 希望以上信息能够对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖色的杯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值