服务器 频道

2008年度评测报告:深入Nehalem微架构

  【IT168评测中心】在过去数年里,Intel的全线处理器产品都是基于Core架构(当然,Itanium除外),包括了从移动、桌面年乃至企业级计算。Core架构来源于Pentium M移动处理器产品,和Pentium 4的NetBurst架构不同,Core架构基于不同的研究团队:以色列(Israel)的海法(Haifa)研究中心。和NetBurst相比,Core更接近于Intel的超标量处理器的鼻祖Pentium Pro。

  毫无疑问Core架构具有一个移动计算的起源,虽然Core现在在所有产品线上都获得了成功——因为它强劲的性能,以及很好的功耗表现。一些原始的偏向移动的设计现在已经不敷使用。


Nehalem先行者:Core i7处理器

  例如,古老的双核设计:笔记本不需要太多的处理核心(即使是现在,也只有少量工作站级别的笔记本配置了四核),让目前的Core架构已经不太适合于服务器市场,虽然6核心的7400系列Xeon已经推出,不过它和当前的四核产品一样,都是多个“双核”粘结在一起的产物。不会有基于Core架构(不是Core微架构)的8核心产品了,那样做的代价太大,基于FSB方面的原因,性能将会很受限制。

  古老的共享式FSB总线设计在服务器领域已经体现出了疲态,即使是在桌面领域也得到了体现,目前的四核Core产品,两个“双核”之间的通信是基于FSB总线,而不是基于内部开关或者其他的结构。服务器市场通过Snoop Filter缓存可以部分地提升核心之间的通信效率。在这一点上竞争对手做的比较好。

  此外,移动计算对64位计算的要求也不高,这也导致了当前的Core架构主要是为32位计算优化(虽然其64位性能也算不错)。


Nehalem先行者:Core i7处理器

  总而言之,Core架构已经到了需要改革的地步了,这也是Nehalem出现的理由。Nehalem是俄勒冈州(Oregen)的一条河流的名字,以前曾经是一种超高频率Pentium 4的代号。下面我们就深入地分析一下Nehalem的微架构。我们先从处理器的核心开始(如L3架构、QPI总线这样的部分在Nehalem里面称之为Uncore——非核心部分)。

再攀性能之巅 Intel全新酷睿i7深度评测

机密揭露:Intel超线程技术有多少种?

[IDF08]基辛格演讲:Nehalem集群演示

初代Core架构:Banias Pentium M

   Core架构具有一个移动计算的起源,它源自Banias Pentium M处理器,Pentium M处理器是以色列(Israel)的海法(Haifa)研究中心专门针对笔记本电脑的产品,特点是高效、低耗。时值2004年,开发NetBurst架构的美国德克萨斯州(Texas)的奥斯丁(Austin)设计团队尚在设计Tejas(Prescott的下一代)。很快NetBurst失败,Core架构被扶正,之后迅速地成为Intel的主要架构,产品开始扩展到桌面乃至服务器产品线(很可怕地,Austin设计团队被分派去设计一个极低功耗的CPU,就是后来的Atom凌动处理器)。


Harpertown是基于45nm Penryn的四核Xeon DP处理器

  Core架构无疑很成功,然而随着时间的流逝,如古老的双核设计(4核是两个双核粘在一起,6核则是三个双核粘结)、过时的FSB总线以及没有充分为64位计算准备等等,让其无法获得很好的伸缩性,难以未来需要高性能多处理器需求的企业级市场。此外,在NetBurst架构上耗资了数亿美元的HTT超线程技术也没能得到体现,Intel需要制作一款新的处理器产品来满足未来的需求。

  Intel对Core架构作出了改动,首先它将原来的架构扩展为原生4核(甚至6核、8核)设计,并为多核的需要准备了新的总线QPI来满足巨大的带宽需求,结果就是Nehalem内核。Nehalem内核还采用了集成内存控制器的设计,也是为了满足多核心巨大的带宽需求。


4核心Nehalem: Core i7处理器晶元图


4核心Nehalem: Core i7处理器的一些简要参数

  除了采用原生多核的设计外,Nehalem还融合了NetBurst架构的超线程技术以提高性能。超线程技术是Hillsboro团队基于NetBurst架构设计的,最先在Xeon处理器上得到应用并延伸到桌面的Pentium  4处理器。Core 2架构则一直没有应用超线程技术,部分地是因为要用到的架构上需要进行不少的修改和测试工作。当时Core 2并没有这样的美国时间来做这样的事,现在他们终于有空了。

 

Nehalem的超线程技术就是NetBurst超线程技术的升级版本,和Atom和Itanium的超线程技术都不同

 

Nehalem超线程技术的实施细节
 

  超线程技术可以通过很少的代价提升并行应用的性能特别是在服务器领域,因此Nehalem在服务器领域的能力将会再一次得到提升,即使是没有考虑到后面介绍的微架构方面的改进的情况下。    


     CPU-Z 1.48可以正常识别出桌面版Nehalem——Core i7 940的详细信息

The Core: Partition
核心:功能区间划分

  Nehalem是一款OOOE乱序执行的Superscaler超标量处理器。现在的乱序执行处理器采用的流水线可能深度不一,但是它们都离不开取指(Instruction Fetch)、解码(Decode)、执行(Execute)、串行顺序回退(Retire)这四个阶段。 

 
官方公布的简单的架构图

  既然是乱序执行,那么四个阶段中,取指令、指令解码和回退阶段实际上仍然是属于In-Order顺序的。加上内存存取方面的内容,Nehalem处理器可以按下面的颜色划分:

 
Nehalem Microarchitecture,经笔者整理

   紫色部分属于取指令部分,橙色则属于解码部分。黄色部分是乱序执行的准备部分(灰色Retirement Register File属于乱序架构的Retire部分),蓝色方框是计算单元,绿色方框是内存子系统(包括紫色部分的指令缓存在内),计算单元和内存子系统的一部分(存取单元)一起成为乱序执行单元,下面就大致从指令拾取开始介绍Nehalem的微架构,这些内容就经过了笔者的多方面查证以确保具有较高的准确性。然而由于内容太多,错漏难以避免,欢迎读者们一一指出。

The Core Front-End: Instruction Fetch
处理器核心前端:指令拾取

  在执行指令之前,必须先装载指令。指令会先保存在L1缓存的I-cache(Instruction-cache)指令缓存当中,Nehalem的指令拾取单元使用128bit带宽的通道从I-cache中读取指令。这个I-cache的大小为32KB,采用了4路集合关联,在后面的存取单元介绍中我们可以得知这种比Core更少的集合关联数量是为了降低延迟。

  为了适应超线程技术,RIP(Relative Instruction Point,相对指令指针)的数量也从一个增加到了两个,每个线程单独使用一个。

 
The Core Front-End: Instruction Fetch

  指令拾取单元包含了分支预测器(Branch Predictor),分支预测是在Pentium Pro处理器开始加入的功能,预测如if then这样的语句的将来走向,提前读取相关的指令并执行的技术,可以明显地提升性能。指令拾取单元也包含了Hardware Prefetcher,根据历史操作预先加载以后会用到的指令来提高性能,这会在后面得到详细的介绍。


两级分支预测机制


重命名的返回堆栈缓冲器

  当分支预测器决定了走向一个分支之后,它使用BTB(Branch Target Buffer,分支目标缓冲区)来保存预测指令的地址。Nehalem从以前的一级BTB升级到了两个级别,这是为了适应很大体积的程序(数据库以及ERP等应用,跳转分支将会跨过很大的区域并具有很多的分支)。Intel并没有提及BTB详细的结构。与BTB相对的RSB(Return Stack Buffer,返回堆栈缓冲区)也得到了提升,RSB用来保存一个函数或功能调用结束之后的返回地址,通过重命名的RSB来避免多次推测路径导致的入口/出口破坏。RSB每个线程都有一个,一个核心就拥有两个,以适应超线程技术的存在。

  指令拾取单元使用预测指令的地址来失去指令,它通过访问L1 ITLB里的索引来继续访问L1I Cache,128条目的小页面L1 ITLB按照两个线程静态分区,每个线程可以获得64个条目,这个数目比Core 2的少。当关闭超线程时,单独的线程将可以获得全部的TLB资源。除了小页面TLB之外,Nehalem还每个线程拥有7个条目的全关联(Full Associativity)大页面ITLB,这些TLB用于访问2M/4M的大容量页面,每个线程独立,因此关闭超线程不会让你得到14个大页面ITLB条目。

  指令拾取单元通过128bit的总线将指令从L1I Cache拾取到一个16Bytes(刚好就是128bit)的预解码拾取缓冲区。128位的带宽让人有些迷惑不解,Opteron一早就已经使用了256bit的指令拾取带宽。最重要的是,L1D和L1I都是通过256bit的带宽连接到L2 Cache的。

  由于一般的CISC x86指令都小于4Bytes(32位x86指令;x86指令的特点就是不等长),因此一次可以拾取4条以上的指令,而预解码拾取缓冲区的输出带宽是6指令每时钟周期,因此可以看出指令拾取带宽确实有些不协调,特别是考虑到64位应用下指令会长一些的情况下(解码器的输入输出能力是4指令每时钟周期,因此32位下问题不大)。

  指令拾取结束后会送到18个条目的指令队列,在Core架构,送到的是LSD循环流缓冲区,在后面可以看到,Nehalem通过将LSD移动后更靠后的位置来提高性能。

The Core Front-End: Decode
处理器核心前端:解码

  在将指令填到18条目指令队列之后,就可以进行解码工作了。解码是类RISC(精简指令集或简单指令集)处理器导致的一项设计,从Pentium Pro开始在IA架构出现。处理器接受的是x86指令(CISC指令,复杂指令集),而在执行引擎内部执行的却不是x86指令,而是一条一条的类RISC指令,Intel称之为Micro Operation——micro-op,或者写为µ-op,一般用比较方便的写法来代替希腊字母:u-op或者uop。相对地,一条一条的x86指令就称之为Macro Operation,或macro-op。

  RISC架构的特点就是指令长度相等,执行时间恒定(通常为一个时钟周期),因此处理器设计起来就很简单,可以通过深长的流水线达到很高的频率(例如31级流水线的Pentium 4……当然Pentium 4要超过5GHz的屏障需要付出巨大的功耗代价),IBM的Power6就可以轻松地达到4.7GHz的起步频率。关于Power6的架构的非常简单的介绍可以看《机密揭露:Intel超线程技术有多少种?》,我们继续说Nehalem:和RISC相反,CISC指令的长度不固定,执行时间也不固定,因此Intel的RISC/CISC混合处理器架构就要通过解码器将x86指令翻译为uop,从而获得RISC架构的长处,提升内部执行效率。

  和Core一样,Nehalem的解码器也是4个:3个简单解码器加1个复杂解码器。简单解码器可以将一条x86指令(包括大部分SSE指令在内)翻译为一条uop,而复杂解码器则将一些特别的(单条)x86指令翻译为1~4条uops——在极少数的情况下,某些指令需要通过额外的可编程microcode解码器解码为更多的uops(有些时候甚至可以达到几百个,因为一些IA指令很复杂,并且可以带有很多的修改量,当然这种情况很少见),下图Complex Decoder左方的ucode方块就是这个解码器,这个解码器可以通过一些途径进行升级或者扩展,实际上就是通过主板Firmware里面的Microcode ROM部分。

  之所以具有两种解码器,是因为仍然是关于RISC/CISC的一个事实:大部分情况下(90%)的时间内处理器都在运行少数的指令,其余的时间则运行各式各样的复杂指令(不幸的是,复杂就意味着较长的运行时间),RISC就是将这些复杂的指令剔除掉,只留下最经常运行的指令(所谓的精简指令集),然而被剔除掉的那些指令虽然实现起来比较麻烦,却在某些领域确实有其价值,RISC的做法就是将这些麻烦都交给软件,CISC的做法则是像现在这样:由硬件设计完成。因此RISC指令集对编译器要求很高,而CISC则很简单。对编程人员的要求也类似。


The Core Front-End: Decode

  作为对比,AMD的处理器从K8以来就具有强大的三组复杂解码器,并且大部分指令都可以解码为1~2条AMD的Micro-operations(这个Micro-op和Intel的uop是不同的东西)。


Macro Fusion功能

  在进行解码的时候,会碰到Intel在Core 2开始加入的技术:Macro Fusion。关于Macro Fusion可以看这里《64位对决32位 SPEC CPU运算效能测试》,这项技术可以将一些比较并跳转的分支x86指令集合(CMP+TEST/JCC)最终解码为单条uop(CMP+JCC),从而提升了解码器的带宽、降低执行指令数量,让系统运行效率更高。和Core 2相比,Nehalem现在支持更多的比较/跳转分支情况,如JL/JNGE、JGE/JNL、JLE/JNG、JG/JNLE等,此外,Nehalem也开始更多地为服务器平台考虑,它的Macro Fusion开始支持64位模式,而不是Core 2的只支持32位模式。随着内存容量的日益增长,即使是在桌面平台及移动平台,64位用户也在不断增加,因此这个改进对用户很有吸引力。

  除了将多条Macro Ops(就是x86指令)聚合之外,Nehalem也支持将多条uops聚合,也就是Micro Fusion功能,用于降低uop的数量。这是一个从Pentium M开始存在的老功能了,它在顺序上是在比Macro Fusion后面的后面,也就是在LSD循环流监测器后方。据说,Micro Fusion可以提升5%的整数性能和9%的浮点性能,而Macro Fusion则可以降低10%的uop数量。

The Core Front-End: Loop Stream Detector
处理器核心前端:循环流检测

  在解码为uop之后Nehalem会将它们都存放在一个叫做uop LSD Buffer的缓存区。在Core 2上,这个LSD Buffer是出现在解码器前方的,Nehalem将其移动到解码器后方,并相对加大了缓冲区的条目。Core 2的LSD缓冲区可以保存18个x86指令而Nehalem可以保存28个uop,从上一页可以知道,大部分x86指令都可以解码为一个uop,少部分可以解码为1~4个uop,因此Nehalem的LSD缓冲区基本上可以相当于保存21~23条x86指令,比Core 2要大上一些。

 
The Core Front-End: Loop Stream Detector

  LSD循环流监测器也算包含在解码部分,它的作用是:假如程序使用的循环段(如for..do/do..while等)少于28个uops,那么Nehalem就可以将这个循环保存起来,不再需要重新通过取指单元、分支预测操作,以及解码器,Core 2的LSD放在解码器前方,因此无法省下解码的工作。

  Nehalem LSD的工作比较像NetBurst架构的Trace Cache,其也是保存uops,作用也是部分地去掉一些严重的循环,不过由于Trace Cache还同时担当着类似于Core/Nehalem架构的Reorder Buffer乱序缓冲区的作用,容量比较大(可以保存12k uops,准确的大小是20KB),因此在cache miss的时候后果严重(特别是在SMT同步多线程之后,miss率加倍的情况下),LSD的小数目设计显然会好得多。不过笔者认为28个uop条目有些少,特别是考虑到SMT技术带来的两条线程都同时使用这个LSD的时候。

  在LSD之后,Nehalem将会进行Micro-ops Fusion,这也是前端(The Front-End)的最后一个功能,在这些工作都做完之后,uops就可以准备进入执行引擎了。

The Core Execution Engine: Out-of-Order Execution
处理器核心执行引擎:乱序执行

  OOOE——Out-of-Order Execution乱序执行也是在Pentium Pro开始引入的,它有些类似于多线程的概念,这些在《机密揭露:Intel超线程技术有多少种?》里面可以看到相关的介绍。乱序执行是为了直接提升ILP(Instruction Level Parallelism)指令级并行化的设计,在多个执行单元的超标量设计当中,一系列的执行单元可以同时运行一些没有数据关联性的若干指令,只有需要等待其他指令运算结果的数据会按照顺序执行,从而总体提升了运行效率。乱序执行引擎是一个很重要的部分,需要进行复杂的调度管理。

  然而乱序执行架构中,不同的指令可能都会需要用到相同的通用寄存器(GPR,General Purpose Registers),为了在这种情冲突的况下指令们也能并行工作,处理器需要准备解决方法。一般的RISC架构准备了大量的GPR,而x86架构天生就缺乏GPR(x86具有8个GPR,x86-64具有16个,一般RISC具有32个,IA64则具有128个),为此Intel开始引入重命名寄存器(Rename Register),不同的指令可以通过具有名字相同但实际不同的寄存器来解决,这有点像魔兽世界当中的副本设计,当然其出现要比WoW早多了。

  此外,为了SMT同步多线程,这些寄存器还要准备双份,每个线程具有独立的一份。


The Core Execution Engine: Out-of-Order Execution

  乱序执行从Allocator定位器开始,Allocator管理着RAT(Register Alias Table,寄存器别名表)、ROB(Re-Order Buffer,重排序缓冲区)和RRF(Retirement Register File,退回寄存器文件)。在Allocator之前,流水线都是顺序执行的,在Allocator之后,就可以进入乱序执行阶段了。在每一个线程方面,Nehalem和Core 2架构相似,RAT将重命名的、虚拟的寄存器(称为Architectural Register或Logical Register)指向ROB或者RRF。RAT是一式两份,每个线程独立,每个RAT包含了128个重命名寄存器(Pentium 4之前——Pentium Pro到Pentium III的重命名寄存器基于ROB,数量为40个;据说Pentium 4有128个)。RAT指向在ROB里面的最近的执行寄存器状态,或者指向RRF保存的最终的提交状态。

  ROB(Re-Order Buffer,重排序缓冲区)是一个非常重要的部件,它是将乱序执行完毕的指令们按照程序编程的原始顺序重新排序的一个队列,打乱了次序的指令们依次插入这个队列,当一条指令通过RAT发往下一个阶段确实执行的时候这条指令(包括寄存器状态在内)将被加入ROB队列的一端,执行完毕的指令(包括寄存器状态)将从ROB队列的另一端移除(期间这些指令的数据可以被一些中间计算结果刷新),因为调度器是In-Order顺序的,这个队列也就是顺序的。从ROB中移出一条指令就意味着指令执行完毕了,这个阶段叫做Retire回退,相应地ROB往往也叫做Retirement Unit(回退单元),并将其画为流水线的最后一部分。

  在一些超标量设计中,Retire阶段会将ROB的数据写入L1D缓存,而在另一些设计里,写入L1D缓存由另外的队列完成。例如,Core/Nehalem的这个操作就由MOB(Memory Order Buffer,内存重排序缓冲区)来完成。


ReOrder Buffer重排序缓冲区是一个Retire Queue回退队列,是OOOE乱序架构流水线的最后一段

  ROB是乱序执行引擎架构中都存在的一个缓冲区,重新排序指令的目的是将指令们的寄存器状态依次提交到RRF退回寄存器文件当中,以确保具有因果关系的指令们在乱序执行中可以得到正确的数据。从执行单元返回的数据会将先前由调度器加入ROB的指令刷新数据部分并标志为结束(Finished),再经过其他检查通过后才能标志为完毕(Complete),一旦标志为完毕,它就可以提交数据并删除重命名项目并退出ROB了。提交状态的工作由Retirement Unit(回退单元)完成,它将确实完毕的指令包含的数据写入RRF(“确实”的意思是,非猜测执性、具备正确因果关系,程序可以见到的最终的寄存器状态)。和RAT一样,RRF也同时具有两个,每个线程独立。Core/Nehalem的Retirement Unit回退单元每时钟周期可以执行4个uops的寄存器文件写入,和RAT每时钟4个uops的重命名一致。

The Core Execution Engine: Out-of-Order Execution
处理器核心执行引擎:乱序执行

   由于ROB里面保存的指令数目是如此之大(128条目),因此一些人认为它的作用是用来从中挑选出不相关的指令来进入执行单元,这多少是受到一些文档中的Out-of-Order Window乱序窗口这个词的影响(后面会看到对ROB会和MOB一起被计入乱序窗口资源中)。

  ROB确实具有RS的一部分相似的作用,不过,ROB里面的指令是调度器通过RAT发往RS的同时发往ROB的,也就是说,在“乱序”之前,ROB的指令就已经确定了。指令并不是在ROB当中乱序挑选的(这在RS当中进行),ROB担当的是流水线的最终阶段:一个指令的Retire回退单元;以及担当中间计算结果的缓冲区。

RS(Reservation Station,中继站):等待源数据到来以进行OOOE乱序执行(没有数据的指令将在RS等待)
ROB(ReOrder Buffer,重排序缓冲区):等待结果到达以进行Retire指令回退(没有结果的指令将在ROB等待)

 
The Core Execution Engine: Out-of-Order Execution

  Nehalem的128条目的ROB担当中间计算结果的缓冲区,它保存着猜测执行的指令及其数据(猜测执行也是在Pentium Pro开始引入的,Pentium Pro真是一个划时代的产品),猜测执行允许预先执行方向未定的分支指令,Nehalem的分支预测成功率没有被提及(只是含糊地说“业内最高”)。在大部分情况下,猜测执行工作良好——分支猜对了,因此其在ROB里产生的结果被标志为已结束,可以立即地被后继指令使用而不需要进行L1 Data Cache的Load操作(这也是ROB的另一个重要作用,典型的x86应用中Load操作是如此频繁,达到了几乎占1/3的地步,因此ROB作用巨大)。在剩下的不幸的情况下,分支未能按照如期的情况进行,这时猜测的分支指令段将被清除,相应指令们的流水线阶段清空,对应的寄存器状态也就全都无效了,这种无效的寄存器状态不会也不能出现在RRF里面。


Pentium Pro架构,可见在后继型号在OOOE方面变化的基本只是一些数字变大了

  重命名技术并不是没有代价的,在获得前面所说的众多的优点之后,它令指令在发射的时候需要扫描额外的地方来寻找到正确的寄存器状态,不过总体来说这种代价是非常值得的。RAT可以在每一个时钟周期重命名4个uops的寄存器,经过重命名的指令在读取到正确的操作数并发射到统一的RS(Reservation Station,中继站,Intel文档翻译为保留站点)上。RS中继站保存了所有等待执行的指令。

  和Core 2相比,Nehalem的ROB大小和RS大小都得到了提升,ROB重排序缓冲区从96条目提升到128条目(鼻祖Pentium Pro具有40条),RS中继站从32提升到36(Pentium Pro为20),它们都在两个线程(超线程中的线程)内共享,不过采用了不同的策略:ROB是采用了静态的分区方法,而RS则采用了动态共享,因为有时候会有一条线程内的指令因等待数据而停滞,这时另一个线程就可以获得更多的RS资源。停滞的指令不会发往RS,但是仍然会占用ROB条目。由于ROB是静态分区,因此在开启HTT的情况下,每一个线程只能分到64条,不算多,在一些极少数的应用上,我们应该可以观察到一些应用开启HTT后会速度降低,尽管可能非常微小。

The Core Execution Engine: Execution Unit
处理器核心执行引擎:执行单元

  在为SMT做好准备工作并打乱指令的执行顺序之后,uops通过每时钟周期4条的速度进入Reservation Station中继站(保留站),总共36条目的中继站uops就开始等待超标量(Superscaler)执行引擎乱序执行了。自从Pentium开始,Intel就开始在处理器里面采用了超标量设计(Pentium是两路超标量处理器),超标量的意思就是多个执行单元,它可以同时执行多条没有相互依赖性的指令,从而达到提升ILP指令级并行化的目的。Nehalem具备6个执行端口,每个执行端口具有多个不同的单元以执行不同的任务,然而同一时间只能由一条指令(uop)进入执行端口,因此也可以认为Nehalem有6个“执行单元”,在每个时钟周期内可以执行最多6个操作(或者说,6条指令),和Core一样;令人意外的是,古老的Pentium 4每时钟周期也能执行最多6个指令,虽然它只有4个执行端口,然而其中两个执行端口的ALU单元是双倍速的(Double Pump,每时钟周期执行两条ALU指令)。


Nehalem:Superscale Execution Unit超标量执行单元

  中文版本可能反而不便于理解(如负载操作实际上是Load载入操作),下面是英文原版:


Nehalem:Superscale Execution Unit超标量执行单元

  36条目的中继站指令在分发器的管理下,挑选出尽量多的可以同时执行的指令(也就是乱序执行的意思)——最多6条——发送到执行端口。

  这些执行端口并不都是用于计算,实际上,有三个执行端口是专门用来执行内存相关的操作的,只有剩下的三个是计算端口,因此,在这一点上Nehalem实际上是跟Core架构一样的,这也可以解释为什么有些情况下,Nehalem和Core相比没有什么性能提升。

  计算操作分为两种:使用ALU(Arithmetic Logic Unit,算术逻辑单元)的整数(Integer)运算和使用FPU(Floating Point Unit,浮点运算单元)的浮点(Floating Point)运算。SSE指令(包括SSE1到SSE4)是一种特例,它虽然有整数也有浮点,然而它们使用的都是128bit浮点寄存器,使用的也大部分是FPU电路。在Nehalem中,三个计算端口都可以做整数运算(包括MMX)或者SSE运算(浮点运算不太一样,只有两个端口可以进行浮点ADD和MUL/DIV运算,因此每时钟周期最多进行2个浮点计算,这也是目前Intel处理器浮点性能不如整数性能突出的原因),不过每一个执行端口都不是完全一致:只有端口0有浮点乘和除功能,只有端口5有分支能力(这个执行单元将会与分支预测单元连接),其他FP/SSE能力也不尽相同,这些不对称之处都由统一的分发器来理解,并进行指令的调度管理。没有采用完全对称的设计可能是基于统计学上的考虑。和Core一样,Nehalem的也没有采用Pentium 4那样的2倍频的ALU设计(在Pentium 4,ALU的运算频率是CPU主频的两倍,因此整数性能明显要比浮点性能突出)。


The Core Execution Engine: "Computional" Unit

  Nehalem的ALU和FP/SSE单元都使用了相同的三个端口,相比之下,Barcelona Opteron的FP/SSE单元和ALU单元具有不同的入口,因此每时钟周期可以同时执行最多6条计算指令。不过,Barcelona Opteron的3存取操作使用和ALU单元一样的端口,因此其执行单元每时钟周期可以同时执行的指令仍然为6条。

  不幸的是,虽然可以同时执行的指令很多,然而在流水线架构当中运行速度并不是由最“宽”的单元来决定的,而是由最“窄”的单元来决定的。这就是木桶原理,Opteron的解码器后端只能每时钟周期输出3条uops,而Nehalem/Core2则能输出4条,因此它们的实际最大每时钟运行指令数是3/4,而不是6。同样地,多少路超标量在这些乱序架构处理器中也不再按照运算单元来划分,Core Duo及之前(到Pentium Pro为止)均为三路超标量处理器,Core 2/Nehalem则为四路超标量处理器。可见在微架构上,Nehalem/Core显然是要比其他处理器快一些。顺便说一下,这也是Intel在超线程示意图中,使用4个宽度的方块来表示而不是6个方块的原因。

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个时钟周期。

Core i7 940的缓存架构

 

The Memory sub-System: Cache
内存子系统:缓存

  与Core 2相比,Nehalem新增加了一层L3缓存,这是为了多个核心共享数据的需要(未来Nehalem会具有6到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 & QPI
核外系统:集成内存控制器及QPI

  从形式上来看,L3缓存、集成的内存控制器乃至QPI总线都属于Uncore核外部分,从L2、L1一直到执行单元都属于Core核内部分。由于Nehalem首次采用了这种核心内外的相对独立设计思路,因此核心之外的设计相对于Core架构来说显得新颖许多,这就是Nehalem的模块式设计。

  模块式设计可以提供灵活的产品给用户,现在4核心、三通IMC和单QPI的桌面Nehalem已经面市,预计明年3月将会出现4核心、4通道IMC和双QPI的企业级Nehalem产品。包含了PCIE控制器乃至集成显卡的产品也已经在路线上了。

  继续回到处理器架构:当处理器在L3 Cache未找到所要内容(L3 Cache Miss)的时候,它将会继续通过IMC集成内存控制器往系统内存索取,同时通过QPI总线询问其他处理器(如果是多处理器平台)。

  使用高频率DDR3内存,访问本地内存的延迟大约为60个时钟周期,而通过QPI总线访问远端的处理器并返回数据大约需要90个时钟周期(如上一页所述)。QPI的就是Core架构为了使用服务器市场而做出的进化,它可以建立一个庞大的可扩展的解决方案。

QPI vs. FSB
 Intel FSB(Front Side Bus)Intel QuickPath Interconnect(QPI)
拓扑共享总线点对点连接
物理总线宽度(bits)6420
数据传输宽度(bits)6416
需要边带信号
引脚数15084
时钟数11
集成时钟NoNo
总线传输方向双向单向

  除了提供更高的带宽(每链路25.6GB双向带宽)之外,QPI总线还让多处理器系统更有效率:处理器之间可以直接连接。如上图,每个CPU都可以直接和其他三个CPU通信;4路Barcelona Opteron在对角线上是无法直接通信的,需要邻近的CPU进行接力,这显然会降低效率;不过Shanghai Opteron已经可以做到:《全国首发 AMD Shanghai/上海性能评测》。

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页面。

Nehalem: ccNUMA & SMT & OS
Nehalem:ccNUMA与SMT与操作系统

  我们已经知道多路Nehalem会形成一个ccNUMA架构,在NUMA系统中,由于本地内存的访存延迟低于远程内存的访存延迟,因此将进程分配到本地内存附近的处理器上可极大优化应用程序的性能。这就需要操作系统支持并智能地进行这样的分配。


多个Nehalem处理器之间使用MESIF协议来保持缓存一致性

 
多个核心之间是否也使用MESIF协议来保持缓存一致性呢?

  除了NUMA架构的要求外,Nehalem的SMT技术(超线程技术)也要求操作系统的支持,这是基于这样的一个事实:线程调度时在两个逻辑CPU之间进行线程迁移的开销远远小于物理CPU之间的迁移开销以及逻辑CPU共享Cache等资源的特性。这一点和NUMA上同一个CPU的不同核心之间进行线程迁移的开销远远小于多个CPU之间的迁移开销以及同核心的CPU共享Cache等资源的特性是一样的,要系统发挥最大的性能,操作系统必须对NUMA以及超线程这样的实质上比较类似NUMA的这些架构作出优化。

  传统的基于NT核心的Windows都可以支持SMP对称多处理器技术,然而它们并没有很好地为NUMA和超线程优化(这也是当初Pentium 4 HT推荐使用WIndows XP而不是WIndows 2000操作系统的原因),在购买到Nehalem系统之后,你需要采用最新的操作系统:


Windows Server 2008内核对NUMA的优化


Windows Server 2008内核对NUMA IO的优化


Windows Server 2008对逻辑处理器们的划分(Group——Processor Group是Windows Server  2008 R2/Windows 7加入的功能)

  经过多次升级的Windows Server 2003可以较好地支持NUMA技术(为了支持广泛应用的Opteron——典型的NUMA架构),Windows XP也为超线程技术做了优化,然而它们都不够Windows Server 2008深入。2008为NUMA做出了包括内存管理方面的多种优化:分布式的非分页池、系统页表、系统缓存以及内存分配策略,同时还更好地支持NUMA I/O。在使用多Nehalem或者多Opteron这样的处理器时,你应该使用Windows Server 2008操作系统或者Windows Vista操作系统(2008和Vista使用了相同的内核,区别只是一些小的特性)。甚至在使用单Nehalem的时候,你也应该使用Vista,因为超线程的缘故。

  Linux 2.4内核中的调度器由于只设计了一个运行队列,可扩展性较差,在SMP平台表现一直不理想。后来在2.5内核开发时出现一个多队列调度器(Ingo Molnar),称为O(1),每个处理器具有一个运行队列,从2.5.2开始集成。由于O(1)调度器不能较好地感知NUMA系统中结点这层结构,从而不能保证在调度后该进程仍运行在同一个结点上,为此Linux 2.6内核出现了结点亲和的NUMA调度器(Eirch Focht),建立在Ingo Molnar的O(1)调度器基础上的(这个调度器后来也向后移植到2.4.X内核中),因此现在的Linux 2.6核心可以较好地支持NUMA和超线程。

  FreeBSD的SMP功能直到7.0版本才算大为完善,就目前来看,FreeBSD对NUMA的支持还比较原始。

Nehalem: Virtualization & SSE4.2
Nehalem:虚拟化与SSE4.2

  虚拟化作为Intel架构的重点,一直是Intel处理器的重要特性,每次处理器架构的更新,都会得到更多的支持。Nehalem也不例外,改进的地方虽然不多,然而这些改动大大提高了虚拟化性能。这些改动包括了两个部分:EPT扩展页表和VPID虚拟处理器ID,其中前者消灭了当前存在的虚拟机内存操作中存在的大量内存地址转换(以前使用软件来模拟EPT的功能,现在用硬件实现了,据说虚拟化延迟比Penryn降低了33%),后者则减少了对TLB的无效操作,这些都明显提升了虚拟机的性能。


EPT扩展页表和VPID虚拟处理器ID

  I/O虚拟化的关键在于解决I/O设备与虚拟机数据交换的问题,而这部分主要相关的是DMA直接内存存取,以及IRQ中断请求,只要解决好这两个方面的隔离、保护以及性能问题,就是成功的I/O虚拟化。在以前,Intel提供的设备虚拟化技术(VT-d,VT是Virtualization Technology虚拟化技术,d是device设备的意思)多出现在服务器芯片组上,现在随着Nehalem的出现,VT-d技术也开始流入桌面/移动市场(Core i7主板上已经可以见到VT-d功能)。

Core i7主板:Intel X58SO主板 - VT-d设置界面

 

Core i7主板:Intel X58SO主板 - VT-d设置界面

 

以往VT-d技术集成在北桥MCH内,和内存控制器的关系非浅

  和处理器上的Intel VT-i和VT-x一样,Intel VT-d技术是一种基于North Bridge北桥芯片(或者按照较新的说法:MCH)的硬件辅助虚拟化技术,通过在北桥中内置提供DMA虚拟化和IRQ虚拟化硬件,实现了新型的I/O虚拟化方式,Intel VT-d能够在虚拟环境中大大地提升 I/O 的可靠性、灵活性与性能。

  由于现在的x86 CPU都可以通过解码器将x86指令解码为uops再执行,而这个解码阶段是可以通过Microcode ROM控制的,添加新的解码方法很容易,因此现在Intel处理器的指令集扩充显得有些任意:指令数目不停地增多。当然,从编程的角度来看,使用预先给出的硬件指令到底是会比自己进行繁琐的程序设计要方便得多,而性能也获多或少总有些提升:


Nehalem附带了SSE4.2指令集,共7条指令

  SSE4指令集是自SSE以来最大的一次指令集扩展,它实际上分成了三个阶段来更新:提前发布的SSSE3、Penryn中出现的 SSE4.1和Nehalem中出现的SSE4.2,其中成熟的Penryn中集成的SSE4.1占据了大部分的指令,因此Nehalem中的SSE4指令集更新很少。要发挥新指令集的功能,需要在程序设计方面的支持,Intel自己的编译工具自然有所提供——从10.0版本开始。

  【IT168评测中心】从前面种种来看,虽然Nehalem在微架构上仍然是基于Core,然而做出了多方面的改进。从设计思想上看,Nehalem开始从微架构/架构上更多地考虑服务器市场,从大架构上:崭新的IMC集成内存控制器、QPI点对点连接总线都是对服务器应用影响甚巨的技术,在微架构上,Core古老的来源于Pentium M的单核(双核、四核、六核只是简单地扩展)设计被原生4核代替,并可以很容易地发展至原生6核/8核,配合HTT超线程技术和更完善的64位计算支持可以为服务器应用提供强大的性能。


Nehalem:可动态扩展的微架构

  不仅仅是考虑到服务器市场,Nehalem被设计为全线处理器市场考虑,它采用了模块化的设计,这也是架构方面的最大的变动,这种设计让其可以顺利地发展出对应各种平台的处理器产品来。总而言之,Nehalem在微架构上是一次不算大的变动,在架构上则是一次很大的变化。


超大Nehalem晶片点击大图

0
相关文章