转自:http://www.zrway.com/lab/lab_show.jsp?RecNo=2701
The Core Execution Engine: Load/Store Unit
处理器核心执行引擎:存取单元
运算需要用到数据,也会生成数据,这些数据存取操作就是存取单元所做的事情,实际上,Nehalem和Core的存取单元没什么变化,仍然是3个。
Nehalem的Load/Store结构和Core架构一样
这三个存取单元中,一个用于所有的Load操作(地址和数据),一个用于Store地址,一个用于Store数据,前两个数据相关的单元带有AGU(Address Generation Unit,地址生成单元)功能(NetBurst架构使用快速ALU来进行地址生成)。
The Core Execution Engine: Load/Store Unit
在乱序架构中,存取操作也可以打乱进行。类似于指令预取一样,Load/Store操作也可以提前进行以降低延迟的影响,提高性能。然而,由于Store操作会修改数据影响后继的Load操作,而指令却不会有这种问题(寄存器依赖性问题通过ROB解决),因此数据的乱序操作更为复杂。
![]() |
数据乱序操作的困境:Load/Store依赖性 |
如上图所示,第一条ALU指令的运算结果要Store在地址Y(第二条指令),而第九条指令是从地址Y Load数据,显然在第二条指令执行完毕之前,无法移动第九条指令,否则将会产生错误的结果。同样,如果CPU也不知道第五条指令会使用什么地址,所以它也无法确定是否可以把第九条指令移动到第五条指令附近。
内存数据相依性预测功能(Memory Disambiguation)
内存数据相依性预测功能(Memory Disambiguation)可以预测哪些指令是具有依赖性的或者使用相关的地址(地址混淆,Alias),从而决定哪些Load/Store指令是可以提前的,哪些是不可以提前的。可以提前的指令在其后继指令需要数据之前就开始执行、读取数据到ROB当中,这样后继指令就可以直接从中使用数据,从而避免访问了无法提前Load/Store时访问L1缓存带来的延迟(3~4个时钟周期)。
不过,为了要判断一个Load指令所操作的地址没有问题,缓存系统需要检查处于in-flight状态(处理器流水线中所有未执行的指令)的Store操作,这是一个颇耗费资源的过程。在NetBurst微架构中,通过把一条Store指令分解为两个uops??一个用于计算地址、一个用于真正的存储数据,这种方式可以提前预知Store指令所操作的地址,初步的解决了数据相依性问题。在NetBurst微架构中,Load/Store乱序操作的算法遵循以下几条原则:
- 如果一个对于未知地址进行操作的Store指令处于in-flight状态,那么所有的Load指令都要被延迟
- 在操作相同地址的Store指令之前Load指令不能继续执行
- 一个Store指令不能移动到另外一个Store指令之前
这种原则的问题也很明显,比如第一条原则会在一条处于等待状态的Store指令所操作的地址未确定之前,就延迟所有的Load操作,显然过于保守了。实际上,地址冲突问题是极少发生的。根据某些机构的研究,在一个Alpha EV6处理器中最多可以允许512条指令处于in-flight状态,但是其中的97%以上的Load和Store指令都不会存在地址冲突问题。
基于这种理念,Core微架构采用了大胆的做法,它令Load指令总是提前进行,除非新加入的动态混淆预测器(Dynamic Alias Predictor)预测到了该Load指令不能被移动到Store指令附近。这个预测是根据历史行为来进行的,据说准确率超过90%。
在执行了预Load之后,一个冲突监测器会扫描MOB的Store队列,检查该是否有Store操作与该Load冲突。在很不幸的情况下(1%~2%),发现了冲突,那么该Load操作作废、流水线清除并重新进行Load操作。这样大约会损失20个时钟周期的时间,然而从整体上看,Core微架构的激进Load/Store乱序策略确实很有效地提升了性能,因为Load操作占据了通常程序的1/3左右,并且Load操作可能会导致巨大的延迟(在命中的情况下,Core的L1D Cache延迟为3个时钟周期,Nehalem则为4个。L1未命中时则会访问L2缓存,一般为10~12个时钟周期。访问L3通常需要30~40个时钟周期,访问主内存则可以达到最多约100个时钟周期)。Store操作并不重要,什么时候写入到L1乃至主内存并不会影响到执行性能。
![]() |
图9:数据相依性预测机制的优势 |
如上图所示,我们需要载入地址X的数据,加1之后保存结果;载入地址Y的数据,加1之后保存结果;载入地址Z的数据,加1之后保存结果。如果根据Netburst的基本准则,在第三条指令未决定要存储在什么地址之前,处理器是不能移动第四条指令和第七条指令的。实际上,它们之间并没有依赖性。因此,Core微架构中则“大胆”的将第四条指令和第七条指令分别移动到第二和第三指令的并行位置,这种行为是基于一定的猜测的基础上的“投机”行为,如果猜测的对的话(几率在90%以上),完成所有的运算只要5个周期,相比之前的9个周期几乎快了一倍。
The Core Execution Engine: Load/Store Unit
处理器核心执行引擎:存取单元
和为了顺序提交到寄存器而需要ROB重排序缓冲区的存在一样,在乱序架构中,多个打乱了顺序的Load操作和Store操作也需要按顺序提交到内存,MOB(Memory Reorder Buffer,内存重排序缓冲区)就是起到这样一个作用的重排序缓冲区(上图,介于Load/Store单元与L1D Cache之间的部件),所有的Load/Store操作都需要经过MOB,MOB通过一个128bit位宽的Load通道与一个128bit位宽的Store通道与双口L1D Cache通信。和ROB一样,MOB的内容按照Load/Store指令分发(Dispatched)的顺序加入队列的一端,按照提交到L1D Cache的顺序从队列的另一端移除。ROB和MOB一起实际上形成了一个分布式的Order Buffer结构,有些处理器上只存在ROB,兼备了MOB的功能。
和ROB一样,Load/Store单元的乱序存取操作会在MOB中按照原始程序顺序排列,以提供正确的数据,内存数据依赖性检测功能也在里面实现。Intel没有给出MOB详细的结构??包括外部拓扑结构在内,在一些玩家制作的架构图当中,MOB被放在Load/Store单元与Internal Results Bus之间并互相联结起来,意思是MOB的Load/Store操作结果也会直接反映到ROB当中。
The Core Execution Engine: Load/Store Unit
然而基于以下的一个事实,笔者将其与Internal Results Bus进行了隔离:MOB还附带了数据预取(Data Prefetch)功能,它会猜测未来指令会使用到的数据,并预先从L1D Cache缓存Load入MOB中(Data Prefetcher也会对L2至系统内存的数据进行这样的操作),这样MOB当中的数据有些在ROB中是不存在的(这有些像ROB当中的Speculative Execution猜测执行,MOB当中也存在着“Speculative Load Execution猜测载入”,只不过失败的猜测执行会导致管线停顿,而失败的猜测载入仅仅会影响到性能)。此外,MOB与L1D之间是数据总线,不带有指令,经过MOB内部的乱序执行之后,ROB并不知道进出的数据对应哪一条指令。最终笔者制作的架构图就如上方所示。
每个Core 2内核具有3个Prefetcher(1个指令,两个数据);每两个核心共享两个L2 Prefetcher
Nehalem的Hardware Prefetcher功能,其中L1 Prefetchers基于指令历史以及载入地址参数
数据预取功能和指令预取功能一起,统称为Hardware Prefetcher硬件预取器。笔者在年少时对BIOS里面通常放在CPU特性那一页里面的Hardware Prefetcher迷惑不解(通常在一起的还有一个Adjacent Cache Line Prefetch相邻缓存行预取,据说这些选项不包含L1 Prefetcher;亦尚不清楚是否包括MOB的预取功能),现在我们知道了这两个功能就是和这一页的内容相关的。很不幸,在以往的CPU中,失败的预取将会白白浪费掉L1/L2/L3/Memory的带宽,而在服务器应用上通常会进行跨度很大的Load操作,因此Hardware Prefetcher经常会起到降低性能的作用。对于这种情况,处理器厂商们除了在BIOS里面给出一个设置选项就没有更好的方法了(这些选项在桌面应用上工作良好)。糟糕的是,很多用户都不知道这些选项是干什么用的。据说Nehalem上这个情况得到了好转,用户可以简单地设置为Enable而不用担心性能下降。或许以后我们IT168评测中心会进行相关的测试检验是否是这样,不过我们可以想象,内存带宽得到巨大提升的Nehalem已经具有足够的资本来开启这些选项了。
提高并行度:扩大RS和MOB的容量(MOB包括了Load Buffers和Store Buffers),所谓的乱序窗口资源
乱序执行中我们可以看到很多缓冲区性质的东西:RAT寄存器别名表、ROB重排序缓冲区、RS中继站、MOB内存重排序缓冲区(包括LB载入缓冲和SB存储缓冲)。在超线程的作用下,RAT是一式两份,包含了128个重命名寄存器;128条目的ROB、48条目的LB和32条目的SB都静态划分为两个分区:每个线程64个ROB、24个LB和16个SB;RS则是在两个线程中动态共享。可见,虽然整体数量增加了,然而就单个线程而言,获得的资源并没有提升。这会影响到HTT下单线程下的性能。
The Memory sub-System: Cache
内存子系统:缓存
MOB通过两条128位宽的Load/Store通道与L1D Cache连接,L1D Cache同时通过256位宽的总线与L2连接:L1D Cache是双口(Dual Ported)的。在缓存方面,Nehalem和Core相比具有了一些变化。
绿色部分都属于缓存相关部分
Nehalem/Core的L1I Cache(L1指令缓存)和L1D Cache(L1数据缓存)都是32KB,不过Nehalem的L1I Cache从以往的8路集合关联降低到了4路集合关联,L1 DTLB也从以往的256条目降低到64条目(64个小页面TLB,32个大页面TLB),并且L1 DTLB是在两个多线程之间动态共享的(L1 ITLB的小页面部分则是静态分区,也就是64条目每线程,是Core 2每线程128条目的一半;每个线程还具有7个大页面L1I TLB)。
Nehalem TLB架构
为什么L1I Cache的集合关联降低了呢?这都是为了降低延迟的缘故。随着现代应用程序对数据容量的要求在加大,需要提升TLB的大小来相应满足(TLB:Translation Lookaside Buffer,旁路转换缓冲,或称为页表缓冲;里面存放的是虚拟地址到物理地址的转换表,供处理器以及具备分页机构的操作系统用来快速定位内存页面;大概很多人知道TLB是因为AMD的处理器TLB Bug事件)。Nehalem采用了较小的L1 TLB附加一层较大的L2 TLB的方法来解决这个问题(512个条目以覆盖足够大的内存区域,它仅用于较小的页面,指令和数据共用,两个线程共享)。
为了降低能耗,Nehalem架构将以往应用的Domino线路更换为Static CMOS线路,并大规模使用了长沟道晶体管技术,速度有所降低,但是能源效率提升了
虽然如此,Nehalem L1D Cache的延迟仍然从Core 2的3个时钟周期上升到了4个时钟周期,这是由于线路架构改变的缘故(从Domino更换成Static CMOS,大量使用长沟到晶体管)。类似地L1I Cache乃至L2、L3的延迟都相应地会上升,然而指令缓冲的延迟对性能的影响要比数据严重;每一次取指令都会受到延迟影响,而缓存的延迟则可以通过乱序执行和猜测载入来解决。因此Intel将L1I Cache的集合关联从8路降低到4路,以维持延迟仍然在3个时钟周期。
Nehalem-EP Xeon X5570的缓存结构:64KB L1,256KBL2,8MB共享L3
The Memory sub-System: Cache
内存子系统:缓存
与Core 2相比,Nehalem新增加了一层L3缓存,这是为了多个核心共享数据的需要(Nehalem-EX具有8个核心),也因此这个L3的容量很大。出于消除多核心共享数据的压力,前面的缓存不能让太多的缓存请求到达L3,而且L3的延迟(约30~40个时钟周期)和L1的延迟(3~4个时钟周期)相差太大,因此L2是很有必要的。Nehalem简单地在很小的L1和大尺寸的L3之间插入256KB的L2来起到中继的作用??中继具有两个方面的含义:容量和延迟。256KB不算大,可以维持约低于10个时钟周期的延迟。Nehalem的L2和L1不是包含也不是非包含的关系。
![]() |
通常缓存具有两种设计:非独占和独占,Nehalem处理器的L3采用了非独占高速缓存设计(或者说“包含式”,L3包含了L1/L2的内容),这种方式在Cache Miss的时候比独占式具有更好的性能,而在缓存命中的时候需要检查不同的核心的缓存一致性。Nehalem并采用了“内核有效”数据位的额外设计,降低了这种检查带来的性能影响。随着核心数目的逐渐增多(多线程的加入也会增加Cache Miss率),对缓存的压力也会继续增大,因此这种方式会比较符合未来的趋势。在后面可以看到,这种设计也是考虑到了多处理器协作的情况(此时Miss率会很容易地增加)。这可以看作是Nehalem与以往架构的基础不同:之前的架构都是来源于移动处理设计,而Nehalem则同时为企业、桌面和移动考虑而设计。
在L3缓存命中的时候(单处理器上是最通常的情况,多处理器下则不然),处理器检查内核有效位看看是否其他内核也有请求的缓存页面内容,决定是否需要对内核进行侦听:
笔者相信这一点是不对的:假如一个L3页面被多个内核共享(多于一个有效被设置为1),那么这个处理器的该页面就不能进入Modified状态
基于后面的NUMA章节的内容,多个处理器中的同一个缓存页面必定在其中一个处理器中属于F状态(可以修改的状态),这个页面在这个处理器中没有理由不可以多核心共享(可以多核心共享就意味着这个能进入修改状态的页面的多个有效位被设置为一)。笔者相信MESIF协议应该是工作在核心(L1+L2)层面而不是处理器(L3)层面,这样统一处理器里多个核心共享的页面,只有其中一个是出于F状态(可以修改的状态)。见后面对NUMA和MESIF的解析。
在L3缓存未命中的时候(多处理器下会频繁发生),处理器决定进行内存存取,按照页面的物理位置,它分为近端内存存取(本地内存空间)和远端内存存取(地址在其他处理器的内存的空间):
![]() |
Cache Miss时而页面地址为本地的时候,处理器进行近端内存访问
延迟取本地内存访问和远程CPU Cache Hit的延迟的最大值
![]() |
Cache Miss时而页面地址为远程的时候,处理器进行远端内存访问
延迟取远程内存访问和远程CPU Cache Hit的延迟的最大值
![]() |
近端访问约60个时钟周期,远端访问约90个时钟周期(据说仍然比Harptertown Xeon快),本地L3 Cache Hit则为30个时钟周期
The Uncore: IMC
核外系统:集成内存控制器
从形式上来看,L3缓存、集成的内存控制器乃至QPI总线都属于Uncore核外部分,从L2、L1一直到执行单元都属于Core核内部分。由于Nehalem首次采用了这种核心内外的相对独立设计思路,因此核心之外的设计相对于Core架构来说显得新颖许多,这就是Nehalem的模块式设计。
![]() |
模块式设计可以提供灵活的产品给用户,现在4核心、三通IMC和单QPI的桌面Nehalem已经面市,预计明年3月将会出现4核心、4通道IMC和双QPI的企业级Nehalem产品。包含了PCIE控制器乃至集成显卡的产品也已经在路线上了。
继续回到处理器架构:我们都知道,Nehalem和Intel以往处理器相比最大的特点就是直联架构??包括两个方面:处理器直联以及内存直联,前者就是依靠QPI总线的实现,后者则是由于处理器内置了内存控制器(IMC,Integrated Memory Controller)。当处理器在L3 Cache未找到所要内容(L3 Cache Miss)的时候,它将会继续通过IMC集成内存控制器往系统内存索取,同时通过QPI总线询问其他处理器(如果是多处理器平台)。
为什么直联架构可以很明显地提升性能?这要先从x86架构的存储体系说起。在很久很久以前,在一个记忆体短缺的时代??不仅仅处理器外面记忆体很少,处理器里面也是。使用了CISC架构的x86处理器里面只有8个GPR通用寄存器(一般的RISC处理器有32个以上的通用寄存器,现在的x86-64有16个通用寄存器),由于通用寄存器数量上的短缺,因此不像RISC处理器那样,CISC的x86处理器使用了堆叠运算指令。堆叠运算也就是将运算结果保存在源寄存器上的,如ADD AX, BX指令会将AX寄存器与BX寄存器的内容相加,并将结果保存到AX上??这样对比于使用三个寄存器做同一运算的非堆叠指令RISC架构就节约了一个寄存器,然而相应地源寄存器的内存就销毁了。x86架构需要执行大量的Load/Store微指令(Pentium Pro开始具备)来进行寄存器-寄存器或寄存器-内存之间的数据搬运操作。RISC处理器当中,Load/Store操作也很频繁。
如前面所述,最常用的20条x86指令当中:
mov占35%(寄存器之间、寄存器与内存之间移动数据),push占10%(压入堆栈,也经常用来传递参数),call占6%,cmp占5%,add、pop、lea占4%(实际计算指令非常少)
mov、push、pop都是和load/store直接相关的,add、cmp等则间接相关
顺便:
75%的x86指令短于4 bytes,也就是小于32 bits。不过这些短指令只占代码大小的53%??有一些指令非常长
单操作数指令占37%,双操作数指令占60%
双操作数指令中,直接数操作20%,寄存器操作数56%,绝对寻址操作数1%,间接寻址操作数23%
Load操作占据了x86 uops当中的约30%
大量的Load/Store操作已经通过ROB/MOB降低到一定程度,不过,在多核心/超线程的情况下,对缓存/内存子系统仍然具有很大的压力
现在来看这样的设计简直是无法想象,不过这样脑残的设计不仅仅用到了今天,而且还加速到了一个不可思议的境界……在与各种RISC架构处理器的交锋也不落下风……回到架构上,由于x86架构实际上是通过耗费寄存器带宽及缓存-内存带宽来节约处理器内部寄存器数量,大量的Load/Store操作(Load操作占据了x86 uops当中的约30%),对缓存乃至内存的性能非常依赖。
Nehalem具有三个Load/Store单元以及一个MOB架构,并支持内存数据相依性预测功能,缓存性能非常出色
缘此,x86架构在缓存-内存上的提升是不遗余力,不提2008年度评测报告:深入Nehalem微架构中说到的内存数据相依性预测功能(Memory Disambiguation),对于Nehalem而言,这方面最大的改进就是直联架构带来的IMC集成内存控制器,它使CPU到内存的路径更短,大幅度降低了内存的延迟,同时每一个CPU都具有自己专有的内存带宽。这一点在数据库应用中表现非常显著,数据库应用对存储器的延迟很敏感。
The Uncore: QPI
核外系统:QPI
直联架构不仅仅意味着处理器与内存直接相连,还让处理器之间也直接联系起来。Hyper-Transport总线的使用让Operton进入了高性能计算市场,QPI所作的事情是一样的。通过QPI总线,处理器之间可以直接相连,不再需要经过拥挤、低带宽的FSB共享总线,多处理器系统运行效率大为提升。 对于多处理器系统而言,QPI提供的巨大带宽对性能提升很有作用。
QPI vs. FSB | ||
名称 | Intel FSB(Front Side Bus) | Intel QuickPath Interconnect(QPI) |
拓扑 | 共享总线 | 点对点连接 |
物理总线宽度(bits) | 64 | 20 x 2(双向) |
数据总线宽度(bits) | 64 | 16 x 2(双向) |
传输速率 | 333MHz 1.333GT/s 10.6GB/s | 3.2GHz 6.4GT/s 12.8GB/s(单向) 25.6GB/s(双向) |
需要边带信号 | 是 | 否 |
引脚数 | 150 | 84 |
时钟数 | 1 | 1 |
集成时钟 | 否 | 否 |
总线传输方向 | 双向 | 单向 |
使用高频率DDR3内存,访问本地内存的延迟大约为60个时钟周期,而通过QPI总线访问远端的处理器并返回数据大约需要90个时钟周期(如上一页所述)。QPI的就是Core架构为了使用服务器市场而做出的进化,它可以建立一个庞大的可扩展的解决方案。
![]() |
除了提供更高的带宽(每链路25.6GB双向带宽)之外,QPI总线还让多处理器系统更有效率:处理器之间可以直接连接。如上图,每个CPU都可以直接和其他三个CPU通信;4路Barcelona Opteron在对角线上是无法直接通信的,需要邻近的CPU进行接力,这显然会降低效率;不过Shanghai Opteron已经可以做到:《全国首发 AMD Shanghai/上海性能评测》。
Nehalem-EX的时钟架构
所有的Nehalem都按时钟分为三个部分:核心、核外(L3和系统逻辑)和IO(QPI和IMC),这三个部分的频率通常互不相同。由于L3缓存属于核外部分,因此它的频率和核心频率通常是不同的,在以往,CPU内的高速缓存通常都是全速的,只有Pentium II的L2缓存是半速的(它和CPU内核不在同一个晶圆上,虽然在同一个CPU封装内),而K6之前的L2缓存都是放在主板上面的,速度极低。现在,Nehalem架构下,L3缓存的时钟频率也不再是全速,而是要较低一些,例如,Core i7 920的L3频率应该是2.133GHz,Xeon X5570的L3频率应该是2.667GHz。
Nehalem-EP/Gainestown Xeon X5570处理器,主频2.93GHz,QPI总线频率高达3.2GHz,比主频还要高
QPI总线频率一般和L3频率也不同,不过它们具有一些联系。对于桌面处理器来说,QPI总线只有一条,简单地连接处理器与IOH,然而对于服务器处理器来说,除了连接IOH之外,处理器与处理器之间也需要通过QPI总线,因此服务器的处理器都具有很高的QPI频率,有些时候甚至高于处理器主频率,如Xeon X5570处理器。
桌面Nehalem:Core i7 920的主频是2.67GHz,而QPI总线频率只有2.4GHz
一些主板允许单独设置这些不同的频率以方便超频。在这里,笔者可以回答很多用户关心的UCLK频率(一些主板上具有的Uncore Clock设置选项)的问题:L3缓存频率和IMC集成内存控制器的频率是不同的,也就是UCLK和内存频率是不同的,不过它们具有一些内在关系。此外,由于UCLK关系的Uncore部分关系到了整个处理器的中枢部分:系统逻辑(包括中央路由器和集线器),因此它的频率设定可以很大地影响到整个处理器的运行效能。
Architecture: ccNUMA Architecture
架构:缓存一致的非一致性内存访问架构
由于IMC和QPI的存在,从形式上来看,多路Nehalem处理器系统将会组成一个NUMA(Non-Uniform Memory Access或者Non-Uniform Memory Architecture,非一致性内存访问或非一致性内存架构)系统,NUMA系统是多处理器系统的一种形式,以往的通过单条FSB连接多个Xeon处理器的系统叫做UMA(Uniform Memory Architecture,内存一致架构)系统??传统地,按照处理器架构来分的话属于SMP(Symmetric MultiProssecor,对称多处理器)系统。NUMA的特点是访问内存不同的区域具有着不一致的延迟,NUMA和UMA的共同点是所有内存是硬件共享的,操作系统只看到一片单一的内存区域,比起MPP大型并行处理系统在编程方面更为简便。
多个Nehalem处理器之间使用MESIF协议来保持缓存一致性
按照缓存页面同步的形式,NUMA可以分为两种:Cache Coherent缓存一致和Non-Cache Coherent缓存非一致性,由于编程上的艰难,因此后一种形式的实际产品几乎不存在,所以NUMA几乎就是ccNUMA(Cache Coherent NUMA)的代名词。多路Nehalem处理器就是一个典型的ccNUMA架构。ccNUMA的特点是多个处理器之间进行共享、传输的是缓存页面(缓存页面所对应的内存页面则固定地保留在某一个处理器连接的内存上)。
Nehalem通过MESIF协议来维护缓存页面的一致性(也就是Cache Coherent缓存一致的含义),而使用HT总线的AMD Opteron(多Opteron也组成一个ccNUMA架构)则使用的是MOESI协议,老的Xeon则使用MESI协议。MESIF的意思就是M(Modified)E(Exclusive)S(Shared)I(Invalid)F(Forward),MOESI则是M(Modified)O(Owner)E(Exclusive)S(Shared)I(Invalid),这些词分别代表了一个缓存页面的状态,Nehalem多了一个F状态,而Opteron则多了一个O状态。
Cache Coherent Protocol缓存同步协议
| |||||||
| 干净/脏 | 唯一 | 可写 | 转发 | 可安静地转化成的状态 | 说明 | |
MESIF over QPI/CSI(Intel Nehalem)
| |||||||
M(Modified) 修改 | Dirty 脏 | 是 | 是 | 是 | 被请求时需要先写入内存 并转化为F状态 | ||
E(Exclusive) 独占 | Clean 干净 | 是 | 是 | 是 | M、S、I、F | 被写入时转化为M状态 | |
S(Shared) 共享 | Clean 干净 | 否 | 否 | 否 | I | 主副本被写入时转为无效 | |
I(Invalid) 无效 | - | - | - | - | - | ||
F(Forward) 转发 | Clean 干净 | 是 | 否 | 是 | S、I | 主副本 被写入时转换为M状态 并使其他S副本无效 | |
MOESI over HTT(AMD Opteron)
| |||||||
M(Modified) 修改 | Dirty 脏 | 是 | 是 | 是 | O | 被请求时不需要写入内存 而仅仅转化为O状态 | |
O(Owner) 拥有者 | Dirty 脏 | 是 | 是 | 是 | 主副本 转换为其他状态需要先写入内存 | ||
E(Exclusive) 独占 | Clean 干净 | 是 | 是 | 是 | M、S、I | 被写入时转化为M状态 | |
S(Shared) 共享 | 干净或脏 | 否 | 否 | 否 | I | 可以同时为干净或者脏 主副本被写入时转为无效 | |
I(Invalid) 无效 | - | - | - | - | - | ||
MESI over FSB(Intel Xeon)
| |||||||
M(Modified) 修改 | Dirty 脏 | 是 | 是 | 是 | 被请求时需先写入内存 | ||
E(Exclusive) 独占 | Clean 干净 | 是 | 是 | 是 | M、S、I | 被写入时转化为M状态 | |
S(Shared) 共享 | Clean 干净 | 否 | 否 | 是 | I | 可以转发 | |
I(Invalid) 无效 | - | - | - | - | - |
三种缓存同步协议对比:Nehalem MESIF、Opteron MOESI、Xeon MESI
MESIF可以说是Intel在多Xeon使用的MESI协议的扩充,增加了一个F状态(同时修改了S状态让其无法转发以避免进行过多的传输)。F状态就是这样一个状态:在一个多处理器之间共享的缓存页面中,只有其中一个处理器的该页面处于F状态,另外所有处理器的该页面均处于S状态,F状态负责响应其他没有该页面的处理器的读请求,而S状态则不响应并且不允许将缓存页面发给他人(或许S用Silent来代表更合适)。
当一个新处理器需求读取这个F页面时,原有的F页面则转为S状态,新的处理器获得的页面总是保持为F状态。在一群相同的页面中总有并且只有一个页面是处于F状态,其他的S副本则以F副本为中心。这种流动性让传输压力得以分散到各个处理器上,而不是总维持在原始页面上。
不会改变的页面的共享很好处理,关键的是对Dirty页面的对待(Dirty页面是指一个内容被修改了的缓存页面,需要更新到内存里面去),显然,一堆页面的副本中同一时间内只能有其中一个可以被写入。MESIF中,只具有一个副本的E状态在被写入的时候只需要简单地转化为M状态;而F状态被写入时则会导致其所有的S副本都被置为无效(通过一个广播完成);S副本是“沉默”的,不允许转发,也不允许被写入,这些副本所在的处理器要再次使用这个副本时,需要再次向原始F副本请求,F副本现在已经转化为M副本,被请求状态下M副本会写入内存并重新转化为F状态,不被请求时则可以保持在M状态,并可以不那么快地写入内存以降低对内存带宽的占用。
MESIF实际上只允许一堆共享副本当中的中央副本(F状态)被写入,在多个处理器均需要写入一个缓存页面的时候,会引起“弹跳”现象,F副本在各个处理器之间不停传输??这有点像令牌环??会降低性能,特别是F副本不在其所在的原始内存空间的时候。
Opteron的MOSEI协议不需要被写入的M状态写入内存就可以进行共享(这时M状态会转变为O状态,共享后的Dirty副本被标记为S状态),这避免了一次写入内存,节约了一些开销,尚不清楚为什么Intel没有在新生的总线上采用这种更为优化的协议;当再次写入O状态副本时,其他的S副本同样会被设置为无效。MOSEI也只允许一堆共享副本当中的中央副本(O状态)被写入,也存在着弹跳现象。
不过,在一个方面Nehalem具有优势:包含式(或者非独占式)L3缓存,当一个处理器被请求一个页面,或者被通知一个页面要被设置为无效的时候,它只需要检查L3就可以知道该如何操作。在L3缓存没有这个页面的时候,不需要像非包含式L3设计那样,再检查L1、L2页面。