现代微处理器-90分钟指南

有了篇像样文章,虽然是翻译的。

翻译为学习之用,没有核对校正,觉得不准确有错误的地方请看原文

http://www.lighterra.com/papers/modernmicroprocessors/

 

 

现代微处理器

-90 分钟指南

By Jason Patterson , last updated Jan 2011 (orig Feb 2001)

 

 

 

 

 

 

一、          不仅仅兆赫 ( 频率 ) 2

二、          流水线和指令集并行 ... 2

三、          深度流水线 - 超级流水线 ... 4

四、          多发射 - 超标量 ... 4

五、          显式并行 -VLIW .. 6

六、          指令相关和延迟 ... 6

七、          分支和分支预测 ... 7

八、          带谓词的分支取消 ... 8

九、          指令调度,寄存器重命名和 OoO .. 8

十、          超智能的争论 ... 9

十一、          X86 是什么样的 ? . 10

十二、          线程 -SMT, 超线程和多核 ... 11

十三、          更多的核心或者更宽的核心 ... 13

十四、          数据并行 -SIMD 矢量指 ... 14

十五、          Caches 和内存架构 ... 15

十六、          Cache 冲突和相联度 ... 16

十七、          内存带宽和延迟 ... 18

 

你是一个本科生,作为学位的一部分学习过硬件/ 汇编课程,但那时几年前的事情,现在你并没有跟踪处理器设计的最新细节。

你可能还没有意识到最近一些关键的主题正在快速发展。

  • 流水线( 超标量,OoO,VLIW, 分支预测,预测)
  • 多核、同时多线程(SMT, 超线程)
  • SIMD 矢量指令集(MMX/SSE/AVC,AltiVec)
  • 缓存和内存架构

不用担心,这篇文章会让你快速跟上( 这些主题的发展) ,你将会像专家一样讨论顺序和乱序、超线程、多核和缓存优化.

准备好了,这篇文章很概括和切中要害没有空隙, 步伐非常的大.

一、   不仅仅兆赫( 频率)

首先必须明确的是时钟速度和处理器性能之间的不同,他们不是一回事。请看一下几年之前一些处理器的测试结果:

  

SPECint95

SPECfp95

195 MHz

MIPS R10000

11.0

17.0

400 MHz

Alpha 21164

12.3

17.2

300 MHz

UltraSPARC

12.1

15.5

300 MHz

Pentium-II

11.6

8.8

300 MHz

PowerPC G3

14.8

11.4

135 MHz

POWER2

6.2

17.6

虽然200 MHz MIPS R10000 300 MHz UltraSPARC  400 MHz Alpha 21164 时钟速度差了两倍,但是运行大多数程序的速度差不多相同,300 MHz Pentium-II 大多数程序速度和上面几个处理器差不多,但是浮点运算比如科学数字处理的速度大约只有的它们的一半, 对于同样300MhZA PowerPC G3 在通常的整型运算代码比其他处理器稍快,但浮点运算比前三名慢了很多.

另一个极端情况,只有135MHz IBM POWER2 浮点运算速度和400MHzAlpha21164 差不多,但是通用整型程序速度只有它的一边

这些应该怎么解释呢?显然不仅仅是时钟速度再起作用,更重要的是处理器在每个时钟周期内做了多少工作,这就引出下面的课题.

二、   流水线和指令集并行

指令在处理器内部一个接一个的执行,对吗? 这样让人很容易理解,但并不是真正发生的事情,事实上,从80 年代中期就不是这样子了,取而代之的是几条指令在同时执行某个部分

指令怎么执行的- 取指、解码、使用合适的功能单元执行、最终结果写到合适的位置。对于这种简单策略,每条执行花费4 个时钟周期(CPI=4)

Figure 1 – 顺序处理器指令流

现代处理器在流水线上重叠这些阶段,像一个生产线,一条执行正在执行,下一条正在解码,再下一条正在取指.

Figure 2 – 流水线处理器指令流

处理器每个周期执行一条指令,不用改变时钟速度获得4 倍速度提高.

从硬件角度来看,每个流水阶段由一些组合逻辑组成,可能访问寄存器组和几种高速缓存和内存。流水阶段由 闩分割,普通的时钟信号同步每个阶段的闩,所以所有的闩在同时获得每个流水段产生的结果。实际上,时钟“泵”指令到流水线。

时钟周期的开始阶段,部分处理指令的数据和控制信息在流水线的 闩中保存,这些信息形成下一阶段逻辑电路的输入。时钟周期期间信号通过本阶段的组合逻辑,在时钟周期结束时产生被下一阶段的闩获取的输出

Figure 3 – 流水线微型架构

指令的结果在执行阶段已经完成,下一条指令应该马上可以使用结果值,而不是等到结果在写回阶段提交到目的寄存器。为了实现这一点,增加叫做“旁路”的通道,沿着流水线向前传送数据。

Figure 4 – 有旁路的流水线微型架构

流水线阶段非常简单,但是应该记住非常重要的一点,执行阶段是由不同的逻辑组构成的,包括为处理器必须执行的每种不同类型的操作而实现的功能单元.

Figure 5 – 更多细节的流水线微型架构

和上面的不同,早期的RISC 处理器比如IBM801 研究原型,MIPS R2000 和最初的SPARC 都实现了一个简单的5 阶段流水线( 多出的阶段是内存访问,在执行后面). 同时主流的80386,68030VAX 处理器使用微代码顺序工作(RISC 更容易实现流水线,因为它的执行都是简单的寄存器- 寄存器操作,这点和x86, 68KVAX 不同), 结果导致20MHzSPARC33MHz386 处理器速度更快. 从那以后每个处理器都流水化处理,至少做了一些流水线方面的扩展. 关于最初的RISC 研究项目总结可以在1985CACM 文章中找到.

三、   深度流水线- 超级流水线

流水线中时间最长的阶段限制了时钟速度,每个阶段特别是比较长的阶段逻辑门可以分割, 从而使用更短的阶段组成较深的超级流水线,整个处理器就可以在更高的时钟速度运行了. 当然每条指令会使用更多的时钟周期,但是处理器还是会每个周期完成一条指令(吞吐率), 由于每秒有更多的时钟周期,所以每秒可也完成更多的指令( 真正的性能).

Figure 6 – 超级流水线处理器指令流

Alpha 使用了这种超流水的架构,导致早期的Alpha 处理器有非常深的流水线,并且有非常高的时钟速度. 现代处理器非常强烈需要降低每个流水段的门延迟( 大约1530 个门延迟) ,大部分有很深的流水线(PowerPC G4e7-12, ARM11 & Cortex A9 超多8, Athlon 10-15, Pentium-Pro/II/III/M 超过12, Athlon 64 & Phenom 12-17 , Cortex A8 超过13, UltraSPARC-III/IV 14 , Core 2 超过14, Core i*214 段到超过18, Core 超过16, PowerPC G5 16-25, Pentium-4 超过20, Pentium-4E 超过31).X86 处理器通常比RISC 处理器有更深的流水线,因为它们需要做很多额外的工作解码复杂的x86 指令. UltraSPARC-T1/T2/T3 是深流水线趋势的例外(UltraSPARC-T1 仅仅6,T2/T3 8-12).

四、   多发射- 超标量

流水线每个执行阶段使用一组执行不同任务的功能单元,看起来很适合并行执行多条指令,每个使用各自的功能单元. 为了实现这种功能,取指和解码/ 分发阶段必须加强以便可以并行解码多条指令并且发送到“执行资源”.

Figure 7 – 超标量微架构

很显然,每个功能单元使用独立的流水线,它们甚至可以有不同的长度,这样的话简单指令可以完成的比较快,减少延迟. 在流水线内部和之间还要有一些旁路,导致事情没那么简单了。

上面的例子中,处理器可能每个时钟周期执行3 条指令- 如整数、浮点和内存访问操作。可以增加更多的功能单元,处理器可能每个周期执行两条整数指令或两条浮点指令或者目标程序使用最优化的方式执行.

超标量流水线处理器的指令流看起来像下面这个样子

Figure 8 – 超标量处理器指令流

非常好!现在每个周期完成3 条指令(CPI=0.33,IPC=3), 处理器每个时钟周期发射或完成的指令数叫做处理器的宽度.

需要注意的是发射宽度通常小于功能单元的数量,因为不同的代码序列指令混合方式不同,所以功能单元必须更加多一些. 这种方式每个周期完成3 条指令,这些指令中不总是只有一条整数、一条浮点和一条内存操作,所以需要更多的功能单元.

PowerPC 的前一代IBM POWER1 是主流的超标量处理器,随后大多数RISC 采用超标量.Intel 试图为x86 实现超标量,但是x86 复杂的指令集对他们来说是个很大的问题.

当然,没有什么事情阻止处理器同时使用深流水线和多指令发射,所以处理器既可以是超流水线同时也可以是超标量的。

Figure 9 – 超流水线- 超标量处理器指令流

今天,总的来说每个处理器都是超流水线- 超标量的, 简单的成为超标量代替,因为严格的来说超流水线只不过是更深的流水线而已.

当前处理器的宽度从1- 发射(ARM11, UltraSPARC-T1)2- 发射(UltraSPARC-T2/T3, Cortex A8 & A9)3- 发射(Pentium-Pro/II/III/M, Athlon/Athlon 64 & Phenom, Pentium-4)4- 发射(UltraSPARC-III/IV, PowerPC G4e, Core 2, Core i, Core i*2)5- 发射(PowerPC G5) 甚至6- 发射(Itanium, but it's a VLIW – see below). 每个处理器的具体数目和功能单元依赖于它的目标市场. 有些处理器拥有更多的浮点执行资源(IBM's POWER 系列), 其他的有更多的整数资源,有一些在SIMD 上投入更多的资源(PowerPC G4e), 更多的处理器采用“平衡”的策略.

五、   显式并行-VLIW

在不考虑向后兼容的情况下,为了并行执行,指令集可以显示的成组来设计,这种方法可以减少分发阶段复杂的依赖检查逻辑,使得处理器设计更加容易( 更容易提高时钟速度,至少从理论上来说).

这种类型的处理器中,指令有一些子指令组成导致指令本身很长,所以它们的名字叫做 超长指令字”, 每个指令中包含多个并行操作的信息.

Figure 10 –VLIW 处理器指令流

处理简化分发逻辑外,VLIW 处理器和超标量非常相似,从编译器的角度来看更是如此.

大多数VLIW 设计不是交叉的,意味着它们不检查指令之间的依赖性,cache 缺失的情况下,它们会阻塞整个处理器而不是阻塞指令. 编译器需要在依赖指令之间插入合适的周期,如果没有指令填补空缺,必要的时候使用nops 指令. 因为编译器做了通常是由超标量处理器运行时做的事情,所以变得复杂,但是编译器额外的代码非常小可以节约处理器的芯片资源.

没有VLIW 设计已经商用成功,但是IntelIA64 架构, 作为Itanium 处理器仍在生产,曾经试图替代x86.IntelIA64 叫做“EPIC”( 显式并行质量计算) 设计, 但是实际上它是VLIW 带有聪明的分组和预测. 许多图形处理器被认为是类似VLIW 的,尽管它们提供面向图形的指令集,并且具有Transmeta.

六、   指令相关和延迟

流水线和多发射可以走多远呢?如果5 阶段流水线快5 倍,为什么不建造20 阶段的超级流水线呢?如果4 发射超标量足够好,为什么不使用8 发射的呢? 同样的道理,为什么不建造一个20 发射50 阶段的流水线的处理器呢?

那么,考虑下面的两条指令

a = b * c;

d = a + 1;

第二条指令依赖第一条指令- 处理器不能执行第二条指令直到第一条完成计算它的结果. 这是一个关键的问题,因为指令相互依赖导致不能并行执行,多发射在这种情况下是不可能的.

如果第一条指令是简单的整数加,那么对于流水单发处理器还是可以接受的,因为整数加非常快,第一条指令的结果可以反馈到下一条指令(通过旁路). 但是在乘法的情况下,它使用多个周期完成,第一条指令的结果不能第二条指令的执行阶段到达. 因此处理器需要停止执行指令直到数据可用, 通过插入一个什么工作也不做的 气泡 到流水线中来实现.

使用延迟可能带来一些误解, 这里我从编译器的角度讨论延迟. 硬件工程师可能认为延迟是执行需要的总的时钟周期( 流水线的长度) ,所以硬件工程师会说整数流水线中吞吐率是1 的时候一条指令延迟是5 ,但是从编译器角度来开它们的延迟是1 ,因为它们的结果可以在下一个周期使用. 编译器角度更为通用,甚甚至在硬件手册中也经常使用.

一条指令的执行阶段到它的结果可以被下一条指令使用之间的周期数叫做指令的延迟. 流水线越深,阶段越多那么延迟越长. 所以一个深流水线不一定比短

从编译器的角度来看,现代处理器典型的延迟对于整数操作是一个周期,浮点加大约3-6 个周期,(整数)乘法操作相等或更长一些,整数除法大约12 个周期.

内存加载是主要的麻烦,部分原因是它们在代码序列的较前执行导致很难使用有用的执行填充, 还有同等重要的一点是它们很难预测- 加载延迟随cache 访问是否命中有很大不同.

七、   分支和分支预测

流水线的另外一个关键问题是分支,考虑下面的代码序列

if (a > 5)

b = c;

else

b = d;

编译成下面的代码:

cmp a, 5    ; a > 5 ?

ble L1

mov c, b    ; b = c

br L2

L1: mov d, b    ; b = d

L2: ...

考虑流水线处理器执行这个代码序列,第二行的条件分支到达执行阶段,处理器已经取指和解码下面很多条指令,但是是那些执行呢? 它应该取指和解码if 分支还是else 分支? 在条件分支到达执行阶段之前它是不知道的, 但是在较深的流水线处理器中已经浪费了几个时钟周期, 它不能承受只是等待- 处理器平均每六条指令遇到一条分支指令, 如果每个分支指令倒要等待几个周期,那么就会失去使用流水化得到的大部分性能(提升).

所以处理器必须猜测,处理器取指它猜测路径上的指令并且投机地开始执行这些指令. 当然它不会真正提交(写回)这些指令直到分支输出可以确定. 比较坏的情况,如果猜测是错的,指令必须被取消,这些周期就被浪费了. 但是如果猜测是正确的,处理器就可以全速运行.

关键问题是处理器如何猜测,有两种选择. 第一, 编译器标记分支高速处理器执行那个路径,这叫做静态分支预测. 如果指令格式中有一位可以编码预测那就比较理想了,但是对于比较老的架构这不是一个选择,所以使用约定来代替( 比如预测后向分支会执行,前向分支不会执行). 更重要的是这种方法需要编译器足够聪明做出正确的猜测,对于循环来说比较容易但是其他分支比较难.

另外的选择是处理器在运行时做出猜测, 通常由片上的分支预测表实现, 它包含最近分支的地址和一位用来表明每个分支上次是否 分支成功. 事实上, 大多数处理器使用两位, 因此一次偶尔没有分支不会影响通常分支预测( 对于循环边界很重要 ). 当然动态分支预测表会占用宝贵的片上空间,但是分支预测的重要性值得这么做.

不行的是即使最好的分支预测技术有时候也会出错,对于深度流水线必须取消多条指令, 这叫做错误分支惩罚. Pentium-Pro/II/III 是个很好的例子, 它有超过12 阶段的流水线,因此错误分支惩罚是10-15 个周期. 即使是聪明的动态预测器90%  的预测是正确的, 这么高的错误分支惩罚意味着Pentium-Pro/II/III 损失30% 的性能. 也就是说Pentium-Pro/II/III 三分之一的时间没有在做有用的工作,只是在说”oops, 错误路径”. 现代处理器着重使用更多的硬件试图提高分支预测的正确率,但是即使最好的处理器也因为分支预测错误损失很多性能.

八、   带谓词的分支取消

条件分支问题如此多以至于最好能把它们全部取消,很明显if 语句不能从编程语言中取消,那么(编译后的)结果分支怎么可能取消呢? 答案依赖于分支使用的方法.

重新考虑上面的例子,五条指令中两条是分支,其中一个非条件分支,如果能够标识mov 指令在某些条件下才能执行,代码就会得到简化.

cmp a, 5       ; a > 5 ?

mov c, b       ; b = c

cmovle d, b    ; if le, then b = d

引进一条新的cmovle 指令,表示 条件move 如果小于或等于”. 这条指令像普通的一样执行,但是只有在条件为真的时候提交,这样的指令叫做谓词指令,因为它的执行由谓词控制.

使用新的谓词move 指令,两条代价非常高的分支指令从代码中取消,另外通过聪明的并且总是执行mov 指令然后有必要时进行覆写,这样可以提高代码的并行性, 路径12 可以并行执行, 导致50% 的性能提升(2 个周期而不是3). 更重要的是避免了做出错误预测并且承受巨大的错误预测惩罚的可能性也消除了.

当然,如果ifelse 代码块比较长,使用预测意味着比使用分支执行更多的指令,因为处理器必须执行两条路径的代码. 多执行一些指令避免分支是否值得是一个微妙的决定- 对于非常小和非常大的代码块决定很简单,但是对于中等大小的代码块优化器必须做出复杂的权衡.

Alpha 架构最先加入条件move 执行,随后MIPS SPARCx86 也加入了. 对于IA64,Intel 几乎所有指令都加入谓词指令期望减低内部循环带来的分支问题, 特别是分支不可预测的时候( 例如编译器和OS 内核). 有趣的是很多phonestablets 使用的ARM 是第一个带有完全谓词指令集的架构, 更加有趣的是早期的ARM 处理器只有很短的流水线,错误分支惩罚相对很小.

九、   指令调度,寄存器重命名和OoO

如果分支和长延迟指令在流水线中导致气泡, 也许这些空的周期可以用来做其他工作. 为了达到这一目的程序中的指令必须重新排序,这样的话一条指令等待的时候,其他的指令可以执行. 例如在上面的乘法例子中可以从程序向下比较远的位置选择一些指令放在两条指令中间.

两种方法可以做这件事情,一种是运行时硬件重排序. 处理器动态指令调度意味着分发逻辑必须向前看很多组指令,并且以最好的使用处理器功能单元的方式分发他们,顾名思义这种方法叫做乱序执行,简称OoO.

如果处理器乱序执行指令,需要关注这些指令之间的依赖关系,通过使用一组重命名的寄存器而不是架构中定义的寄存器可以比较容易处理( 依赖关系). 例如存放寄存器内容到内存的指令后面跟着把其他位置的内存内容加载到相同寄存器的指令,它们表示不同的值,不需要使用同一个物理寄存器,并且如果这些指令映射到不同的物理寄存器可以并行执行, 这就是OoO 的关键点. 处理器必须时刻保持指令和物理寄存器的映射关系,这个过程叫做寄存器重命名. 作为一个额外的好处,越多的真实寄存器从代码中获取更多的并行性成为可能.

所有这些相关分析、寄存器重命名和OoO 执行在处理器上增加了很多复杂逻辑,导致设计变得很难,芯片面积更大和更多的能量需求. 附加的逻辑是主要的能源消耗,因为晶体管总是在工作,其他的功能单元至少还有一些时间是空闲的. 也就是说软件需要重新编译才能获取新处理器架构带来的好处.

另外一种方法是编译器重新安排指令解决整个的问题( 叫做静态, 或编译时指令调度). 重排的指令流放入使用顺序多发射逻辑的简单处理器中,依赖于编译器提供了最好的指令流. 避免复杂的OoO 逻辑使得处理器设计变得非常简单、更少的能量消耗并且更小,意味着同样的芯片面积可以防止更多的核心( 或更多的cache).

编译器方法还有一些其他的优点- 比硬件看程序向前看的更远,可以推测多个路径而不仅仅是一个. 话又说回来,编译器没有特异功能不会所有的事情都做得完美, 没有OoO 硬件,流水线在编译器没有预测到cache 缺失的时候就会停顿.

大多数早期超标量处理器是顺序设计(SuperSPARC, hyperSPARC, UltraSPARC-I/II, Alpha 21064 & 21164, the original Pentium). 早期的OoO 设计样例是MIPSR10000, Alpha 21264POWER/PowerPC 产品线的一些扩展. 今天几乎所有的高性能处理器都是OoO 设计,UltraSPARC-III/IVPOWER6 是例外. 低功耗处理器ARM11, Cortex A8 and Atom 是顺序设计的,因为OoO 消耗大量能量获得的性能提升却很少.

十、   超智能的争论

有个问题必须明确OoO 逻辑是不是必须的, 或者没有它的情况下编译器是否能够把指令调度任务做的足够好,历史上称为brainiac vs speed-demon 争论, 这个简单的设计风格争论首次出现在 1993 Microprocessor Report editorial by Linley Gwennap ,因为Dileep Bhandarkar's Alpha Implementations & Architecture 而众所周知

Brainiac 设计指的是聪明机器终端使用大量的OoO 硬件试图避免所有的性能损失,即使使用上百万的晶体管和耗费大量的能量. 相反的,speed-demon 设计较简单较小,依赖于聪明的编译器并且希望可以从简单性带来的好处中获得指令级的并行. 历史上speed-demon 设计运行在比较高的时钟速度由于它们是简单的,但是现在不再是这种情况了,因为时钟速度主要受能量和热量问题限制.

很明显,OoO 硬件可以带来更多的指令级并行, 因为前阶段不能预测的事情在执行时是知道的. 另外来说,简单的顺序设计更小使用更少的能量,意味着在同样的芯片上放置更多的顺序核心,你更想选择哪一个:4 个强大的聪明核心,或者8 个简单的顺序核心

那个是更重要的因素目前正在激烈地争论. 通常来说在过去OoO 执行的优点和代价都有些夸大, 关于代价90 年代后期合适的流水线分发和寄存器重命名逻辑使得OoO 处理器的时钟速度可以和较简单设计的处理器进行竞争,最近几年聪明的工程师应经降低了OoO 执行的能量负载,只有芯片面积代价没有解决. 这里是对一些工程用处理器架构的测试,不幸的是OoO 执行在增加额外的指令级并行的影响让人失望,大概只有很少的提升,大约20%.OoO 执行也不能如愿的达到调度敏感的等级,即使在比较有竞争性的OoO 处理器上使用重编译仍然可以提高速度.

聪明争论过程中,许多厂商改变了他们的想法转换到另一面.

Figure 11 – Brainiacs vs speed-demons.

例如DEC 前两代Alphaspeed-demon ,第三代改为brainiacMIPS 与此类似.Sun 刚好相反, 最初的超标量是brainiac ,最近的设计改为speed-demon. 过去几年POWER/PowerPC 阵营也渐渐的远离brainiac 设计, 尽管所有的PowerPC 的保留站设计确实提供了不同功能单元之间OoO 执行,虽然每个功能单元内存是严格顺序排队执行的.

Intel 是所有之中最有趣的,现在x86 处理器因为x86 架构的限制没有选择只有一些brainiac 特性, Pentium-Pro/II/III 完全采用这种态度. 但是Pentium-4 尽可能向speed-demon 靠拢为了解耦x86 微架构,IA64 Intel 有采用智能编译器的方法,简单设计广泛依赖依赖静态调度. 面对Pentium-4 高耗能和发热问题,Intel 又一次重新使用较老的Pentium-Pro/II/III brainiac 设计生产出Pentium-M Core 系列.

不管选择那个路线,关键问题依然存在- 通常的程序不会从并行中得到很多好处.4 发射超标量处理器每个周期需要4 条不相关指令并且所有的相关和延迟得到满足. 实际中这些几乎不可能,特别是加载延迟有34 个周期. 当前现实中主流应用的指令级并行限制在每周期大约2 条指令. 特定类型应用比如科学代码有更多的并行性,但是不代表主流应用. 还有一些类型的代码比如指针跟踪甚至每周期1 条指令都是非常困难的. 对于这些程序关键问题是内存系统.

十一、   X86 是什么样的?

X86 适合上面的哪个位置呢,IntelAMD 如何使30 多年的架构保持竞争力的?

最初的Pentium ,超标量x86, 是一个令人困惑的工程品,很明显最大的问题是复杂和庞大的x86 指令集. 复杂的地址模型和最少数目的寄存器意味着很少的指令可以并行执行因为潜在的相关.x86 阵营为了和RISC 竞争,他们必须找到包装x86 指令集的方法.

NexGen Intel 的工程师几乎同时独立的发明了解决方法,那就是动态解码x86 指令形成简单的像RISC 一样的微指令, 它们可以使用快速的、RISC 风格的寄存器重命名OoO 超标量核心执行. 微指令通常叫做uops(micro-ops 的简写). 大多数x86 指令解码成1,23uops, 复杂的指令会更多一些.

对于这些解耦超标量x86 处理器,由于x86 架构32 模式只有少的可怜的8 个寄存器(64 位模式增加了另外8 个),寄存器重命名至关重要. 这和RISC 架构不同,它们提供较多的寄存器重命名影响较小. 然而,使用聪明的寄存器重命名RISC 所有的技巧在x86 世界实现了,只有两个高级的静态指令调度 ( 因为微指令隐藏在x86 层之下,对编译器是不可见的) 和使用大寄存器集避免内存访问除外.

基本的工作场景像这样

Figure 12 – “RISC x86” 解耦的微架构

所有的x86 处理器使用这种架构, 当然他们的内核流水线不同,功能单元也是如此,就像不同的RISC 处理器,但是把x86 转换成内部微指令的基本思想是一样的.

最有意思的类RISC 风格的x86 组是Transmeta Crusoe 处理器, 它把x86 转换成内部的VLIW 形式而不是内部超标量,并且使用软件在运行时完成转换,很像Java 虚拟机. 这种方法允许处理器自己成为一个简单的VLIW ,没有复杂的x86 解耦的解码转换和寄存器重命名,也没有超标量分发和OoO 逻辑. 基于软件的转换确实降低系统的性能相对硬件转换,但是结果是能够快速运行、冷却和使用比较少的能量的芯片. 600 MHz Crusoe 处理器的性能和500 MHz Pentium-III 在低功耗模式的性能相匹配,但是只使用很少的能量产生很少的热量. 对于laptop 和手持电脑是非常理想的选择,它们的电池生命非常重要. 现在x86 处理器为了低功耗改变了一些设计,例如Pentium-M 和它的下一代Core ,使得基于软件的Transmeta 风格的方法不再必要.

十二、   线程-SMT, 超线程和多核

正如上面提到的由于通常的程序本身不具有并行性严重地削弱了通过超标量执行实现的指令级并行性,因为这个原因运行现实中的软件时,即使最聪明的OoO 超标量处理器,同时结合聪明的和富有竞争性的编译器,平均每个周期执行不会超过2 条指令,这是受到加载延迟、cache 缺失、分支和指令之间相关等的综合影响. 同一个时钟周期发射多条指令,大多数周期被低并行度的代码分割,至多在很少的周期内充满( 全速运行).

如果程序内部不相关指令不能执行,还有另外的潜在不相关指令- 其他的程序( 或者同一个程序的其他线程). 同时多线程就是一种探索这种线程级并行的处理器设计技术.

这个思想也是使用有用的指令填充流水线中的空气泡, 只不过不是使用同一个程序后面的代码,这些指令来源于多个同时在一个处理器内核上执行的线程. 所以SMT 处理器好像是多个不相关的处理器,就像一个真正的多处理器系统一样.

当然真正的多处理器系统也是同时执行多个线程- 但是每个核心只有一个, 多核处理器也是如此, 它们在单个芯片上有多个处理器核心,但是和传统的多处理器不同. 与此不同,SMT 处理器使用一个物理处理器核心代表两个或多个逻辑处理器,SMT 在芯片空间、建造成本、能量使用、热量损耗上比多核心处理器更加有效率,当然多核处理器也可以在每个核上实现SMT 设计.

从硬件的角度看实现SMT 需要重复处理器中保存每个线程执行状态的部件, 如程序计数器、( 硬件) 架构可见的寄存器( 不是重命名寄存器)TLB 中的内存映射等. 幸运的是这些部件只占整个处理器成本的很少一部分,真正巨大和复杂的部件,例如解码和分发逻辑、功能单元和cache 都是在线程间共享的.

当然处理器必须跟踪在给定的时间点上那个指令和那个重命名寄存器属于那个线程,但是这些只会增加很少的核心逻辑复杂性, 大约比核心多出10% 的成本( 对于总的晶体管数和最终的产品成本是微不足道的) ,处理器能同时执行多个线程,增加了功能单元的利用率和每时钟周期的指令数.

SMT 处理器的指令流像这样:

Figure 13 –SMT 处理器的指令流

非常好! 可以用多个线程填充这些气泡,我们可以增加更多的功能单元,并且确实可以实现多指令发射,在一些情况下甚至可以提高单线程的性能( 例如特别是ILP 比较好的代码)

那么我们可以使用20 发射, 对吗? 不幸的是答案是否定的.

SMT 性能是一件极其复杂的事情, 首先SMT 的思想建立在多个程序同时执行或者一个程序中多个线程同时执行的基础之上, 从现有的多处理器系统的经验来看这些不总是正确的. 实际中至少对于桌面、laptops 和小服务器同时有不同的程序活跃的情况非常少, 最常见的是只有一个任务当前在使用一台机器.

一些应用,例如数据库系统、图像和视频处理、声音处理、3D 图形渲染和科学代码等确实有很高的并行性,并且容易利用,但是不幸的是大多数这些类型的应用没有编写成可以利用多处理器上的多线程( 能力). 另外许多源自自然的并行受限于内存带宽而不是处理器( 例如: 图像和视频处理、声音处理、大多数科学代码), 所以增加额外的线程或处理器没有多少帮助- 除非内存带宽也可以理想地增长. 更糟的是,其他很多应用例如web 浏览器、多媒体设计工具、语言解释器、硬件模拟器等等本身的并行性不足以利用多个处理器.

处理之外,SMT 设计中多线程共享一个处理器核心、一组cache 导致和真正的多处理( 或多核) 相比性能有很大降低.SMT 处理器的流水线中, 如果一个线程占满了其他线程需要的功能单元,它会阻塞所有其他线程,即使他们只会很少使用这个单元. 线程之间的平衡变得很关键,因此最有效利用SMT 的是有很大不同的代码的应用(这样线程不会总是竞争同样的硬件资源). 线程之间对cache 空间的竞争可能产生比一个线程独占cache 空间更坏的结果- 特别是应用的关键工作部分高度cache 大小敏感的时候, 例如硬件模拟器、虚拟机和高质量视频解码( 有大的运动预测窗口).

作为底线对于有些应用STM 性能可能比单线程和传统的线程上下文切换更差. 另外一方面,受限于内存延迟( 不是内存带宽) 的应用, 例如数据库系统和3D 图形渲染,SMT 显著地获得益处,主要是因为SMT 提供了有效利用cache 缺失造成的空闲时间的方法. 因此SMT 性能表现和特定的应用密切相关. 这对市场是个很困难的挑战,有时候比两个“真正 的处理器还快,有时候像两个功能不足的处理器,有时候甚至比一个处理器还差.

Pentium-4 是第一个使用SMT 的处理器,Intel 成为 超线程”, 它的设计允许两个同时线程( 尽快早期的Pentium-4 因为bug 禁用SMT). 对于不同应用Pentium-4SMT 加速比在-10%30% 之间. 结果导致Pentium-MCore 2 在转回智能设计的时候抛弃了SMT, 同时转向多核. 其他的SMT 设计大约同时取消了(Alpha 21464, UltraSPARC-V, 看起来SMT 已经完全过时了直到随着POWER5 归来, 它有两个两线程的SMT 设计的核心( 每核两个线程乘以每片两个核心=4 线程每片).IntelCore iCore i*2 也是两线程SMT, 作为低功耗的x86 处理器. 典型的4Core i 处理器每片8 线程.Sun 在线程级并行做的更加有竞争性, UltraSPARC-T1 提供8 个简单的顺序核心,每个有4SMT 线程, 每片共有32 的线程. UltraSPARC-T2 增加到每核8 个线程,UltraSPARC-T316 个核心总有128 个线程.

十三、   更多的核心或者更宽的核心

既然SMT 具有把线程级并行性转化为指令级并行性的能力,结合对于单线程性能有更多好处的ILP 友好的代码,你可能认为使用相等的SMT 宽度建造多核心处理器是更好的方法.

不幸的是没有那么简单. 看上面讨论的,非常宽的超标量设计使得芯片面积和时钟速度方面变得很差. 关键问题是多发射分发逻辑随发射宽度二次方的增长, 也就是说5 发射处理器的分发逻辑是4 发射设计的两倍,6 发射是4,7 发射是8 倍以此类推. 另外非常宽的超标量设计需要更多的寄存器文件和cache. 这两个因素不仅导致大小增加,更是增加了大量的电路设计,给时钟速度增加更多的限制. 所以10 发射核心比25 发射核心更大更慢,我们的20 发射SMT 设计有序电路设计的限制不能实现.

那么既然SMT 和多核心的益处如此一来目标应用,广泛的设计方法应该仍然对不同级别的SMT 和多核产生作用,让我们探索一些可能兴.

今天典型的SMT 设计意味着宽的执行核心和OoO 执行逻辑,包括多个解码器, 巨大和复杂的超标量分发逻辑等等. 典型的SMT 核心芯片面积很大,同样的芯片面积可以容纳几个简单的、单发射、顺序核心( 可以有或者没有基本的SMT). 事实上可能有半打小的简单的核心放在一个现代采用的OoO 执行超标量SMT 设计的芯片面积上.

既然指令级并行和线程级并行都会减少回报,并且SMT 可有效地把TLP 转化成ILP 但是宽的超标量OoO 设计会非线性增加芯片面积和设计复杂性, 很明显的问题是哪里才是最好的点呢? 核心多大的宽度才能在ILPTLP 之间做到好的平衡呢? 目前正在探索不同的方法.

Figure 14 – 设计极端: Core i*2 Sandy Bridge 对比UltraSPARC-T3"Niagara 3"

一个极端是IntelCore i*2 采用”Sandy Bridge”( 左上),4 个大的、宽的、4 发射、乱序、非常智能的核心组成( 在顶部, 下面是共享的L3 cache), 每个运行2 个线程,共有8 快速 线程. 另一种方法是SunUltraSPARC-T3 "Niagara 3" (右上)包含16 个更小、更简单、2 发射、顺序核心( 在上部和下部, 中间是共享L2 cache) 每个运行8 个线程, 共有128 个线程, 这些线程比Core i*2 的慢一些. 两个芯片大约都包含10 亿晶体管,大小比例和上面的差不多,可以看出简单的顺序核心确实小很多.

那个是更好的方法? 没有简单的答案- 很大程度上依赖应用, 对于有很多活跃的胆识受限于内存延迟的线程( 例如:数据库系统、3D 图形渲染) ,更简单的核心更好,因为大的/ 宽的核心花费大量时间等待内存. 大多数应用显然没有那么多的活跃线程,一个线程的性能更加重要,所以更少更大、更宽、更智能的核心更加合适.

当然两个极端之间也有更多的选择. 例如IBMPOWER7 采用中间策略使用84 线程SMT 设计,使用不那么极端的OoO 执行硬件. AMD 即将出产的”Bulldozer” 设计使用更加创新的方法,对于每对核心使用共享的类SMT 前端给由没有共享的类多核心整型执行单元,但是共享的类SMT 浮点单元组成的后端(提供数据), 这种方法模糊了SMT 和多核心的界限. 谁知道呢,也许将来会看到非对称设计,使用一个或两个大的、宽的、智能核心加上大量更小、更窄、更简单的核心. 想想一下如何给它们优化代码吧!IBMCell 处理器是第一个这样的设计,尽管小的、简单的核心和大的主核心的指令集不兼容,更像一个专门目的的协处理器.

十四、   数据并行-SIMD 矢量指令

除了指令并行,许多程序中还有另外一种并行来源- 数据并行. 思想是寻找一条指令并行处理一组值的方法而不是并行地执行一组 .

这种方法称为SIMD 并行,大多数时候称为矢量处理. 原来超级计算机带有很长的矢量使用很多矢量处理,因为超级计算机上运行的科学程序非常适合矢量处理.

目前矢量超级计算机被一个处理处理单元的通用CPU 代替了,那么为什么矢量处理又重新流行呢?

大多数情况,特别是图像、视频和多媒体应用,程序需要为一组相关的值执行同一条指令,通常是短的矢量. 例如,图像处理应用需要为一组8 位数字做加法,8 位数字代表像素的红、绿、蓝或 alpha (透明度)值.

Figure 15 –SIMD 矢量加法操作

每个第8 位进位不进位,那么32 位加法操作怎么处理呢, 并且当8 位全部充满( 全是1) 时不能作为0 处理,而是保持255 作为最大值 ( 叫做饱和度算术), 换句话说每个第8 位进位不向前进位,而是触发全1 的结果, 所以矢量加是一种经过调整的加法.

从硬件的角度看,增加矢量指令不是很困难- 使用现有寄存器并且许多情况下功能单元可以和现有的整数或浮点单元共享. 可以为字节增加其他打包和解包的指令以及为位掩码增加一些类谓词指令. 一组很少的矢量指令可以带来显著的加速比.

当让没有理由停止在32 位,如果恰好有64 位寄存器,(针对)浮点运行的架构通常会有,可以提供64 位矢量,因此并行性加倍(SPARC VISx86MMX 这么做的). 如果允许定义新的寄存器,它们要尽可能的加宽(SSESIMD 浮点增加8 个新的128 位寄存器,后来增长到16 个,在SSE2 中支持整数的SIMD 操作,随后使用AVX 增长到256 位宽.PowerPC AltiVec 提供32 个新的128 位寄存器,保持了PowerPC 比较独立的设计风格,甚至它的分支指令都有自己的寄存器). 寄存器也可以使用其他方法划分,比如作为16 位整数或浮点值. 例如使用AltiVec,4 路并行执行浮点乘- 加作为单个全功能流水线化得指令.

对于容易提取数据并行性的应用,SIMD 矢量指令可以产生令人惊讶的加速度, 最初的领域集中在图像和视屏处理, 但是适合的应用包括音频处理、语音识别、一些3D 图形渲染和很多科学程序. 对于其他应用例如编译器和数据库系统加速很小甚至没有.

不幸的是, 编译器很难从普通的源代码自动利用矢量指令,除了一些特殊情况. 关键问题是程序员倾向于编写序列化的程序,导致编译器很淡证明两个操作是独立可以并行的. 此领域在慢慢进步,但是现在程序必须手工重写以利用矢量指令( 科学计算中基于数组的简单循环除外)

幸运的是重写只是关键部分的很少的代码,集中于操作系统的图形、视频和音频库,会对大多数应用产生广泛影响. 今天大多数OS 已经加强了它们关键的库函数以利用矢量指令的高性能. 抽象胜利的又一次明证.

几乎所有架构加入了SIMD 指令扩展, 包括SPARC(VIS),x86(MMX/SSE/AVX), POWERPC(AltiVec)ARM(NEON) ,只有每种架构相对较新的处理器能够执行这些新指令. 但是导致一些向后兼容性问题,特别是因为它的SIMD 指令进化很随意(3DNow!,MMX,SSE,SSE2,SSE3,SSE4,AVX).

十五、   Caches 和内存架构

前面提到的,延迟对流水线处理器是个大问题, 并且从内存加载的延迟很糟糕,它占了几乎所有指令的1/4.

加载倾向于出现在代码序列的开始( 基本块) ,很多其他指令依赖于加载的数据,导致其他指令阻塞很难获得大量的指令集并行. 事情比看到的可能更加糟糕,实际中大多数超标量处理器每周期只能发射一条至多两条内存指令.

内存访问核心问题是由于一些固有的限制比如光速,构建快速的内存系统非常困难, 它们导致了信号传向RAM 和从RAM 传回的延迟,没有什么可以改变自然法则- 我们必须学会与之相处.

例如, 即使使用现在CAS 延迟是5SDRAM 主存的访问延迟大约是15 个内存系统时钟周期-1 个发送地址到芯片组( 北桥),1 个到达DIMM,RAS-to-CAS 延迟5 (假设1 一次缺页),CAS 延迟5 ,另外1 个把数据输出到DIMM 缓冲,1 个把数据送回芯片组最后1 个把数据送回处理器(e-cache). 多处理器系统上为了支持cache 相连可能需要更多的总线周期.

假设典型的400MHz SDRAM 内存系统(DDR2-800)2GHz 处理器,访问内存使用15*5=75CPU 的时钟周期,更糟的是2.4GHz 处理器是90 个周期,2.8GHz 处理器是105 个周期, 即使内存系统增加到666MHz(DDR3-1333,CAS 延迟是8),3.3GHz 处理器仍然等待105 个周期,4.0GHz 处理器是126 个周期

尽管DDR SDRAM 可以在信号的上升和下降沿传输数据( 双倍速率), 真正的内存系统时钟速度只有它的一半,它是控制信号的真正时钟速度. 所以DDR 内存系统延迟和非DDR 系统是一样的, 只不过带宽是双倍的.

内存延迟有一小部分是处理器和主板上的芯片组传输数据,增加位于处理器和芯片组之间的前端总线的速度可以显著降低此部分延迟(Pentium-4800MHz QDR,

PowerPC G5 1.25GHz DDR). 一种更好的方法是把内存控制器集成在处理器芯片上,2 个总线周期转化成更快的处理器周期.UltraSPARC-IiiAthlon 64 是这么做的最早的主流处理器, 现代设计都是用片上内存控制器的方法,Intel 采用这种方法比较晚,只有在Core iCorei*2 才开始把内存控制器集成到CPU.

不幸的是,DDR 内存和片上内存控制器只能做这么多- 内存延迟仍然是主要的问题,导致处理器和内存之间差距越来越大的问题有时候被称为内存墙. 尽管因为处理器时钟速度由于能量和发热的限制不再以原来的速率增长这个问题不那么重要了, 它仍然是硬件工程师面临的重要问题,.

内存延迟仍然是个大问题.

现代处理器试图使用cache 解决这个问题,cache 是处于或靠近处理器芯片的小且快速的内存类型,它的作用是保存小部分主存的copy. 当处理器请求特定部分的主存时,如果数据在cache 中,cache 可以比主存更快的提供数据

Cache 的意思是隐藏或保存东西的地方.

典型的处理器芯片上每个核心有一个小且快速的L1 cache, 大概8-64KB 大小,L2cache 更大在芯片上离的稍远( 几百K 到几MB) ,可能还有更大较慢的L3cache.

片上cache 外部cache 和主存组合在一起形成内存架构, 下一级比上一级更大但较慢. 内存架构的最底层是虚拟内存系统, 通过把页从硬盘移进内存和从内存移出到硬盘,它几乎可提供无限数量的主存.

  ( 图书馆比喻略去)

令人惊奇的是cache 工作的非常好- 主存系统看起来和L1cache 一样快和主存一样大. 现代L1cache 延迟只有24 个处理器周期, 是访问主存的几倍快, 几乎所有应用的cache 命中率可以达到90%, 所以90% 的内存访问只会使用几个时钟周期.

Cache 可以达到如此令人惊讶的命中率在于程序的工作方式, 大部分程序具有时间和空间局部性- 程序访问某个区域的内存,那么很可能在不久的将来还会重新访问相同的区域( 时间局部性), 同样的很可能还会访问附近的内存( 空间局部性). 通过把最近访问的数据保存在cache 中利用时间局部性, 通过一次性把数据从主存中成块的传输到cache 利用空间局部性,块中包含若干字节,程序cache.

从硬件的角度看,cache 像一个两列的表- 一列是内存地址,另一列是数据值的块( 记住每个cache 行是整块的数据,不仅仅是一个值). 实际中cache 只需要存储地址的高位部分,地位部分用于cache 内部地址的索引. 高位部分称为tag

tag 和表中的tag 匹配时就是命中, 合适的数据就会发送到CPU.

Figure 16 – Cache 查找

既可以用虚拟地址也可以用物理地址进行cache 查找,Each has pros and cons (like everything else in computing), 使用虚拟地址会导致问题因为不同的程序使用相同的虚拟地址映射到不同的物理地址-cache 必须在每次上下文切换时刷新. 使用物理地址意味着cache 查找时必须执行虚拟到物理地址的映射, 使得每次查找变慢. 通过的小技巧是使用虚拟地址作为cache 索引,物理地址作为tag. 虚拟到物理地址的映射可以和cache 索引并行进行,在tag 比较的时候已经准备好了. 这种策略叫做虚拟索引- 物理tag cache.

     现代处理器中不同级别cache 的大小和速度对性能至关重要, 最重要的是L1 cache. 一些处理器有较小的数据cache(Pentium-Pro/II/III & Pentium-4E 具有16k D-caches, 较早的 Pentium-4s and UltraSPARC-T1/T2/T3 更小只有8k), 大部分是32k 作为合适的点, 一些较大有64k (Athlon/Athlon 64/Phenom & UltraSPARC-III/IV). 对于这些caches ,加载延迟通常3 个周期, 有时更短(UltraSPARC-III/IV, Pentium-4 & UltraSPARC-T1/T2/T3 2 个周期) 或更长(Pentium-4E, Core i & i*2, Cortex A9 4 个周期). 加载延迟一个周期的改变看起来是小问题但可能压垮整个性能, 并且最终用户很难注意到或理解. 通常来说,对于指针间接代码,处理器加载延迟是最最重要的性能因素.

大多数现代处理器具有大的二级或三级片上cache, 在所有核心之间共享. 这个cache 也非常重要, 但是它的适合大小点严重依赖应用的类型和应用活跃工作集的大小.2M8M L3cache 之间的不同有些应用很难测量, 但是其他的应用有很大不同. 对于大多数现代处理器相对较小的L1cache 已经占据芯片面积的一半, 可以想象大的L2L3cache 占用多大的面积, 这还是使用最现代的芯片技术取得的最好结果. 通常L2/L3cache 如此的大以至于在芯片上可以看到, 相对比较杂乱的核心和内存控制器的逻辑晶体管来说,它显得很整齐.

十六、   Cache 冲突和相联度

理想化来说,cache 中应该保存最可能在将来使用到的, 但是cache 不是超自然的, 较好的近似是保存最近使用的数据.

不幸的是, 保存确实是最常最近使用的数据意味着从任何内存位置的数据应该可以放置在cache 任何的行.Cache 因此包含最常最近使用的nKB 的数据, 可以很大利用局部性, 但不幸的时对快速访问不合适- 访问cache 时需要检查可能匹配的每个cache, 对于包含几千行的现代cache 来说太慢了.

取而代之的,cache 只是包含从内存中特定地址的数据占有cache 中一个或几个位置. 因此访问时只需要做一个或几个检查,所以访问可以保持快速. 这种方法有些缺点- 意味着cache 不能保存绝对的最佳最近访问的数据集, 因为一些不同内存位置会映射到cache 中的同一个位置, 当两个这样的内存位置同时需要时,这种场景称为cache 冲突

Cache 冲突导致“病态的 的糟糕性能问题, 因为当程序重复访问映射到同一个cache 行的两个内存位置时,cache 必须不断从主存中保存和加载并且承受每次访问主存的长延迟( 记住是100 个时钟周期)( 带来的以你共享). 这种情况叫做“颠簸”,cache 没有取得任何东西只是在哪里重复- 明显的时间局部性和数据重用, 由于内存位置和cache 行简单化的映射cache 不能充分利用这种访问模型提供的局部性.

为解决这一问题更精确的cache 可以把数据放在几个不同的位置而不仅仅是单个位置. 数据可以在cache 中存放位置的数量称为相联度. 这个词来源于cache 查找是相联工作的- 也就是说内存中特性的地址和cache 的特性位置相联( 组相联cache 是一组位置).

像上面描述的,最简单最快速的cache 只允许内存的每个地址对应一个位置- 每片数据简单地映射到cacheaddress%size 位置, 只用关心地址的较低位,这叫做直接映射cache. 内存中任何两个低位相同的地址映射到cache 中相同的行导致cache 冲突.

Cache 中允许占用两个位置叫做2 路组相联, 同样的4 路组相联cache 允许4 个可能的位置对于给定的数据. 组相联cache 工作像直接映射,除了有多个表, 都是并行索引的, 每个表的tag 都比较检查是否其中的一个相匹配.

Figure 17 –4 路组相联cache

每个表或路可能还有标签位只有这样当有新的行进来时最近最少使用的路才被替换( 或者较快的近似方法)

组相联可以避免直接映射由于不幸的cache 冲突带来的问题, 增加更多的路数可能避免更多的冲突. 不幸的是, 相联度越大访问越慢, 因为每次访问有更多的比较. 尽管比较本身是并行进行的, 如果命中的话需要额外的逻辑选择合适的命中, 并且cache 需要更新每路德标识位. 需要更多的芯片面积, 因为cache 数据大部分被tag 信息占用而不是数据块, 并且需要额外的数据路径并行访问每个单独的路. 所有这些因素都会给访问时间带来负面影响, 因此2 路组相联比直接映射更慢更聪明,4 路和8 路也是更慢更聪明以此类推.

现代处理器中指令cache 通常高度相联,由于它的延迟可以被取和缓冲掩盖. 从另一方面来说, 为了保持地延迟数据cache 通常也是组相联但不是很高的度(Athlon/Athlon 64/Phenom & PowerPC G52, Pentium-Pro/II/III, UltraSPARC-III/IV, Pentium-4, UltraSPARC-T1/T2 & Cortex A8/A94, PowerPC G4e, Pentium-M, Core 2, Core i, Core i*28). 作为通向主存的重新排序的片上L2/L3cache 通常也有高的相联度, 尽管外部E-cache 有时为了实现容易扩展采用直接映射方式.

Cache 的概念扩展到软件系统, 例如主存作为文件系统的cache 加快文件I/O,WEB cache( 叫做代理cache) 在本地服务器上cache 远端web 服务器的内容. 对于主存和虚拟内存可以看是聪明的全相连cache ,像上面提到的理想cache 一样. 毕竟虚拟内存系统是由操作系统的智能软件管理的.

十七、   内存带宽和延迟

内存以块传输,cache 缺失是潜在阻塞处理器的主要事件,那么从内存中传输块的速度是很关键的. 内存系统的传输率称为带宽, 它和延迟有什么区别呢?

  ( 高速公路比喻略去)

对于内存系统延迟和带宽之间有微妙的平衡. 低延迟设计对指针间接代码较好,比如编译器和数据库系统, 面向带宽的系统有利于简单的线性访问模式, 例如图像处理和科学代码.

目前两个主要的内存技术是标准SDRAMRambus RDRAM 在这两方面稍有不同- 对于相同水平的芯片技术,SDRAM 有比较小的延迟,RDRAM 有较高的带宽. 这是由于RDRAM 内存系统的蛇形物理结构通过使用线路分割降低并行进入每个内存模块的信号反射, 而不是顺序直接进入每个模块, 导致RDRAM 运行在较高的时钟速度但是到每个内存模块的平均物理长度较长.

当然比较容易增加带宽,简单地增加内存存储体并且加宽总线可以成倍或成四倍增加带宽. 很多高端系统这么做增加带宽,但是也有缺点. 较宽的总线意味着较贵的主板,限制了RAM 在系统中的布局方式( 成对或4 个一组) 和配置方式.

不幸的是,延迟比带宽更难提高,正如谚语说的“不能收买上帝”. 尽管如此,最近几年内存延迟性能还是有些好的提高, 特别是异步时钟DRAM(SDRAM) 使用和内存总线一样的时钟.SDRAM 最大的优点是流水化内存系统,因为内部的时间点方面和SDRAM 芯片操作具有交叉结构暴露于系统并且可以被利用( 不太明白). 由于允许新的内存访问在当前内存访问之前开始,所以相对同步DRAM 减少了一些等待的时间, 同步DRAM 开始之前必须等待当前访问完成( 平均来说, 异步内存系统必须等待前一个访问传输一半才能开始新的请求, 通常是几个总线周期).

SDRAM 除了减少延迟还会增加带宽,因为内存系统的多个内存请求可以在任何时候以一种高效的全流水线的方式发出. 内存系统的流水化显著增加内存带宽-SDRAM 内存系统通常提供双倍或三倍的同步系统内存带宽, SDRAM 延迟只不过降低一点点.

随着DRAM 技术的提高内存墙还会持续存在呢?同时越来越多处理器内核的需求DRAM 的带宽会不会变得更高?或者不久我们可以在内存和延迟方面突破内存的瓶颈,使得处理器微结构和内核的数目不再是不同之处,而是内存系统和所有的问题相关?进一步观察一定很有趣.

 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 2

打赏作者

lilistudy

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值