① 计算机组成原理之缓存行 cacheLine
缓存行:64字节;缓存行越大,局部空间效率高,读取时间慢;反之局部空间效率低读取速度快,所以折中选择了64个字节;
如何保证CPU中不同L1缓存、L2缓存的 缓存一致性?
MESI缓存一致性协议, 因特尔CPU的协议
其他的缓存一致性协议有:
MSI/MOSI/Synapse/Firefly/Dragon
L1:CPU一级缓存;
L2:CPU二级缓存;
L3:CPU三级缓存;
其中 L3 是计算 CPU共享的,L1、L2是CPU中 核 独享的;
缓存一致性的实现:如上图
如果左侧核的X值在L1中被修改了----modified,此时会有通知到右侧的核 的X为---invalid;
之后,右侧再读取X值时发现无效会往上层查询有效数据;
为了减少 CPU 中缓存数据被不同的 核 读取,程序设计中有一个 RingBuffer 环形缓存,在实际存储变量 INITAL_CURSOR_VALUE 前后都增加了7个变量,long类型的长度时8字节;CPU在读取数据时会修改的变量只有 INITAL_CURSOR_VALUE ,数据只会被其中一个核读取,就不会出现使用 MESI缓存一致性协议 去解决缓存一致性导致的耗时问题;
除了上述方式,JDK1.8中 使用了注解的方式强制让一个变量占据一个缓存行;
@sun.misc.Contented
② 缓存一致性
在现代的 CPU(大多数)上,所有的内存访问都需要通过层层的缓存来进行。CPU 的读 / 写(以及取指令)单元正常情况下甚至都不能直接访问内存——这是物理结构决定的;CPU 都没有管脚直接连到内存。相反,CPU 和一级缓存(L1 Cache)通讯,而一级缓存才能和内存通讯。大约二十年前,一级缓存可以直接和内存传输数据。如今,更多级别的缓存加入到设计中,一级缓存已经不能直接和内存通讯了,它和二级缓存通讯——而二级缓存才能和内存通讯。或者还可能有三级缓存。
缓存是分“段”(line)的,一个段对应一块存储空间,大小是 32、64或128字节,每个缓存段知道自己对应什么范围的物理内存地址。
当 CPU 看到一条读内存的指令时,它会把内存地址传递给一级数据缓存。一级数据缓存会检查它是否有这个内存地址对应的缓存段。如果没有,它会把整个缓存段从内存(或者从更高一级的缓存,如果有的话)中加载进来。是的,一次加载整个缓存段,这是基于这样一个假设:内存访问倾向于本地化(localized),如果我们当前需要某个地址的数据,那么很可能我们马上要访问它的邻近地址。一旦缓存段被加载到缓存中,读指令就可以正常进行读取。
如果我们只处理读操作,那么事情会很简单,因为所有级别的缓存都遵守以下规律—— 在任意时刻,任意级别缓存中的缓存段的内容,等同于它对应的内存中的内容。 。
一旦我们允许写操作,事情就变得复杂一点了。这里有两种基本的写模式:直写(write-through)和回写(write-back)。直写更简单一点:我们透过本级缓存,直接把数据写到下一级缓存(或直接到内存)中,如果对应的段被缓存了,我们同时更新缓存中的内容(甚至直接丢弃),就这么简单。这也遵守前面的定律: 缓存中的段永远和它对应的内存内容匹配。
回写模式就有点复杂了。缓存不会立即把写操作传递到下一级,而是仅修改本级缓存中的数据,并且把对应的缓存段标记为“脏”段。脏段会触发回写,也就是把里面的内容写到对应的内存或下一级缓存中。回写后,脏段又变“干净”了。当一个脏段被丢弃的时候,总是先要进行一次回写。回写所遵循的规律有点不同。 当所有的脏段被回写后,任意级别缓存中的缓存段的内容,等同于它对应的内存中的内容。
换句话说,回写模式的定律中,我们去掉了“在任意时刻”这个修饰语,代之以弱化一点的条件:要么缓存段的内容和内存一致(如果缓存段是干净的话),要么缓存段中的内容最终要回写到内存中(对于脏缓存段来说)。
只要系统只有一个 CPU 核在工作,一切都没问题。如果有多个核,每个核又都有自己的缓存,那么我们就遇到问题了,因为如果一个 CPU 缓存了某块内存,那么在其他 CPU 修改这块内存的时候,我们希望得到通知。系统的内存在各个 CPU 之间无法做到与生俱来的同步,我们需要一个大家都能遵守的方法来达到同步的目的。
缓存一致性协议有多种,但是日常处理的大多数计算机设备使用的都属于“窥探(snooping)”协议。
窥探”背后的基本思想是,所有内存传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线:缓存本身是独立的,但是内存是共享资源,所有的内存访问都要经过仲裁(arbitrate):同一个指令周期中,只有一个缓存可以读写内存。窥探协议的思想是,缓存不仅仅在做内存传输的时候才和总线打交道,而是不停地在窥探总线上发生的数据交换,跟踪其他缓存在做什么。所以当一个缓存代表它所属的处理器去读写内存时,其他处理器都会得到通知,它们以此来使自己的缓存保持同步。只要某个处理器一写内存,其他处理器马上就知道这块内存在它们自己的缓存中对应的段已经失效。
在直写模式下,这是很直接的,因为写操作一旦发生,它的效果马上会被“公布”出去。但是如果混着回写模式,就有问题了。因为有可能在写指令执行过后很久,数据才会被真正回写到物理内存中——在这段时间内,其他处理器的缓存也可能会傻乎乎地去写同一块内存地址,导致冲突。在回写模型中,简单把内存写操作的信息广播给其他处理器是不够的,我们需要做的是,在修改本地缓存之前,就要告知其他处理器。
MESI 是四种缓存段状态的首字母缩写,任何多核系统中的缓存段都处于这四种状态之一。
从CPU读写角度来说:
上图的切换解释:
缓存的一致性消息传递是要时间的,这就使其切换时会产生延迟。当一个缓存被切换状态时其他缓存收到消息完成各自的切换并且发出回应消息这么一长串的时间中CPU都会等待所有缓存响应完成。可能出现的阻塞都会导致各种各样的性能问题和稳定性问题。
比如你需要修改本地缓存中的一条信息,那么你必须将I(无效)状态通知到其他拥有该缓存数据的CPU缓存中,并且等待确认。等待确认的过程会阻塞处理器,这会降低处理器的性能。因为这个等待远远比一个指令的执行时间长的多。
为了避免这种CPU运算能力的浪费,Store Bufferes被引入使用。处理器把它想要写入到主存的值写到缓存,然后继续去处理其他事情。当所有失效确认(Invalidate Acknowledge)都接收到时,数据才会最终被提交。
执行失效也不是一个简单的操作,它需要处理器去处理。另外,存储缓存(Store Buffers)并不是无穷大的,所以处理器有时需要等待失效确认的返回。这两个操作都会使得性能大幅降低。为了应付这种情况,引入了失效队列——对于所有的收到的Invalidate请求,Invalidate Acknowlege消息必须立刻发送,Invalidate并不真正执行,而是被放在一个特殊的队列中,在方便的时候才会去执行,处理器不会发送任何消息给所处理的缓存条目,直到它处理Invalidate。
③ volitate 原理
volatile保证多线程可见性,volatile修饰的变量不会引起上下文切换和调度
cpu缓存,cpu运算速度与内存读写不匹配,因为cpu运算速度比内存读写快的多
从主内存中获取或者写入数据会花费很长时间,现在大多数cpu都不会直接访问内存,而是访问cpu缓存,cpu缓存是cpu与主内存之间的临时存储器,容量小,交换速度快,缓存中的数据是内存中的一小部分数据,是cpu即将访问的。当cpu调用大量数据时候,就先从缓存中读取从而加快读取速度
按照读取顺序与cpu结合的紧密程度,cpu缓存分为
一级缓存:L1位于cpu内核旁边,是与cpu结合最为紧密的cpu缓存
二级缓存:L2分为内部和外部两种芯片,内部芯片二级缓存运行速度与主频相同,外部芯片二级缓存运行速度则只有主频的一半
三级缓存,只有高端的cpu才有
每一级缓存中所存储的数据都是下一级缓存中存储的数据的一部分
cpu要读取一个数据的时候,首先从一级缓存中查找,如果没有就从二级中查找,如果还没有就从三级缓存中或者是内存总进行查找,一般来说,每级缓存的命中率大概有0.8左右,也就是全部数据量的0.8可以在一级缓存中查到,只有剩下的0.2总数据量从二级缓存中或者是三级缓存或者是内存中读取
缓存行:缓存是分line的,一个段对应一个缓存行,是cpu缓存种可分配的最小存储单元,通常是64字节:当cpu看到一条读取内存的指令的时候,会把内存地址传递给一级缓存,一级缓存会检查它是否有这个内存地址对应的缓存段,如果没有就把整个缓存段从内存共或者更高级的缓存种加载进来。
cpu执行计算的过程为:程序和数据被加载到主内存中,指令和数据被加载到cpu缓存中,cpu执行指令将结果写入cpu缓存中,cpu缓存中的数据写回到主内存中,但是这种方式仅限于单核cpu的时候
如果服务器是多核cpu呢,
多核处理器中主内存核处理器一样是分开的,这时候,L3作为统一的高速缓存共享,处理器1拥有自己的L1 L2
这个时候当核0读取了一个字节根据局部性原理,与他相邻的字节同样会被读入核0的缓存中
核3也读取了同样的一个字节,根据局部性原理,与他相邻的字节同样会被读入到核3的数据中
此时,核0和核3的缓存中拥有同样的数据
核0修改了那个字节之后,被修改后那个字节被回写到了核0的缓存中,但是该信息并没有回写到主内存
当核3访问该数据的时候,造成该数据不同步
为了解决这个问题**,当一个cpu修改缓存中的字节的时候,**服务器中其他cpu的会被通知他们的缓存将是为无效,这样核1在修改缓存中的数据的时候,核3会发现自己的缓存中的数据已经无效,核0将自己的写回到主内存中,然后核3将重新读取该数据
将代码转化为汇编指令的时候发现在汇编指令add之前有一个lock指令,lock指令就是关键。
lock指令的作用:在修改内存的时候使用lock前缀指令调用加锁的读修改写操作,保证多处理器系统总处理器之间进行可靠的通讯
1.锁总线,其他cpu对内存的读写请求会被阻塞,直到锁释放,不过实际候来的处理器都采用了缓存缓存代替锁总线,因为总线开销过大,锁总线的时候其他cpu没办法访问内存
2.lock后的写操作会回写已经修改的数据,同时让其他cpu相关缓存行失效,从而重新从内存中加载最新的数据
3.不是内存屏障却能完成内存屏障的功能,阻止屏障两边的指令重排序
嗅探式的缓存一致性协议:所有内存的传输都发生在一条共享的总线上,而所有的处理器都能看到这条总线,缓存本身是独立的,但是内存是共享的。所有的内存访问都要进行仲裁,即同一个指令周期种只有一个cpu缓存可以读写数据。cpu缓存不仅在内存传输的时候与总线打交道,还会不断的在嗅探总线上发生数据交换跟踪其他缓存在做什么,所以当一个cpu缓存代表它所属的处理器读写内存的时候,其他的处理器都会得到通知(主动通知),他们以此使自己的缓存保存同步。只要某个处理器写内存,其他处理器就马上直到这块内存在他们的缓存段种已经失效。。
MESI协议是缓存一致性协议,在MESI协议中每个缓存行有四个状态,Modified修改的,表示这行数据有效,数据被修改了和内存中的数据不一致,数据只存在当前cache中,Exclusive独有的,这行数据有效,数据和内存中的数据一致,数据只存在在本cache中,Shared共享的,这行数据有效,数据和内存中的数据一致,数据存在很多cache中,Invalid这行数据无效,这里的Invalid shared modified都符合我们的嗅探式的缓存一致性协议,但是Exclusive表示独占的,当前数据有效并且和内存中的数据一致,但是只在当前缓存中,Exclusive状态解决了一个cpu缓存在读写内存的之前我们要通知其他处理器这个问题,只有当缓存行处于Exclusive和modified的时候处理器才能写,也就是说只有在这两种状态之下,处理器是独占这个缓存行的,当处理器想写某个缓存行的时候,如果没有独占权就必须先发送一条我要独占权的请求给总线,这个时候会通知处理器把他们拥有同一缓存段的拷贝失效,只要在获得独占权的时候处理器才能修改数据并且此时这个处理器直到这个缓存行只有一份拷贝并且只在它的缓存里,不会有任何冲突,反之如果其他处理器一直想读取这个缓存行(马上就能直到,因为一直在嗅探总线),独占或已修改的缓存行必须要先回到共享状态,如果是已经修改的缓存行,还要先将内容回写到内存中。
volatile变量的读写
工作内存其实就是cpu缓存,当两条线程同时操作主内存中的一个volatile变量时候,A线程写了变量i,此时A线程发出lock指令,发出的lock指令锁总线或者锁缓存行,同时线程b的高速缓存中的缓存行内容失效,线程A想内存中回写最新的i。当线程B读取变量的时候,线程发现对应地址的缓存行被锁了等待锁释放,锁的一致性协议会保证它读取到最新的值。
④ 有谁知道cache的发展过程
纵观PC系统和CPU二十年的发展,随着半导体加工工艺水平的不断提高,CPU和存储器的性能都有了很大的提高。
CPU频率的提高,必然要求系统中存储器的存取速度要提高,还要求其容量要增大。主存储器DRAM容量的提高还是比较快的,但是DRAM读取时间的提高却很慢。从而在速度上与CPU主频的提高产生了极不相配的情况,这样会影响整个系统的性能。二十年来,CPU设计的问题之一就是解决高速CPU和低速DRAM之间的平衡或匹配问题,以求系统性能的整体提高。
在它们之间加入高速缓冲存储器Cache,就是这个问题的解决方案之一。
Cache随CPU的发展而不断改变,可以概括为:从无到有,由小到大,先外后内,纵深配备,软硬兼施。初期的CPU没有Cache,在80386时期出现外部Cache;80486时期开始有内部仅8kB的Cache。Cache的分级也由L1和L2级,发展到L0和L3级的纵深配备;Cache的大小由当初的8kB,直到Merced的1~2MB。为了更好地利用Cache,还专门配有缓存控制指令。
本文回顾了在过去的二十年中,Cache技术的发展历程,并对PC其它设备使用Cache技术作了简单陈述。 PC初期无需Cache在八十年代初,由于CPU主频很低,DRAM的存取时间甚至快于CPU存取时间,因此无需Cache。例如,当时PC机采用8088CPU,系统主频为4.77MHz,一个基本总线周期为4拍,即840ns。此时64kB的DRAM存取周期200ns,造成DRAM等待CPU的执行的局面,无需Cache。
在PC/AT机采用80286CPU后,系统主频增加到10MHz,1个基本总线周期为2拍,即200ns。此时必须用读取时间为100ns的DRAM。在采用25MHz的80386DX时,一个基本总线周期为2拍,即80ns,当时已没有速度相匹配的DRAM可用。解决方案有2种:一种是在基本总线周期中插入等待,降低CPU的处理能力;另一种是采用内部和外部Cache,使用SRAM芯片以提高存储器的读取速度。80386没有L1 Cache80386初期主频为20MHz。Intel公司十分重视80386的设计制造,把它定位于“新一代个人电脑架构”,想把一些新技术设计在芯片中。但由于当时工艺所限,内置高速缓存的芯片体积过大,造成成本上升,同时工期有限,几经权衡,最后决定在80386芯片不设置高速缓冲存储器,可以生产另外的Cache,以配合80386运作。
尽管人们意识到CPU主频的增加与内存DRAM存取时间过慢的矛盾已愈加突出,但因条件所限,80386内部没有L1 Cache,只有外部的Cache。80486出现Cache80486是由80386CPU加80387数字协处理器以及8kB Cache构成。
当CPU的时钟频率继续增加时,外部Cache的SRAM芯片速度也要相应提高,这样会增加系统成本,为此在设计80486时采用了内部Cache。
80486芯片内由8kB的Cache来存放指令和数据。同时,80486也可以使用处理器外部的第二级Cache,用以改善系统性能并降低80486要求的总线带宽。Cache可以工作在80486所有的操作模式:实地址模式、保护模式和X86模式。对Cache的操作是由系统自动进行的,对程序员透明。而在多处理器系统中,可能要求系统软件的干预。对于一般的计算机,在系统CMOS设置中均有Cache使用模式的设置。
80486内部Cache是一个4路组相联Cache,在主存储器中给定单元的数据能够存储在Cache内4个单元中的任何一个。这种4路相联方式是高命中率的全相联Cache和快速的直接映像Cache的一种折衷,因而能进行快速查找并获得高的命中率。Peutium的分离L1 Cache和L2 CachePentium处理器采用了超标量结构双路执行的流水线,有分支预测技术。
由于Pentium设计有2条并行整数流水线,可同时执行2条命令。整数单元的潜在处理能力实际可增加一倍,处理器也需要对命令和数据进进双倍的访问。为使这些访问不互相干涉,Intel把在486上共用的内部Cache,分成2个彼此独立的8kB代码Cache和8kB数据Cache,这两个Cache可以同时被访问。这种双路高速缓存结构减少了争用Cache所造成的冲突,提高了处理器效能。Pentium的Cache还采用了回写写入方式,这同486的贯穿写入方式相比,可以增加Cache的命中率。此外,还采用了一种称为MESI高速缓存一致性协议,为确保多处理器环境下的数据一致性提供了保证。Pentium Pro内嵌式L2 Cache为使Pentium Pro的性能超过Pentium,必需使用创新的设计方法。Pentium Pro使用了新的超标量和级流水线技术,包括无序执行、动态分支预测和推测执行的动态执行新技术。它可以使CPU在一个时钟周期执行3条微操作。CPU并行处理速度的加快,意味着它同时处理指令和数据的数量增加,为不使CPU处于等待状态,需要重新设计Cache。
Pentium Pro在片内第一级Cache的设计方案中,使指令Cache与数据Cache分别设置。指令Cache的容量为8kB,采用2路组相联映像方式。数据Cache的容量也为8kB,但采用4路组相联映像方式。Pentium Pro采用MESI(修改、排他、共享、作废)协议来维持Cache和主存储器之间的一致性。通常,人们总以为,像Pentium Pro这样的3路超标量结构的微处理器会采用更大容量的片内第一级Cache和更大的第二级Cache。然而,Intel公司的设计者却选择了另一条设计思路——设计一种Cache存储阶层结构,使得能够从一个Cache流动到另一个Cache,而不用阻塞执行。
Pentium Pro采用了内嵌式或称捆绑式L2Cache,大小为256kB或512kB。此时的L2已经用线路直接连到CPU上,益处之一就是减少了对急剧增多L1 Cache的需求。L2 Cache还能与CPU同步运行。即当L1 Cache不命中时,立刻访问L2 Cache,不产生附加延迟。为进一步减少因要访问的信息不在高速缓冲中时所带来的性能损失,Pentium Pro的L1和L2都设计成非锁定型。即当哪个Cache中没有CPU所需的信息时,它不妨碍后面访问Cache的处理过程。Cache可以直接处理最多4次的Cache缺页情况,借助CPU的内存有序缓冲区可以顺序保存最多12次的内存访问。非锁定型Cache适用于Pentium Pro的乱序执行核心,因为在可能引发流水线延迟的长等待内存操作期间,这些Cache可以让CPU继续运行。
Pentium Pro的如此捆绑封装,带来器件成本提高。一方面专用的L2 cache芯片成本高,另一方面两个不同功能的芯片只有放在一起联结后才能最后测试其性能的完整性。而当其中有一个有缺陷时,两个芯片都被报废。在以后的Pentium Pro产品中,又将L2 Cache从芯片中去掉。Pentium MMX容量增大的L1和L2CachePentium MMX是能运行多媒体指令MMX的高能奔腾处理器。Pentium MMX具有改进的分支预测和增强型流水线技术,并将L1 Cache容量增加到32kB,L2 Cache为512kB。
Pentium MMX的片内L1数据和指令的Cache,每个增到16kB,4路相联。较大的独立内部Cache、减少平均内存存取时间,同时提供对近期所用指令和数据的快速存取,性能因此得到提高。数据Cache支持采用回写方式更新内存。
由于CacheL1容量的增大,使当时的应用程序运行速度提高了10%左右。PentiumⅡ设有双独立总线连接L2 CachePentiumⅡ是Pentium Pro的改进型,具有MMX指令,使用动态执行技术,采用双独立总线结构。PentiumⅡ同样有2级Cache,L1为32kB(指令和数据Cache各16kB)是Pentium Pro的一倍。L2为512kB。
Pentium Ⅱ与Pentium Pro在L2 Cache 的不同是由于制作成本原因。L2 Cache已不在内嵌芯片上,而是与CPU通过专用64位高速缓存总线相联,与其它元器件共同被组装在同一基板上,即“单边接触盒”上。双独立总线结构就是:L2高速缓存总线和处理器至主内存(Processor-to-main-memory)的系统总线。 PentiumⅡ处理器可以同时使用这两条总线,与单一总线结构的处理器相比,该处理器可以进出两倍多的数据,可允许 PentiumⅡ处理器的L2高速缓存比Pentium处理器的L2高速缓存要快1倍。随着 PentiumⅡ处理器主频的提高,L2高速缓存的速度也将加快。最后,流水线型系统总线可允许同时并行传输,而不是单个顺序型传输。改进型的双重独立总线结构,可以产生超过与单总线结构三倍带宽的性能。另外,在PentiumⅡ中,采用了ECC技术,此技术应用到二级高速缓存中,大大提高了数据的完整性和可靠性。
为开发低端市场,曾在 PentiumⅡ的基板上除去L2,牺牲一些性能,制造廉价CPU。这就是最初的Celeron处理器。以后的Celeron仍加有较小的片上L2 Cache,其大小为128kB。PentiumⅢ的L2 Cache增大PentiumⅢ也是基于Pentium Pro结构为核心,在原有MMX多媒体指令的基础上,又增了70多条多媒体指令。它使用动态执行技术,采用双独立总线结构。
PentiumⅢ具有32kB非锁定L1 Cache和512kB非锁定L2 Cache。L2可扩充到1~2MB,具有更合理的内存管理,可以有效地对大于L2缓存的数据块进行处理,使CPU、Cache和主存存取更趋合理,提高了系统整体性能。在执行视频回放和访问大型数据库时,高效率的高速缓存管理使PⅢ避免了对L2 Cache的不必要的存取。由于消除了缓冲失败,多媒体和其它对时间敏感的操作性能更高了。对于可缓存的内容,PⅢ通过预先读取期望的数据到高速缓存里来提高速度,这一特色提高了高速缓存的命中率,减少了存取时间。Merced设有L0即将推出的第7代处理器Merced主频可达1GHz。很明显,对Cache的要求更高了。为此,lntel本着“大力提高执行单元和缓存间数据交换速度”的思想,在芯片内开发新的Cache,并增加L1 Cache的容量,来平衡CPU和DRAM间的速度。
为此,在Merced的片上最接近执行单元旁再设另一处Cache,称为L0缓存,是指令/数据分离型。由于L0Cache在物理位置上比L1离执行单元更近,布线距离的缩短,使它与执行单元间的数据交换速度比L1还快,可以进一步提高工作主频。
同时,还要在芯片内部配置超过1MB的大容量L1 Cache。芯片内部Cache比外部Cache更易于提升与执行单元间的数据传送速度。内部Cache的加大,执行单元不易发生“等待”。现行的内部Cache容量仅为32kB~128kB。内部Cache容量的增加会引起芯片面积增大,提高制造成本。但大部分公司认为,由于内部Cache容量增大而导致成本的上扬,可以用制造技术来弥补。与Cache相配合的缓存控制指令为进一步发挥Cache的作用,改进内存性能并使之与CPU发展同步来维护系统平衡,一些制造CPU的厂家增加了控制缓存的指令。如Intel公司在PentiumⅢ处理器中新增加了70条3D及多媒体的SSE指令集。其中有很重要的一组指令是缓存控制指令。AMD公司在K6-2和K6-3中的3DNow!多媒体指令中,也有从L1数据Cache中预取最新数据的数据预取指令(Prefetch)。
PentiumⅢ处理器的缓存控制指令,用于优化内存连续数据流。针对数据流的应用需要对以前的Cache运作方式进行了改进,减少了一些不必要的中间环节,节省了时间,增加了CPU数据总线的实际可用带宽,也提高了Cache的效率。
有两类缓存控制指令。一类是数据据预存取(Prefetch)指令,能够增加从主存到缓存的数据流;另一类是内存流优化处理(Memory Streaming)指令,能够增加从处理器到主存的数据流。这两类指令都赋予了应用开发人员对缓存内容更大控制能力,使他们能够控制缓存操作以满足其应用的需求。
数据预存取指令允许应用识别出所需的信息,并预先将其从主存中取出存入缓存。这样一来,处理器可以更快地获取信息,从而改进应用性能。为了进一步削减内存延迟,内存访问还可以与计算机周期保持流水操作。例如,如果一个应用需要计算一些数值以供3D图形使用,当它在计算一个值的同时就可以预取下一个需要计算的数值。
内存流优化处理指令允许应用越过缓存直接访问主存。通常情况下,处理器写出的数据都将暂时存储在缓存中以备处理器稍后使用。如果处理器不再使用它,数据最终将被移至主存。然而,对于多媒体应用来就,通常不再需要使用这些数据。因此,这时将数据尽快地移到主存中则显得至关重要。采用了PentiumⅢ处理器的内存流优化处理指令后,应用程序就能让数据搭乘“直达快车”,直接到达主存。当数据流直接到达主存时,处理器负责维护缓存的一致性。因为这种方式避免了为数据流留出空间清空缓存的当前内容,从而也提高了缓存的利用率。
总而言之,缓存控制指令改进了进出处理器的数据据流,使处理器保持其高速率运作。通过这些指令(同时还需要一些专为其设计以使其发挥优势的软件),商业用户可以在操作系统和图形设备驱动程序中感受其性能优势。Cache在PC中其它设备的应用Cache作为一种速度匹配技术,不仅用在提高CPU对内存的读写速度上,而且也用在CPU结构的其它部分和PC系统中。
PC的显示系统中,由于3D应用的迅猛发展,大量的显示内存使用着高速缓存技术,如前台缓存、后台缓存、深度缓存和纹理缓存等。
PC的磁盘系统中,为提高内存对磁盘(主要是硬盘)的读写速度,就要建立磁盘高速缓存。因为DRAM内存的存取速度对CPU来说较慢,但对磁盘的存取速度却是很快的。这是因为磁盘存储系统包含有磁头的机械运动,而机械运动无法跟传送电信号的电子速度相比。此外,磁头中电与磁的信号转换也对速度有影响。这样,为了提高磁盘存取速度而采用Cache也就顺理成章了。硬盘Cache无需使用高速的SRAM,它只需在内存(DRAM)中划出一个区域,作为专用的磁盘缓冲区,采用一定的数据结构,即可实现磁盘存取的Cache技术。它的过程也是把即将访问的数据整块地拷贝到高速缓存区中,然后内存再到高速缓存中去逐个读取数据。由于数据在RAM空间内部传送要比在RAM与磁盘间传送快得多,系统由此提高了存取速度。
硬盘的Cache可以放在常规内存中。不过,为了不占用宝贵的用户程序空间,通常是把它设在扩展内存或扩充内存里。硬盘Cache是由人们共知的SMARTDRIVE.EXE文件自动建立的,用户只需在AUTOEXEC.BAT与CONFIG.SYS中加入相应的命令行就成了。
在较慢速的其它外围设备和内存的数据交换中,在网络通讯中,都需要使用Cache技术。推而广之,凡是在传输速度有较大差异的设备之间,都可以利用Cache的速度匹配技术。结束语PC中的Cache主要是为了解决高速CPU和低速DRAM内存间速度匹配的问题,是提高系统性能,降低系统成本而采用的一项技术。随着CPU和PC的发展,20年来,现在的Cache已成为CPU和PC不可缺少的组成部分,是广大用户衡量系统性能优劣的一项重要指标。据预测,在21世纪初期,CPU主频加快发展的趋势,加上内存DRAM的存取时间也会提高,从系统的性价比考虑,Cache的配备仍然是重要的技术之一。
⑤ EMSI协议
翻译至 https://en.wikipedia.org/wiki/MESI_protocol
EMSI是基于缓存无效化的一致性缓存协议,并且是一种最常见的支持回写式缓存的协议。它也被称为伊利诺伊州协议(由于其在伊利诺伊大学厄本那香槟分校被开发)。回写式缓存和写入式缓存相比可以节约很多的带宽。回写式缓存经常会存在脏状态,而脏状态表明了高数缓存中的数据与主存中的数据不一致。EMSI要求当缓存未命中时并且别的缓存中有该数据,那么缓存和缓存间应该互相传输数据。MESI相对与MSI来说降低了与主存的交互次数,这带来了显着的性能提升。
MESI的四个字母分别代表了四个可以被标记在缓存行上的独立状态。(也就是用2bit来编码)
当缓存行处于Modified状态时,它表明该缓存行只存在于当前缓存中。并且这个缓存行中的数据是脏数据,也就是说这个缓存行中的数据与主存中的数据不一致。缓存被要求在未来将缓存行的数据写于主存中,但不用立即写入。但如果别的缓存向该缓存请求这个数据,那必须保证该数据写入主存已经完成。当回写回主存完成后,缓存行状态会有Modified变为Shared状态。
当缓存行处于Exclusive状态时,它表明该缓存行只存在于当前缓存中,不过其中的数据与主存中的数据是一致的。当别的缓存向该缓存请求read当前缓存行时,缓存行状态会变成Shared。或者当有write操作时,他会变成Modified状态。
当缓存行处于Shared状态时,它表明该缓存行可能同时存在与别的缓存中,并且其中的数据与主存中一致。这个缓存行随时可能被丢弃(改变为Invalid状态)。
当缓存行处于Invalid 状态时,表明该缓存行是无效的。
对于给定的两个缓存,以下是允许共同存在的状态:
当一个缓存中的变量被标记为M状态,那同样拥有这个变量的别的缓存的缓存行会被标记为Invalid。
从一个状态到另一个状态的转变有两个重要影响因素。第一个因素是处理器发出了特殊的读写请求。举个栗子:处理器A的缓存中有变量X,然后这个处理器向自己的缓存发送了对于这个变量的读写请求。第二个影响因素是来自别的处理器的请求,这些处理器缓存中没有这个变量,或者它们想要更新这个变量。这些总线请求被一个名叫Snoopers的监听器监听着。
下面解释不同种类的处理器请求和总线请求
处理器请求包括如下两种:
总线请求包括如下五种:
如果从主存中获取一个值需要等待的时间比通过缓存间传值等待的时间长,那么我们就可以说缓存间传值可以降低缓存未命中后的读延迟。在多核架构下,一致性主要在二级缓存间保持,但处理器仍然有三级缓存,从三级缓存中获取未命中的变量往往快于从二级缓存。
监控操作 :
所有缓存中的变量都拥有四种状态的有限状态机。
对于不同输入的状态转换关系和回复如下表1.1和1.2
只有写操作发生在Modified或Exclusive状态的缓存行时,才缓存才不需要做额外操作。如果状态为Shared的缓存行被写入,那么首先别的缓存需要无效它们的缓存行。这个由一个叫 Request For Ownership (RFO) .的广播操作执行。
当一个变量为Modified时,这个缓存必须监听所有别的缓存对于该变量对应主存的读请求,一旦监听到就必须将这个变量写入主存。这个过程可以通过强制让别的缓存等待的方法来完成。当完成了对主存的写入,状态变为Shared。但同样可以直接通知别的缓存这个变量的值,而不写入主存。
当处于Shared状态时,必须监听所有的rfo广播,一旦收到就让缓存中的变量无效。
Modified和Exclusive状态是精确的,当有缓存行处于这个状态就表明,这个变量是这个缓存独有的。而Shared状态时不精确的,当别的缓存丢弃了这个Shared状态的缓存行,那么可能只存在一个缓存行状态为Shared但它不会变为Exclusive。丢弃Shared状态行不会引起别的缓存的注意。
使用Exclusive可以带来性能上的提升,因为修改Exclusive不需要通知别的缓存,而修改Shared状态的缓存行需要告诉别的缓存,并使这些缓存行无效,这是耗时的。(这也是EMSI与MSI协议的区别)
MESI简单直接的实现存在着两个性能问题。1.当向一个Invalid的缓存行写入时,需要从别的缓存获取值,这是很耗时的。2.需要将别的缓存行的该值设置为Invalid这也是耗时的。为了解决这两个问题,我们引入了存储缓冲区和无效化队列。
当向一个无效的缓存行写入时会用到储存缓冲区。因为这个写操作最终一定会被执行,因此CPU发送读无效消息(让别的缓存中的该缓存行无效)并且将要写入的值放到存储缓存区中,当这个需要的缓存行写入缓存时,存储缓存区中存储的值将被执行写入。
存储缓存区存在带来的后果是,当一个CPU进行写操作,值不会立即写入缓存中。因此,无论何时CPU读取缓存中的值时,都需要先扫描自己的存储缓冲区,确保缓冲区中是否还有未写入的值。值得注意的时不同CPU之间存储缓冲区时不可见的。
当CPU收到无效请求时,它会将这写无效请求放入无效化队列中。放入队列中的请求会被迅速执行但不是立即执行。所以CPU可以忽略它的缓存块是无效的。CPU不能扫描它的无效队列,这是和存储缓冲区的区别。
可见存储缓冲区带来的是写不同步,而无效化队列则带来读不同步,为了解决这些不同步,我们需要内存屏障。写屏障会刷新我们的存储缓冲区,确保里面所有的写都会被执行到这个CPU的cache上,而读屏障可以保证所有无效化队列里的任务都被执行,确保别的CPU的写对自己可见。
⑥ CPU多级缓存架构
1、基本概念
1.1、总线
前端总线(FSB)就是负责将CPU连接到内存的一座桥,前端总线频率则直接影响CPU与内存数据交换速度,如果FSB频率越高,说明这座桥越宽,可以同时通过的车辆越多,这样CPU处理的速度就更快。目前PC机上CPU前端总线频率有533MHz、800MHz、1066MHz、1333MHz、1600MHz等几种,前端总线频率越高,CPU与内存之间的数据传输量越大。
前端总线——Front Side Bus(FSB),是将CPU连接到北桥芯片的总线。选购主板和CPU时,要注意两者搭配问题,一般来说,前端总线是由CPU决定的,如果主板不支持CPU所需要的前端总线,系统就无法工作。
1.2、频率与降频
只支持1333内存频率的cpu和主板配1600内存条就会降频。核心数跟ddr2和ddr3没关系,核心数是cpu本身的性质,cpu是四核的就是四核的,是双核的就是双核的。如果cpu只支持1333,而主板支持1600,那也会降频;cpu支持1600而主板只支持1333那不仅内存会降频,而且发挥不出cpu全部性能。
另外如果是较新的主板cpu,已经采用新的qpi总线,而不是以前的fsb总线。以前的fsb总线一般是总线为多少就支持多高的内存频率。而qpi总线的cpu集成了内存控制器,5.0gt/s的cpu可能只支持1333内存频率,但是总线带宽相当于1333内存的内存带宽的两倍,这时候,组成1333双通道,内存速度就会翻倍,相当于2666的内存频率。
1.3、cache line
Cache Line可以简单的理解为CPU Cache中的最小缓存单位。目前主流的CPU Cache的Cache Line大小都是64Bytes。假设我们有一个512字节的一级缓存,那么按照64B的缓存单位大小来算,这个一级缓存所能存放的缓存个数就是512/64 = 8个。
2、CPU多级缓存架构
级别越小的缓存,越接近CPU, 意味着速度越快且容量越少。
3、多核CPU多级缓存一致性协议MESI
为了解决这个问题,芯片设计者制定了一个规则。当一个 CPU 修改高速缓存行中的字节时,计算机中的其它 CPU 会被通知,它们的高速缓存将视为无效。于是,在上面的情况下, CPU2 发现自己的高速缓存中数据已无效, CPU1 将立即把自己的数据写回 RAM ,然后 CPU2 重新读取该数据。 可以看出,高速缓存行在多处理器上会导致一些不利。
多核CPU的情况下有多个一级缓存,如何保证缓存内部数据的一致,不让系统数据混乱。这里就引出了一个一致性的协议MESI。
MESI 是指4中状态的首字母。每个Cache line有4个状态,可用2个bit表示,它们分别是:
注意: 对于M和E状态而言总是精确的,他们在和该缓存行的真正状态是一致的,而S状态可能是非一致的。如果一个缓存将处于S状态的缓存行作废了,而另一个缓存实际上可能已经独享了该缓存行,但是该缓存却不会将该缓存行升迁为E状态,这是因为其它缓存不会广播他们作废掉该缓存行的通知,同样由于缓存并没有保存该缓存行的的数量,因此(即使有这种通知)也没有办法确定自己是否已经独享了该缓存行。
从上面的意义看来E状态是一种投机性的优化:如果一个CPU想修改一个处于S状态的缓存行,总线事务需要将所有该缓存行的变成invalid状态,而修改E状态的缓存不需要使用总线事务。
3.2、MESI状态转换
1.触发事件
触发事件描述本地读取(Local read)本地cache读取本地cache数据本地写入(Local write)本地cache写入本地cache数据远端读取(Remote read)其他cache读取本地cache数据远端写入(Remote write)其他cache写入本地cache数据
2.cache分类:
前提:所有的cache共同缓存了主内存中的某一条数据。
本地cache:指当前cpu的cache。
触发cache:触发读写事件的cache。
其他cache:指既除了以上两种之外的cache。
注意:本地的事件触发 本地cache和触发cache为相同。
下图示意了,当一个cache line的调整的状态的时候,另外一个cache line 需要调整的状态。
3.3、多核缓存协同操作
假设有三个CPU A、B、C,对应三个缓存分别是cache a、b、 c。在主内存中定义了x的引用值为0。
单核读取
那么执行流程是:
CPU A发出了一条指令,从主内存中读取x。
从主内存通过bus读取到缓存中(远端读取Remote read),这是该Cache line修改为E状态(独享).
双核读取
那么执行流程是:
CPU A发出了一条指令,从主内存中读取x。
CPU A从主内存通过bus读取到 cache a中并将该cache line 设置为E状态。
CPU B发出了一条指令,从主内存中读取x。
CPU B试图从主内存中读取x时,CPU A检测到了地址冲突。这时CPU A对相关数据做出响应。此时x 存储于cache a和cache b中,x在chche a和cache b中都被设置为S状态(共享)。
修改数据
那么执行流程是:
CPU A 计算完成后发指令需要修改x.
CPU A 将x设置为M状态(修改)并通知缓存了x的CPU B, CPU B将本地cache b中的x设置为I状态(无效)
CPU A 对x进行赋值。
同步数据
那么执行流程是:
CPU B 发出了要读取x的指令。
CPU B 通知CPU A,CPU A将修改后的数据同步到主内存时cache a 修改为E(独享)
CPU A同步CPU B的x,将cache a和同步后cache b中的x设置为S状态(共享)。
MESI优化和他们引入的问题
缓存的一致性消息传递是要时间的,这就使其切换时会产生延迟。当一个缓存被切换状态时其他缓存收到消息完成各自的切换并且发出回应消息这么一长串的时间中CPU都会等待所有缓存响应完成。可能出现的阻塞都会导致各种各样的性能问题和稳定性问题。
CPU切换状态阻塞解决-存储缓存(Store Bufferes)
比如你需要修改本地缓存中的一条信息,那么你必须将I(无效)状态通知到其他拥有该缓存数据的CPU缓存中,并且等待确认。等待确认的过程会阻塞处理器,这会降低处理器的性能。应为这个等待远远比一个指令的执行时间长的多。
Store Bufferes
为了避免这种CPU运算能力的浪费,Store Bufferes被引入使用。处理器把它想要写入到主存的值写到缓存,然后继续去处理其他事情。当所有失效确认(Invalidate Acknowledge)都接收到时,数据才会最终被提交。 这么做有两个风险
Store Bufferes的风险 第一、就是处理器会尝试从存储缓存(Store buffer)中读取值,但它还没有进行提交。这个的解决方案称为Store Forwarding,它使得加载的时候,如果存储缓存中存在,则进行返回。 第二、保存什么时候会完成,这个并没有任何保证。
试想一下开始执行时,CPU A保存着finished在E(独享)状态,而value并没有保存在它的缓存中。(例如,Invalid)。在这种情况下,value会比finished更迟地抛弃存储缓存。完全有可能CPU B读取finished的值为true,而value的值不等于10。
即isFinsh的赋值在value赋值之前。
这种在可识别的行为中发生的变化称为重排序(reordings)。注意,这不意味着你的指令的位置被恶意(或者好意)地更改。
它只是意味着其他的CPU会读到跟程序中写入的顺序不一样的结果。
3.4、硬件内存模型
执行失效也不是一个简单的操作,它需要处理器去处理。另外,存储缓存(Store Buffers)并不是无穷大的,所以处理器有时需要等待失效确认的返回。这两个操作都会使得性能大幅降低。为了应付这种情况,引入了失效队列。它们的约定如下:
原文连接
⑦ 救救我,感激不尽,若能挺过这一关,做牛做马都愿意
熔点在Reichart Thermover热阶段放大获得的。使用Pye Unicam SP- 光谱仪,用溴化钾光盘记录红外光谱。C和H的元素则使用Heraeus CHN - O型快速仪分别完成。质谱记录采用Finnigan Matt 型质谱仪,在电离势为70e下操作。'H和13C核磁共振谱在.94和75.43兆赫分别录得,使用arian 兆赫仪器以CDC13作为溶剂和MeSi(伊利诺斯协定?)作为内部存储标准。31P 核磁共振谱是使用相同的仪器以CDCl31和加入外部磷酸(D20的85%)在21.42兆赫下测定的。工作中使用的试剂和溶剂是由Fluka(布克斯,瑞士)获得的。
属于专业解释,你要自己理解部分:
第一句,好像句子不全或者Microscope有误。 Reichart Thermover是德文或者俄文标志的生产厂的加热仪器,但是也有一种德国产的化学品中出现。
保留所有外文仪器名称,因为是非英文,德文?
Me$i解释为Illinois Protocol缓存和内存一致性的协议,
来世?这些就过时了,没人再为它烦恼了。
⑧ 缓存一致性协议
锁缓存行有一套协议叫做 缓存一致性协议 。缓存一致性协议有MSI、MESI、MOSI、Synapse、Firefly以及DragonProtocol等等。
MESI分别代表缓存行数据的4中状态,通过对这四种状态的切换,来达到对缓存数据进行管理的目的
假设有三个CPU-A、B、C,对应三个缓存分别是cache-a、b、c。在主内存中定义了x的引用值0
单核读取
MESI优化和引入的问题:各CPU缓存行的状态是通过消息传递来进行的。如果CPU0要对一个在缓存中共享的变量进行写入,首先需要发送一个失效的消息给到其他缓存了该数据的CPU,并且要等到他们的确认回执。CPU0在这段时间内都会一直处于阻塞状态,会导致各种各样的性能问题和稳定性问题。
为了避免阻塞带来的资源浪费,在CPU中引入了Store Buffer。
CPU在写入共享数据时,直接把数据写入到Store Buffer中,同时发送Invalidate消息,然后继续去处理其他指令。当收到其他所有CPU发送了Invalidate Acknowledge消息时,再将Store Buffer中的数据存储到Cache Line中,最后再从Cache Line同步到主内存。