代码编织梦想

在这里插入图片描述
在这里插入图片描述


Cache

高速缓冲存储器

内存一般采用SDRAM芯片,对内存的访问肯定是不及CPU的速度的,通常说内存访问要比CPU的速度慢的多。也就是说内存拖后腿了。
CPU访问内存并不是完全随机的。 在某个时间段内,CPU总是访问当前内存地址的相邻内存地址, 想象一下指令顺序执行和循环执行的情景, 这种情景就是著名的程序局部性原理。
基于程序局部性原理,CPU和内存之间可以放置一个小而快的中间存储器,它的速度和CPU的速度差不多,事实上要慢一丢丢,但比内存快得多,这就是高速缓冲存储器。

高速缓冲存储器可以是统一的也可以是分开的。
统一是指数据和指令放在同一个缓冲器中
分开是指数据和指令分别用不同的缓冲器存放

RAM分两种,一种是静态RAM,SRAM(Static RAM);一种是动态RAM,DRAM(Dynamic RAM)。前者的存储速率要比后者快得多
使用的内存一般都是动态RAM。为了增加系统的速率,把缓存扩大就行了,扩的越大,缓存的数据越多,系统就越快了,缓存通常都是静态RAM,速率是非常的快, 但是静态RAM集成度低(存储相同的数据,静态RAM的体积是动态RAM的6倍), 价格高(同容量的静态RAM是动态RAM的四倍), 由此可见,扩大静态RAM作为缓存是一个非常愚蠢的行为, 但是为了提高系统的性能和速率,必须要扩大缓存, 这样就有了一个折中的方法,不扩大原来的静态RAM缓存,而是增加一些高速动态RAM做为缓存, 这些高速动态RAM速率要比常规动态RAM快,但比原来的静态RAM缓存慢, 把原来的静态RAM缓存叫一级缓存,而把后来增加的动态RAM叫二级缓存。


工作机制

缓存只是内存中少部分数据的复制品,所以CPU到缓存中寻找数据时,也会出现找不到的情况(因为这些数据没有从内存复制到缓存中去),这时CPU还是会到内存中去找数据,这样系统的速率就慢下来了,不过CPU会把这些数据复制到缓存中去,以便下一次不要再到内存中去取。随着时间的变化,被访问得最频繁的数据不是一成不变的,也就是说,刚才还不频繁的数据,此时已经需要被频繁的访问,刚才还是最频繁的数据,又不频繁了,所以说缓存中的数据要经常按照一定的算法来更换,这样才能保证缓存中的数据是被访问最频繁的。

CPU下一次要读取的数据90%都在CPU缓存中,只有大约10%需要从内存读取。这大大节省了CPU直接读取内存的时间,也使CPU读取数据时基本无需等待。总的来说,CPU读取数据的顺序是先缓存后内存。

  • Cache收到CPU访问内存的地址
  • Cache的地址变换逻辑 —> 将CPU访问内存的地址分解为块号和块内偏移
  • 通过第二步中分解出的块号和块偏移去寻找Cache块
  • 如果能找到Cache块,则命中,同时去索引块中数据。如果是访问内存的操作则将数据返回给CPU如果是写内存操作,根据Cache类型的不同Cache的动作也不同
  • 如果第3步中没有找到对应的Cache块,则未命中
  • 如果Cache未命中, Cache首先查找Cache内部有没有空闲块。
  • 如果5中找到空闲块,则装入CPU访问内存地址对应的内存块。同时如果是读内存,则代替CPU访问内存后返回数据给CPU。如果是写内存,则根据Cache类型的不同Cache的动作也不同
  • 如果第6步中Cache没有找到一个空闲块, 就让Cache块替换逻辑, 找出Cache中最值得替换出去的块。 并且有相关的替换算法, 只是这种算法是硬件实现的。 如果CPU是读内存操作, 那么根据替换块的块号和状态, Cache会决定是否把这个块回写到内存中, 或者直接废除, 最后在该替换出去的块中装入CPU访问内存地址对应的内存块, 同时把这个地址对应的数据返回给CPU。 如果CPU是写内存操作, 根据Cache的类型不同Cache的动作也不同。
    在这里插入图片描述

Cache的类型

根据Cache的工作机制, 可以把Cache分成很多类型, 我们主要看看下面两种类型, 在ARM920T中知道这两种就够了。 当然了解这两种类型是为了要说明后面要注意的问题, “要注意的问题”才是重要的。

  • 回写式Cache。
  • 写通式Cache。

当CPU执行写数据操作时, 回写式Cache只把该数据写入其数据地址对应的Cache块中, 不直接写入内存。
当CPU执行写数据操作时, 写通式Cache必须同时把该数据写入其数据地址对应的Cache块和内存中。

一些Notice

采用Cache后可能出现的问题

1 一致性问题。

如果硬件系统中有多核心的CPU, 每个CPU核心内部都有独立的Cache。 这时如果内存中有个全局数据A且A=0。 如果两个CPU核都对其执行加1操作。 如果按照下面的执行顺序, 就会出现问题。 而事实上如果不加控制我们无法对这种硬件的执行顺序进行估计, 很有可能就发生

下面的执行顺序。
1) CPU0读取内存中A的数据到R0寄存器, 并且缓存A的数据到CPU0的Cache中。 CPU0的R0寄存器中为0, CPU0的Cache中A数据对应的Cache块中的单元为0。2) CPU0对它的R0寄存器执行加1操作, 并且把结果写入CPU0的Cache中, 假定这个Cache是回写式Cache, CPU0的Cache中A数据对应的Cache块中的单元为1。 CPU0接着执行下一条指令。
3) CPU1读取内存中A的数据到R1寄存器, 并且缓存A的数据到CPU1的Cache中。 由于第2) 步中CPU0的Cache中的A数据没有回写到内存中, 内存中A数据没有改变。 所以CPU1的R1寄存器中为0, CPU1的Cache中A数据对应的Cache块中的单元为0。
4) CPU1对它的R1寄存器执行加1操作, 并且把结果写入CPU1的Cache中, 假定这个Cache是回写式Cache, CPU1的Cache中A数据对应的Cache块中的单元为1。 CPU1接着执行下一条指令。这时不管哪个CPU的Cache中A数据对应的Cache块发生了替换, 内存中数据A为1。

2 写入实时性问题

在打开Cache的情况下, 如果读取RTC寄存器中的状态数据时, 很有可能读取的不是RTC寄存器的最新数据而是读取缓存在Cache中的数据。 这会使我们获取的数据不准确, 有时会产生致命的错误。 如果写入数据到RTC寄存器中对RTC设备进行控制。 这个数据会首先写入Cache中(如果是回写式Cache) 。 我们满心以为RTC设备已经得到了控制,然而我们错了, 这时控制数据也许还在Cache中, 还没有到RTC设备控制寄存器中去。 当然其他设备也是一样。 这种问题非常危险, 而且这种类型的错误根本就不是由程序代码本身的问题导致的, 查起来也不是一般的困难。

解决方式:
解决上述问题的最好方法是, 对映射设备寄存器的存储器地址空间, 不执行Cache缓存。 在MMU的章节中, 了解到每个页表条目中有C、 B两个位。 这两个位就是控制这个页表条目映射的地址空间, 是否被Cache部件(C位) 或者写缓冲部件(B位, 写缓冲也是一种小型的Cache, 但是不详细研究它) 缓存。 只要把设备寄存器所在的页面的页表条目中的C、 B位设置为0, 就解决上述问题了。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43604927/article/details/128000848

爬虫库(Requests-Cache爬虫缓存)-爱代码爱编程

Requests-Cache爬虫缓存 简述安装在Requests中使用缓存示例缓存的存储机制 简述 Requests模块的扩展功能,通过Requests发送请求来生成相应的缓存数据。当Requests重复向同一个URL发送请求的时候,Requests-Cache会判断当前请求是否已产生缓存,若已有缓存,则从缓存里读取数据作为响应内容;若没有缓

Rocket-chip-Cache-爱代码爱编程

关于cache的知识,大家可以看以下链接:https://zhuanlan.zhihu.com/p/102293437?utm_source=qq 下面为验证rocket-chip icache和dcache的代码。 在这里我只验证cache起到的作用,不会分析直接映射缓存、多路组相缓存和全相连缓存在rocket-chip上的差异,也不会分析具体的mis