您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

10张图开启CPU缓存一致性之门 《源码分享》

  • 时间:2022-09-06 04:23 编辑: 来源: 阅读:309
  • 扫一扫,手机访问
摘要:10张图开启CPU缓存一致性之门 《源码分享》
直接BB的不多 10张图打开CPU缓存一致性的大门CPU缓存的文本数据写入随着时间的推移,CPU和内存的访问性能越来越不一样,所以CPU缓存(Cache)是嵌入在CPU中的,离CPU内核相当近,所以它的访问速度非常快,所以它充当了CPU和内存之间的缓存角色。 CPU缓存通常分为三级:L1缓存、L2缓存和L3缓存。级别越低,离CPU核心越近,访问速度越快,但存储容量越小。 其中,在多核CPU中,每个核都有自己的L1/L2缓存,L3缓存由所有核共享。 10张图打开CPU缓存一致性的大门。我们先简单了解一下CPU缓存的结构。CPU缓存由许多缓存行组成。CPU线是CPU从内存中读取数据的基本单位,CPU线由各种标签+数据块组成。下图可以清晰的看到:10张图打开了CPU缓存一致性的大门。当然,我们期望CPU尽可能多地从CPU缓存中读取数据,而不是每次都从内存中获取数据。 所以作为程序员,要尽量写出缓存命中率高的代码,这样才能有效提高程序的性能。具体方法可以参考我上一篇文章《如何编写让CPU运行更快的代码?其实不仅有读操作,还有写操作。如果将数据写入缓存,则缓存对应的数据会有所不同。在这种情况下,缓存和内存中的数据是不一致的,所以我们必须将缓存中的数据同步到内存中。 问题来了。缓存中的数据何时会写回内存?为了解决这个问题,下面详细介绍两种写数据的方法:直写和回写。要保持内存和缓存的一致性,最简单的方法就是将数据同时写入内存和缓存。这种方法称为直写(*****直写* * * * *)。 10张图打开CPU缓存一致性的大门。在这种方法中,在写入之前,会判断数据是否已经在CPU缓存中:如果数据已经在缓存中,则先将数据升级到缓存中再写入内存;如果数据不在缓存中,直接将数据升级到内存中。 直写法直观简单,但问题明显。无论数据是否在缓存中,每一次写操作都会被写回内存,这会耗费大量时间,无疑对性能影响很大。 写回:写回(* * * *写回* * * * *)是一种减少将数据写回内存的频率的方法,因为每次将数据写回内存都会影响性能。 在回写机制中,当发生写操作时,新数据只写入缓存块,只有修改后的缓存块被“替换”时的* * * *才需要写入内存,减少了数据回写内存的频率,从而提高了系统的性能。 10张图打开CPU缓存一致性的大门。你是怎么做到的?下面详细说一下:如果发生写操作时数据已经在CPU缓存中,则将数据升级到CPU缓存中,并将CPU缓存中的这个缓存块标记为脏。这个脏标记表示此时我们CPU缓存中这个缓存块的数据与内存不一致。在这种情况下,没有必要将数据写入存储器。如果写操作发生时“其他存储器地址的数据”存储在相应的缓存块中,则需要检查该缓存块中的数据是否被标记为脏。如果是,我们就把这个缓存块中的数据写回内存,然后把当前要写入的数据写到这个缓存块中,也把它标记为脏。如果缓存块中的数据没有标记为脏,只需将数据直接写入缓存块,然后将缓存块标记为脏即可。 可以找回写的方法。在将数据写入缓存时,只有当缓存未命中且相应缓存中的缓存块被标记为脏时,才会将数据写入内存。在缓存命中的情况下,缓存写入后,只需将数据对应的缓存块标记为脏,而不是写入内存。 这样做的好处是,如果能以大量的操作命中缓存,CPU大部分时间都不需要读写内存,那么性能自然会比直写高很多 缓存一致性问题现在CPU是多核的,因为L1/L2缓存是多核独有的,所以会带来多核的缓存一致性问题(* * * * * * * * * *)。如果不能保证缓存一致性的问题,结果可能是错误的。 缓存一致性的问题是如何发生的?我们以一个双核的CPU为例。 假设核心A和核心B同时运行两个线程,两个线程都操作公共变量I(初始值为0) 10张图打开CPU缓存一致性的大门。此时,如果core A执行i++语句,为了考虑性能,我们使用前面提到的回写策略。首先,将值为1的执行结果写入L1/L2缓存,然后将L1/L2缓存中的相应块标记为脏。此时,数据实际上并没有同步到内存中。由于采用回写策略,只有当核心A中的缓存块被替换时,才会写入数据。 此时,如果下一个核B试图从内存中读取I变量的值,将会是错误的值。因为刚才核心A的I值还没有写入内存,所以内存中的值还是0。 这就是所谓的缓存一致性问题。此时核心A和核心B的缓存不一致,会导致执行结果出错。 10个映像打开了CPU缓存一致性的大门。因此,为了解决这个问题,需要一种机制来同步两个不同内核中的缓存数据。 要实现这种机制,需要保证以下两点:第一,当一个CPU核中的缓存数据被升级时,必须传播到其他核的缓存中,这就是所谓的写传播(* * * * * wre ite propagation * * * *);第二,一个CPU核中数据的操作顺序在其他核中必须看起来一样,这叫做事务序列化(* * * * * * * * * *)。 写传播的第一点很容易理解。当一个内核升级其缓存中的数据时,它需要与其他内核的缓存同步。 对于事务序列化的第二点,我们举个例子来理解一下。 假设我们有一个有四个核心的CPU,所有的核心都操作公共变量I(初始值为0) 核心A先把I的值改成100,同时核心B先把I的值改成200。这里的两个修改都将“传播”到内核C和d。 10张图打开CPU缓存一致性的大门,那么问题来了。核心C首先接收核心A的升级数据的事件,然后接收核心B的升级数据的事件。所以C核看到的变量I先改成100,再改成200。 如果核心D收到的事件反过来,核心D会看到变量I先变成200,然后变成100。即使实现了写入传播,每个缓存中的数据仍然不一致。 因此,我们需要确保核心C和核心D都能以相同的顺序看到数据变化。比如变量I先改成100,再改成200。这个过程就是事务的序列化。 要实现事务序列化,需要做两件事:CPU核需要将缓存中数据的操作同步到其他CPU核;引入“锁”的概念,如果两个CPU核中存在数据相同的缓存,只有缓存数据升级了,才能进行相应的数据升级。 接下来,我们来看看用什么技术来实现写传播和事务序列化。 总线嗅探写入传播的原理是,当CPU内核升级缓存中的数据时,它应该将事件广播给其他内核。 最常见的方式是总线嗅探(* * * *总线嗅探* * * * *)。 我将用前面I变量的例子来说明总线嗅探的工作机制。当CPU内核A修改L1缓存中I变量的值时,它会通过总线将该事件广播给所有其他内核。然后,每个CPU内核将监控总线上的广播事件,并检查其L1缓存中是否有相同的数据。如果CPU核心B的L1缓存中有该数据,则需要将其升级到其L1缓存中。 可以发现,总线嗅探的方法很简单。CPU需要时刻监控总线上的所有活动,但是不管其他核的缓存是否能缓存相同的数据,都需要发出一个广播事件,这无疑会增加总线负载。 另外,总线嗅探只是保证一个CPU核的缓存升级数据能被其他CPU核知道,但不能保证事务序列化。 所以有了基于总线嗅探机制实现事务序列化的协议,也利用状态机机制减轻了总线带宽压力。该协议是MESI协议,实现了CPU缓存的一致性。 MESI协议MESI协议实际上是四个状态字的首字母,分别是:修改、修改独占、独占共享、共享无效、过期,这四个状态来标记缓存行的四种不同状态。 “已修改”状态是前面提到的脏标志,这意味着缓存块上的数据已经升级,但尚未写入内存。 “过期”状态表示该缓存块中的数据已经过期,处于此状态的数据无法读取。 “独占”和“共享”状态都表示缓存块中的数据是干净的,即此时缓存块中的数据与内存中的数据一致。 “独占”和“共享”的区别在于,在独占状态下,数据只存储在一个CPU核的缓存中,而其他CPU核的缓存没有这些数据。 此时,如果要向独占缓存写入数据,可以直接自由写入,无需通知其余CPU内核。由于只有你有这个数据,所以不存在缓存一致性的问题,你可以随意操作数据。 此外,如果其他内核对处于“独占”状态的数据从内存中读取相同的数据到各自的缓存中,那么此时,处于独占状态的数据将变为共享。 那么,“共享”状态意味着相同的数据存在于多个CPU核心的缓存中。因此,当我们想升级缓存中的数据时,我们不能直接修改它。相反,我们必须首先向所有其他CPU核心广播一个请求,以将剩余核心的缓存中的相应缓存线标记为“无效”状态,然后升级当前缓存中的数据。 我们举个具体的例子来看看这四种状态的转变:当CPU核A从内存中读取变量I的值时,数据缓存在CPU核A自己的缓存中,此时其他CPU核的缓存不缓存数据,因此缓存线状态标记为“独占”,此时其缓存中的数据与内存一致;然后CPU核B也从内存中读取变量I的值,并向其他CPU核发送消息。因为CPU核A已经缓存了数据,所以它会将数据返回给CPU核b。 此时,A核和B核缓存相同的数据,缓存行的状态会变成“共享”,其缓存中的数据与内存一致。当CPU核A要修改缓存中I变量的值,发现数据对应的缓存线状态为共享时,会向其他所有CPU核广播一个请求,要求它们先将其他核中对应的缓存线标记为“无效”,然后CPU核A会对缓存中的数据进行升级,将缓存线标记为“已修改”,这样缓存中的数据就与内存不一致了。 如果CPU核A“继续”修改缓存中I变量的值,由于此时的缓存行处于“已修改”状态,所以不需要向其他CPU核发送消息,直接升级数据即可。 如果CPU核A的缓存中I变量对应的缓存线要被“替换”,并且发现该缓存线状态为“已修改”,则在替换之前将数据同步到内存中。 因此可以发现,当缓存线状态为“修改”或“独占”时,不需要向其他CPU核发送广播来修改和升级其数据,这无疑减轻了总线带宽压力。 事实上,整个MESI的状态可以用一个有限状态机来表示。 此外,不同状态触发的事件操作可以是来自本地CPU核的广播事件,也可以是通过总线来自其他CPU核的广播事件。 下图是MESI协议的状态图:10张图打开了CPU缓存一致性的大门。我把MESI协议四个状态之间的流转过程总结成了下表。您可以更详细地看到每个状态转换的原因:10张图打开了CPU缓存一致性的大门。CPU读写数据时,是在CPU缓存中读写数据。原因是缓存接近CPU,读写性能远高于内存。 如果CPU需要读取的数据没有缓存在缓存中,CPU会从内存中读取数据,将数据缓存到缓存中,最后CPU会从缓存中读取数据。 对于数据写入,CPU会先写入缓存,然后在合适的时间写入内存。保证缓存和内存的数据一致性有两种策略,分别是“直写”和“回写”:直写,只要有数据写入,数据就会直接写入内存。这种方法简单直观,但性能会受到存储器访问速度的限制;回写:对于已经缓存在缓存中的数据,只需要升级其数据,而不需要写入内存。只有当缓存中的脏数据需要换出时,数据才会同步到内存中。这样在缓存命中率高的时候性能会更好;现在的CPU都是多核,每个核都有自己独立的L1/L2缓存,只有L3缓存在多个核之间共享。 所以一定要保证多核缓存一致,否则会出现错误的结果。 要实现缓存一致性,关键是要满足两点:第一点是写传播,即当一个CPU核有写操作时,需要将事件广播给其他核;第二点是事物的序列化,这一点很重要。只有保证了这一点,才能保证我们的数据是真正一致的,我们的程序在不同内核上运行的结果也是一致的。基于总线嗅探机制的MESI协议满足了以上两点,是一种保证缓存一致性的协议。 MESI协议是修改状态、独占状态、共享状态和实现状态的英文缩写的组合。 整个MSI状态的改变是基于来自本地CPU核的请求或者通过总线传输的来自其他CPU核的请求,从而形成一个流动的状态机。 此外,对于处于“已修改”或“独占”状态的高速缓存行,不需要向其他CPU核心发送广播来修改和升级其数据。


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术支持|常见问题】1556原创ng8文章搜索页面不齐(2024-05-01 14:43)
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)
【技术支持|常见问题】别告诉我你没看过邰方这两则有思想的创意广告! (2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部