ToB企服应用市场:ToB评测及商务社交产业平台

标题: 16、CPU缓存架构 [打印本页]

作者: 北冰洋以北    时间: 2024-11-19 22:19
标题: 16、CPU缓存架构
CPU缓存架构详解

CPU高速缓存概念

  CPU缓存即高速缓冲存储器,是位于CPU与主内存间的一种容量较小但速度很高的存储器。CPU高速缓存可以分为一级缓存,二级缓存,部门高端CPU还具有三级缓存,每一级缓存中所储存的全部数据都是下一级缓存的一部门,这三种缓存的技术难度和制造资本是相对递减的,所以其容量也是相对递增的。

  由于CPU的速度远高于主内存,CPU直接从内存中存取数据要等待肯定时间周期,Cache中保存着CPU刚用过或循环使用的一部门数据,当CPU再次使用该部门数据时可从Cache中直接调用,减少CPU的等待时间,提高了系统的服从。

在CPU访问存储装备时,无论是存取数据抑或存取指令,都趋于聚集在一片一连的地域中,这就是局部性原理
时间局部性(Temporal Locality):假如一个信息项正在被访问,那么在近期它很可能还会被再次访问。
好比循环、递归、方法的反复调用等。
空间局部性(Spatial Locality):假如一个存储器的位置被引用,那么将来他附近的位置也会被引用。
好比次序执行的代码、一连创建的两个对象、数组等。
CPU多核缓存架构

  当代CPU为了提拔执行服从,减少CPU与内存的交互,一般在CPU上集成了多级缓存架构,常见的为三级缓存结构。如下图:

  CPU寄存器是位于CPU内部的存储器,数据的读写速度非常快,比缓存和主存更快。CPU会把常用的数据放到寄存器中举行处理,而不是直接从主存中读取,如许可以加速数据的访问和处理。但是寄存器的容量非常有限,一般只有几十个大概几百个字节,因此只能存储少量的数据。
  当CPU读取一个地址中的数据时,会先在L1 Cache中查找。假如数据在L1 Cache中找到,CPU会直接从L1 Cache中读取数据。假如没有找到,则会将这个请求发送给 L2 Cache,然后在L2 Cache中查找,假如L2 Cache中也没有找到,则会继续将请求发送到L3 Cache中。假如在L3 Cache中照旧没有找到数据,则最后会从主内存中读取数据并将其存储到CPU的缓存中。
  当CPU写入一个地址中的数据时,同样会先将数据写入L1 Cache中,然后再根据缓存一致性协议将数据写入 L2 Cache、L3 Cache以及主内存中。具体写入过程与缓存一致性协议相关,有可能只写入L1 Cache中,也有可能必要将数据写入 L2 Cache、L3 Cache以及主内存中。写入过程中也可能会使用缓存行失效、写回等技术来提高服从。
CPU多核缓存架构缓存一致性题目分析

场景一

场景二

  在CPU多核缓存架构中,每个处理器都有一个单独的缓存,共享数据可能有多个副本:一个副本在主内存中,一个副本在请求它的每个处理器的本地缓存中。当数据的一个副本发生更改时,其他副本必须反映该更改。也就是说,CPU多核缓存架构要保证缓存一致性。
CPU缓存架构缓存一致性的办理方案

《64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf》中有如下描述:
  1. The 32-bit IA-32 processors support locked atomic operations on locations in system memory. These operations are typically used to manage shared data structures (such as semaphores, segment descriptors, system segments, or page tables) in which two or more processors may try simultaneously to modify the same field or flag. The processor uses three interdependent mechanisms for carrying out locked atomic operations:
  2. • Guaranteed atomic operations
  3. • Bus locking, using the LOCK# signal and the LOCK instruction prefix
  4. • Cache coherency protocols that ensure that atomic operations can be carried out on cached data structures (cache lock); this mechanism is present in the Pentium 4, Intel Xeon, and P6 family processors
复制代码
  32位的IA-32处理器支持对系统内存中的位置举行锁定的原子操作。这些操作通常用于管理共享的数据结构(如信号量、段描述符、系统段或页表),在这些结构中,两个或多个处理器可能同时试图修改相同的字段或标志。处理器使用三种相互依赖的机制来执行锁定的原子操作:

  缓存一致性协议是一种用于确保处理器缓存中的数据和主存中的数据一致的机制。缓存一致性协议会通过处理器之间的通信,确保在一个处理器修改了某个数据后,其他处理器缓存中的该数据会被更新大概失效,从而保证在多个处理器同时对同一个数据举行操作时,它们所看到的数据始终是一致的。
  缓存锁定则是在缓存一致性协议的底子上实现原子操作的机制,它会利用缓存一致性协议来确保在多个处理器同时修改同一个缓存行中的数据时,只有一个处理器能够获得锁定,从而保证原子性。缓存锁定的实现也必要硬件的支持,而且不同的处理器架构可能会有不同的实现方式。
缓存一致性协议不能使用的特殊情况:

但当代的处理器通常都支持缓存锁定机制,因此应该尽量使用缓存锁定来实现原子操作,以获得更好的性能。
缓存一致性协议实现原理

总线窥探

  总线窥探(Bus snooping)是缓存中的一致性控制器(snoopy cache)监视或窥探总线事务的一种方案,其目标是在分布式共享内存系统中维护缓存一致性。包含一致性控制器(snooper)的缓存称为snoopy缓存。该方案由Ravishankar和Goodman于1983年提出。
  1. 在计算机中,数据通过总线在处理器和内存之间传递。每次处理器和内存之间的数据传递都是通过一系列步骤来完成的,这一系列步骤称之为总线事务(Bus Transaction)
复制代码
工作原理

  当特定命据被多个缓存共享时,处理器修改了共享数据的值,更改必须传播到所有其他具有该数据副本的缓存中。这种更改传播可以防止系统违反缓存一致性。数据变动的通知可以通过总线窥探来完成。所有的窥探者都在监视总线上的每一个事务。假如一个修改共享缓存块的事务出如今总线上,所有的窥探者都会检查他们的缓存是否有共享块的相同副本。假如缓存中有共享块的副本,则相应的窥探者执行一个动作以确保缓存一致性。这个动作可以是刷新缓存块或使缓存块失效。它还涉及到缓存块状态的改变,这取决于缓存一致性协议(cache coherence protocol)。
窥探协议类型

根据管理写操作的本地副本的方式,有两种窥探协议:

缓存一致性协议

  缓存一致性协议在多处理器系统中应用于高速缓存一致性。为了保持一致性,人们设计了各种模型和协议,如MSI、MESI(又名Illinois)、MOSI、MOESI、MERSI、MESIF、write-once、Synapse、Berkeley、Firefly和Dragon协议。
MESI协议

  MESI协议是一个基于写失效的缓存一致性协议,是支持回写(write-back)缓存的最常用协议。也称作伊利诺伊协议 (Illinois protocol,因为是在伊利诺伊大学厄巴纳-香槟分校被发明的)。
缓存行有4种不同的状态:

  MESI协议用于确保多个处理器之间共享的内存数据的一致性。当一个处理器必要访问某个内存数据时,它起首会检查自己的缓存中是否有该数据的副本。假如缓存中没有该数据的副本,则会发出一个缓存不命中(miss)请求,从主内存中获取该数据的副本,并将该数据的副本存储到自己的缓存中。
  当一个处理器发出一个缓存不命中请求时,假如该数据的副本已经存在于另一个处理器或焦点的缓存中(即处于共享状态),则该处理器可以从另一个处理器的缓存中复制该数据的副本。这个过程称为缓存到缓存复制(cache-to-cache transfer)。
  缓存到缓存复制可以减少对主内存的访问,从而提高系统的性能。但是,必要确保数据的一致性,否则会出现数据错误或不一致的情况。因此,在举行缓存到缓存复制时,必要使用MESI协议中的其他状态转换来确保数据的一致性。比方,假如两个缓存都处于修改状态,那么必须先将此中一个缓存的数据写回到主内存,然后才能举行缓存到缓存复制。
伪共享的题目

  假如多个核上的线程在操作同一个缓存行中的不同变量数据,那么就会出现频繁的缓存失效,即使在代码层面看这两个线程操作的数据之间完全没有关系。这种不合理的资源竞争情况就是伪共享(False Sharing)。
ArrayBlockingQueue有三个成员变量:

  这三个变量很轻易放到一个缓存行中,但是之间修改没有太多的关联。所以每次修改,都会使之前缓存的数据失效,从而不能完全到达共享的结果。

  如上图所示,当生产者线程put一个元素到ArrayBlockingQueue时,putIndex会修改,从而导致消耗者线程的缓存中的缓存行无效,必要从主存中重新读取。
linux下查察Cache Line巨细

Cache Line巨细是64Byte

大概执行 cat /proc/cpuinfo 命令

避免伪共享方案

方案1:缓存行填充
  1. class Pointer {
  2.     volatile long x;
  3.     //避免伪共享: 缓存行填充
  4.     long p1, p2, p3, p4, p5, p6, p7;
  5.     volatile long y;
  6. }
复制代码
方案2:使用 @sun.misc.Contended 注解(java8)
注意必要配置jvm参数:-XX:-RestrictContended
  1. public class FalseSharingTest {
  2.     public static void main(String[] args) throws InterruptedException {
  3.         testPointer(new Pointer());
  4.     }
  5.     private static void testPointer(Pointer pointer) throws InterruptedException {
  6.         long start = System.currentTimeMillis();
  7.         Thread t1 = new Thread(() -> {
  8.             for (int i = 0; i < 100000000; i++) {
  9.                 pointer.x++;
  10.             }
  11.         });
  12.         Thread t2 = new Thread(() -> {
  13.             for (int i = 0; i < 100000000; i++) {
  14.                 pointer.y++;
  15.             }
  16.         });
  17.         t1.start();
  18.         t2.start();
  19.         t1.join();
  20.         t2.join();
  21.         System.out.println(pointer.x+","+pointer.y);
  22.         System.out.println(System.currentTimeMillis() - start);
  23.     }
  24. }
  25. class Pointer {
  26.     // 避免伪共享: @Contended +  jvm参数:-XX:-RestrictContended  jdk8支持
  27.     //@Contended
  28.     volatile long x;
  29.     //避免伪共享: 缓存行填充
  30.     //long p1, p2, p3, p4, p5, p6, p7;
  31.     volatile long y;
  32. }
复制代码
方案3:使用线程的本地内存,好比ThreadLocal

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4