搭建网站首页,做网站带微好吗,兰州小的网络公司,个人网站可以做淘宝客网站吗英文原文#xff1a;lwn.net#xff0c;翻译#xff1a;开源中国
[编辑的话: Ulrich Drepper最近问我们#xff0c;是不是有兴趣发表一篇他写的内存方面的长文。我们不用看太多就已经知道#xff0c;LWN的读者们会喜欢这篇文章的。内存的使用常常是软件性能的决定性因子lwn.net翻译开源中国
[编辑的话: Ulrich Drepper最近问我们是不是有兴趣发表一篇他写的内存方面的长文。我们不用看太多就已经知道LWN的读者们会喜欢这篇文章的。内存的使用常常是软件性能的决定性因子而如何避免内存瓶颈的好文章却不好找。这篇文章应该会有所帮助。
他的原文很长超过100页。我们把它分成了7篇每隔一到两周发表一篇。7篇发完后Ulrich会把全文发出来。
对原文重新格式化是个很有挑战性的工作但愿结果会不错吧。为了便于网上阅读我们把Ulrich的脚注{放在了文章里}而互相引用的超链接(和[参考书目])要等到全文出来才能提供。
非常感谢Ultich感谢他让LWN发表这篇文章期待大家在不久的将来都能写出内存优化很棒的软件。
1 概述
早期计算机曾经很简单。它的各种组件比如CPU、内存、大容量存储和网络接口都是一起开发的所以性能差不多。举个例子来说内存和网络接口提供数据的速度不会比CPU快多少。
这种情况随着计算机构造的固化和各子系统的优化慢慢地发生了改变。其中一些组件的性能开始落后成为系统的瓶颈。特别是大容量存储和内存子系统由于代价的原因它们的发展严重滞后了。
大容量存储的性能问题往往靠软件来改善: 操作系统将常用(且最有可能被用)的数据放在主存中因为后者的速度要快上几个数量级。或者将缓存加入存储设备中这样就可以在不修改操作系统的前提下提升性能。{然而为了在使用缓存时保证数据的完整性仍然要作出一些修改。}这些内容不在本文的谈论范围之内就不作赘述了。
而解决内存的瓶颈更为困难它与大容量存储不同几乎每种方案都需要对硬件作出修改。目前这些变更主要有以下这些方式:
RAM的硬件设计(速度与并发度)内存控制器的设计CPU缓存设备的直接内存访问(DMA) 本文主要关心的是CPU缓存和内存控制器的设计。在讨论这些主题的过程中我们还会研究DMA。不过我们首先会从当今商用硬件的设计谈起。这有助于我们理解目前在使用内存子系统时可能遇到的问题和限制。我们还会详细介绍RAM的分类说明为什么会存在这么多不同类型的内存。 本文不会包括所有内容也不会包括最终性质的内容。我们的讨论范围仅止于商用硬件而且只限于其中的一小部分。另外本文中的许多论题我们只会点到为止以达到本文目标为标准。对于这些论题大家可以阅读其它文档获得更详细的说明。 当本文提到操作系统特定的细节和解决方案时针对的都是Linux。无论何时都不会包含别的操作系统的任何信息作者无意讨论其他操作系统的情况。如果读者认为他/她不得不使用别的操作系统那么必须去要求供应商提供其操作系统类似于本文的文档。 在开始之前最后的一点说明本文包含大量出现的术语“经常”和别的类似的限定词。这里讨论的技术在现实中存在于很多不同的实现所以本文只阐述使用得最广泛最主流的版本。在阐述中很少有地方能用到绝对的限定词。 1.1文档结构
这个文档主要视为软件开发者而写的。本文不会涉及太多硬件细节所以喜欢硬件的读者也许不会觉得有用。但是在我们讨论一些有用的细节之前我们先要描述足够多的背景。
在这个基础上本文的第二部分将描述RAM随机寄存器。懂得这个部分的内容很好但是此部分的内容并不是懂得其后内容必须部分。我们会在之后引用不少之前的部分所以心急的读者可以跳过任何章节来读他们认为有用的部分。
第三部分会谈到不少关于CPU缓存行为模式的内容。我们会列出一些图标这样你们不至于觉得太枯燥。第三部分对于理解整个文章非常重要。第四部分将简短的描述虚拟内存是怎么被实现的。这也是你们需要理解全文其他部分的背景知识之一。
第五部分会提到许多关于Non Uniform Memory Access (NUMA)系统。
第六部分是本文的中心部分。在这个部分里面我们将回顾其他许多部分中的信息并且我们将给阅读本文的程序员许多在各种情况下的编程建议。如果你真的很心急那么你可以直接阅读第六部分并且我们建议你在必要的时候回到之前的章节回顾一下必要的背景知识。
本文的第七部分将介绍一些能够帮助程序员更好的完成任务的工具。即便在彻底理解了某一项技术的情况下距离彻底理解在非测试环境下的程序还是很遥远的。我们需要借助一些工具。
第八部分我们将展望一些在未来我们可能认为好用的科技。
1.2 反馈问题
作者会不定期更新本文档。这些更新既包括伴随技术进步而来的更新也包含更改错误。非常欢迎有志于反馈问题的读者发送电子邮件。
1.3 致谢
我首先需要感谢Johnray Fuller尤其是Jonathan Corbet感谢他们将作者的英语转化成为更为规范的形式。Markus Armbruster提供大量本文中对于问题和缩写有价值的建议。
1.4 关于本文
本文题目对David Goldberg的经典文献《What Every Computer Scientist Should Know About Floating-Point Arithmetic》[goldberg]表示致敬。Goldberg的论文虽然不普及但是对于任何有志于严格编程的人都会是一个先决条件。
2 商用硬件现状
鉴于目前专业硬件正在逐渐淡出理解商用硬件的现状变得十分重要。现如今人们更多的采用水平扩展也就是说用大量小型、互联的商用计算机代替巨大、超快(但超贵)的系统。原因在于快速而廉价的网络硬件已经崛起。那些大型的专用系统仍然有一席之地但已被商用硬件后来居上。2007年Red Hat认为未来构成数据中心的“积木”将会是拥有最多4个插槽的计算机每个插槽插入一个四核CPU这些CPU都是超线程的。{超线程使单个处理器核心能同时处理两个以上的任务只需加入一点点额外硬件}。也就是说这些数据中心中的标准系统拥有最多64个虚拟处理器。当然可以支持更大的系统但人们认为4插槽、4核CPU是最佳配置绝大多数的优化都针对这样的配置。在不同商用计算机之间也存在着巨大的差异。不过我们关注在主要的差异上可以涵盖到超过90%以上的硬件。需要注意的是这些技术上的细节往往日新月异变化极快因此大家在阅读的时候也需要注意本文的写作时间。这么多年来个人计算机和小型服务器被标准化到了一个芯片组上它由两部分组成: 北桥和南桥见图2.1。 图2.1 北桥和南桥组成的结构 CPU通过一条通用总线(前端总线FSB)连接到北桥。北桥主要包括内存控制器和其它一些组件内存控制器决定了RAM芯片的类型。不同的类型包括DRAM、Rambus和SDRAM等等要求不同的内存控制器。为了连通其它系统设备北桥需要与南桥通信。南桥又叫I/O桥通过多条不同总线与设备们通信。目前比较重要的总线有PCI、PCI Express、SATA和USB总线除此以外南桥还支持PATA、IEEE 1394、串行口和并行口等。比较老的系统上有连接北桥的AGP槽。那是由于南北桥间缺乏高速连接而采取的措施。现在的PCI-E都是直接连到南桥的。这种结构有一些需要注意的地方:
从某个CPU到另一个CPU的数据需要走它与北桥通信的同一条总线。与RAM的通信需要经过北桥RAM只有一个端口。{本文不会介绍多端口RAM因为商用硬件不采用这种内存至少程序员无法访问到。这种内存一般在路由器等专用硬件中采用。}CPU与南桥设备间的通信需要经过北桥在
上面这种设计中瓶颈马上出现了。第一个瓶颈与设备对RAM的访问有关。早期所有设备之间的通信都需要经过CPU结果严重影响了整个系统的性能。为了解决这个问题有些设备加入了直接内存访问(DMA)的能力。DMA允许设备在北桥的帮助下无需CPU的干涉直接读写RAM。到了今天所有高性能的设备都可以使用DMA。虽然DMA大大降低了CPU的负担却占用了北桥的带宽与CPU形成了争用。
第二个瓶颈来自北桥与RAM间的总线。总线的具体情况与内存的类型有关。在早期的系统上只有一条总线因此不能实现并行访问。近期的RAM需要两条独立总线(或者说通道DDR2就是这么叫的见图2.8)可以实现带宽加倍。北桥将内存访问交错地分配到两个通道上。更新的内存技术(如FB-DRAM)甚至加入了更多的通道。由于带宽有限我们需要以一种使延迟最小化的方式来对内存访问进行调度。我们将会看到处理器的速度比内存要快得多需要等待内存。如果有多个超线程核心或CPU同时访问内存等待时间则会更长。对于DMA也是同样。除了并发以外访问模式也会极大地影响内存子系统、特别是多通道内存子系统的性能。关于访问模式可参见2.2节。在一些比较昂贵的系统上北桥自己不含内存控制器而是连接到外部的多个内存控制器上(在下例中共有4个)。 图2.2 拥有外部控制器的北桥 这种架构的好处在于多条内存总线的存在使得总带宽也随之增加了。而且也可以支持更多的内存。通过同时访问不同内存区还可以降低延时。对于像图2.2中这种多处理器直连北桥的设计来说尤其有效。而这种架构的局限在于北桥的内部带宽非常巨大(来自Intel)。{出于完整性的考虑还需要补充一下这样的内存控制器布局还可以用于其它用途比如说「内存RAID」它可以与热插拔技术一起使用。}使用外部内存控制器并不是唯一的办法另一个最近比较流行的方法是将控制器集成到CPU内部将内存直连到每个CPU。这种架构的走红归功于基于AMD Opteron处理器的SMP系统。图2.3展示了这种架构。Intel则会从Nehalem处理器开始支持通用系统接口(CSI)基本上也是类似的思路——集成内存控制器为每个处理器提供本地内存。 图2.3 集成的内存控制器 通过采用这样的架构系统里有几个处理器就可以有几个内存库(memory bank)。比如在4 CPU的计算机上不需要一个拥有巨大带宽的复杂北桥就可以实现4倍的内存带宽。另外将内存控制器集成到CPU内部还有其它一些优点这里就不赘述了。同样也有缺点。首先系统仍然要让所有内存能被所有处理器所访问导致内存不再是统一的资源(NUMA即得名于此)。处理器能以正常的速度访问本地内存(连接到该处理器的内存)。但它访问其它处理器的内存时却需要使用处理器之间的互联通道。比如说CPU 1如果要访问CPU 2的内存则需要使用它们之间的互联通道。如果它需要访问CPU 4的内存那么需要跨越两条互联通道。使用互联通道是有代价的。在讨论访问远端内存的代价时我们用「NUMA因子」这个词。在图2.3中每个CPU有两个层级: 相邻的CPU以及两个互联通道外的CPU。在更加复杂的系统中层级也更多。甚至有些机器有不止一种连接比如说IBM的x445和SGI的Altix系列。CPU被归入节点节点内的内存访问时间是一致的或者只有很小的NUMA因子。而在节点之间的连接代价很大而且有巨大的NUMA因子。目前已经有商用的NUMA计算机而且它们在未来应该会扮演更加重要的角色。人们预计从2008年底开始每台SMP机器都会使用NUMA。每个在NUMA上运行的程序都应该认识到NUMA的代价。在第5节中我们将讨论更多的架构以及Linux内核为这些程序提供的一些技术。除了本节中所介绍的技术之外还有其它一些影响RAM性能的因素。它们无法被软件所左右所以没有放在这里。如果大家有兴趣可以在第2.1节中看一下。介绍这些技术仅仅是因为它们能让我们绘制的RAM技术全图更为完整或者是可能在大家购买计算机时能够提供一些帮助。以下的两节主要介绍一些入门级的硬件知识同时讨论内存控制器与DRAM芯片间的访问协议。这些知识解释了内存访问的原理程序员可能会得到一些启发。不过这部分并不是必读的心急的读者可以直接跳到第2.2.5节。
2.1 RAM类型
这些年来出现了许多不同类型的RAM各有差异有些甚至有非常巨大的不同。那些很古老的类型已经乏人问津我们就不仔细研究了。我们主要专注于几类现代RAM剖开它们的表面研究一下内核和应用开发人员们可以看到的一些细节。第一个有趣的细节是为什么在同一台机器中有不同的RAM或者说得更详细一点为什么既有静态RAM(SRAM {SRAM还可以表示「同步内存」。})又有动态RAM(DRAM)。功能相同前者更快。那么为什么不全部使用SRAM答案是代价。无论在生产还是在使用上SRAM都比DRAM要贵得多。生产和使用这两个代价因子都很重要后者则是越来越重要。为了理解这一点我们分别看一下SRAM和DRAM一个位的存储的实现过程。在本节的余下部分我们将讨论RAM实现的底层细节。我们将尽量控制细节的层面比如在「逻辑的层面」讨论信号而不是硬件设计师那种层面因为那毫无必要。
2.1.1 静态RAM 图2.6 6-T静态RAM 图2.4展示了6晶体管SRAM的一个单元。核心是4个晶体管M1-M4它们组成两个交叉耦合的反相器。它们有两个稳定的状态分别代表0和1。只要保持Vdd有电状态就是稳定的。当需要访问单元的状态时升起字访问线WL。BL和BL上就可以读取状态。如果需要覆盖状态先将BL和BL设置为期望的值然后升起WL。由于外部的驱动强于内部的4个晶体管所以旧状态会被覆盖。更多详情可以参考[sramwiki]。为了下文的讨论需要注意以下问题:一个单元需要6个晶体管。也有采用4个晶体管的SRAM但有缺陷。维持状态需要恒定的电源。升起WL后立即可以读取状态。信号与其它晶体管控制的信号一样是直角的(快速在两个状态间变化)。状态稳定不需要刷新循环。SRAM也有其它形式不那么费电但比较慢。由于我们需要的是快速RAM因此不在关注范围内。这些较慢的SRAM的主要优点在于接口简单比动态RAM更容易使
2.1.2 动态RAM
动态RAM比静态RAM要简单得多。图2.5展示了一种普通DRAM的结构。它只含有一个晶体管和一个电容器。显然这种复杂性上的巨大差异意味着功能上的迥异。 图2.5 1-T动态RAM 动态RAM的状态是保持在电容器C中。晶体管M用来控制访问。如果要读取状态升起访问线AL这时可能会有电流流到数据线DL上也可能没有取决于电容器是否有电。如果要写入状态先设置DL然后升起AL一段时间直到电容器充电或放电完毕。动态RAM的设计有几个复杂的地方。由于读取状态时需要对电容器放电所以这一过程不能无限重复不得不在某个点上对它重新充电。更糟糕的是为了容纳大量单元(现在一般在单个芯片上容纳10的9次方以上的RAM单元)电容器的容量必须很小(0.000000000000001法拉以下)。这样完整充电后大约持有几万个电子。即使电容器的电阻很大(若干兆欧姆)仍然只需很短的时间就会耗光电荷称为「泄漏」。这种泄露就是现在的大部分DRAM芯片每隔64ms就必须进行一次刷新的原因。在刷新期间对于该芯片的访问是不可能的这甚至会造成半数任务的延宕。相关内容请察看【highperfdram】一章这个问题的另一个后果就是无法直接读取芯片单元中的信息而必须通过信号放大器将0和1两种信号间的电势差增大。最后一个问题在于电容器的冲放电是需要时间的这就导致了信号放大器读取的信号并不是典型的矩形信号。所以当放大器输出信号的时候就需要一个小小的延宕相关公式如下 这就意味着需要一些时间时间长短取决于电容C和电阻R来对电容进行冲放电。另一个负面作用是信号放大器的输出电流不能立即就作为信号载体使用。图2.6显示了冲放电的曲线x轴表示的是单位时间下的R*C
与静态RAM可以即刻读取数据不同的是当要读取动态RAM的时候必须花一点时间来等待电容的冲放电完全。这一点点的时间最终限制了DRAM的速度。
当然了这种读取方式也是有好处的。最大的好处在于缩小了规模。一个动态RAM的尺寸是小于静态RAM的。这种规模的减小不单单建立在动态RAM的简单结构之上也是由于减少了静态RAM的各个单元独立的供电部分。以上也同时导致了动态RAM模具的简单化。
综上所述由于不可思议的成本差异除了一些特殊的硬件包括路由器什么的之外我们的硬件大多是使用DRAM的。这一点深深的影响了咱们这些程序员后文将会对此进行讨论。在此之前我们还是先了解下DRAM的更多细节。
2.1.3 DRAM 访问
一个程序选择了一个内存位置使用到了一个虚拟地址。处理器转换这个到物理地址最后将内存控制选择RAM芯片匹配了那个地址。在RAM芯片去选择单个内存单元部分的物理地址以许多地址行的形式被传递。它单独地去处理来自于内存控制器的内存位置将完全不切实际4G的RAM将需要 232 地址行。地址传递DRAM芯片的这种方式首先必须被路由器解析。一个路由器的N多地址行将有2N 输出行。这些输出行能被使用到选择内存单元。使用这个直接方法对于小容量芯片不再是个大问题但如果许多的单元生成这种方法不在适合。一个1G的芯片容量我反感那些SI前缀对于我一个giga-bit将总是230 而不是109字节将需要30地址行和230 选项行。一个路由器的大小及许多的输入行以指数方式递增当速度不被牺牲时。一个30地址行路由器需要一大堆芯片的真实身份另外路由器也就复杂起来了。更重要的是传递30脉冲在地址行同步要比仅仅传递15脉冲困难的多。较少列能精确布局相同长度或恰当的时机现代DRAM类型像DDR3能自动调整时序但这个限制能让他什么都能忍受 图2.7展示了一个很高级别的一个DRAM芯片DRAM被组织在行和列里。他们能在一行中对奇但DRAM芯片需要一个大的路由器。通过阵列方法设计能被一个路由器和一个半的multiplexer获得{多路复用器multiplexer和路由器是一样的这的multiplexer需要以路由器身份工作当写数据时候。那么从现在开始我们开始讨论其区别.}这在所有方面会是一个大的存储。例如地址linesa0和a1通过行地址选择路由器来选择整个行的芯片的地址列当读的时候所有的芯片目录能使其纵列选择路由器可用依据地址linesa2和a3一个纵列的目录用于数据DRAM芯片的接口类型。这发生了许多次在许多DRAM芯片产生一个总记录数的字节匹配给一个宽范围的数据总线。对于写操作内存单元的数据新值被放到了数据总线当使用RAS和CAS方式选中内存单元时数据是存放在内存单元内的。这是一个相当直观的设计在现实中——很显然——会复杂得多对于读需要规范从发出信号到数据在数据总线上变得可读的时延。电容不会像前面章节里面描述的那样立刻自动放电从内存单元发出的信号是如此这微弱以至于它需要被放大。对于写必须规范从数据RAS和CAS操作完成后到数据成功的被写入内存单元的时延当然电容不会立刻自动充电和放电。这些时间常量对于DRAM芯片的性能是至关重要的我们将在下章讨论它。另一个关于伸缩性的问题是用30根地址线连接到每一个RAM芯片是行不通的。芯片的针脚是非常珍贵的资源以至数据必须能并行传输就并行传输比如64位为一组。内存控制器必须有能力解析每一个RAM模块RAM芯片集合。如果因为性能的原因要求并发行访问多个RAM模块并且每个RAM模块需要自己独占的30或多个地址线那么对于8个RAM模块仅仅是解析地址内存控制器就需要240之多的针脚。在很长一段时间里地址线被复用以解决DRAM芯片的这些次要的可扩展性问题。这意味着地址被转换成两部分。第一部分由地址位a0和a1选择行如图2.7。这个选择保持有效直到撤销。然后是第二部分地址位a2和a3选择列。关键差别在于只需要两根外部地址线。需要一些很少的线指明RAS和CAS信号有效但是把地址线的数目减半所付出的代价更小。可是地址复用也带来自身的一些问题。我们将在2.2章中提到。2.1.4 总结如果这章节的内容有些难以应付不用担心。纵观这章节的重点有
为什么不是所有的存储器都是SRAM的原因存储单元需要单独选择来使用地址线数目直接负责存储控制器主板DRAM模块和DRAM芯片的成本在读或写操作结果之前需要占用一段时间是可行的
接下来的章节会涉及更多的有关访问DRAM存储器的实际操作的细节。我们不会提到更多有关访问SRAM的具体内容它通常是直接寻址。这里是由于速度和有限的SRAM存储器的尺寸。SRAM现在应用在CPU的高速缓存和芯片它们的连接件很小而且完全能在CPU设计师的掌控之下。我们以后会讨论到CPU高速缓存这个主题但我们所需要知道的是SRAM存储单元是有确定的最大速度这取决于花在SRAM上的艰难的尝试。这速度与CPU核心相比略慢一到两个数量级。
2.2 DRAM访问细节
在上文介绍DRAM的时候我们已经看到DRAM芯片为了节约资源对地址进行了复用。而且访问DRAM单元是需要一些时间的因为电容器的放电并不是瞬时的。此外我们还看到DRAM需要不停地刷新。在这一节里我们将把这些因素拼合起来看看它们是如何决定DRAM的访问过程。我们将主要关注在当前的科技上不会再去讨论异步DRAM以及它的各种变体。如果对它感兴趣可以去参考[highperfdram]及[arstechtwo]。我们也不会讨论Rambus DRAM(RDRAM)虽然它并不过时但在系统内存领域应用不广。我们将主要介绍同步DRAM(SDRAM)及其后继者双倍速DRAM(DDR)。同步DRAM顾名思义是参照一个时间源工作的。由内存控制器提供一个时钟时钟的频率决定了前端总线(FSB)的速度。FSB是内存控制器提供给DRAM芯片的接口。在我写作本文的时候FSB已经达到800MHz、1066MHz甚至1333MHz并且下一代的1600MHz也已经宣布。但这并不表示时钟频率有这么高。实际上目前的总线都是双倍或四倍传输的每个周期传输2次或4次数据。报的越高卖的越好所以这些厂商们喜欢把四倍传输的200MHz总线宣传为“有效的”800MHz总线。以今天的SDRAM为例每次数据传输包含64位即8字节。所以FSB的传输速率应该是有效总线频率乘于8字节(对于4倍传输200MHz总线而言传输速率为6.4GB/s)。听起来很高但要知道这只是峰值速率实际上无法达到的最高速率。我们将会看到与RAM模块交流的协议有大量时间是处于非工作状态不进行数据传输。我们必须对这些非工作时间有所了解并尽量缩短它们才能获得最佳的性能。
2.2.1 读访问协议 图2.8: SDRAM读访问的时序 图2.8展示了某个DRAM模块一些连接器上的活动可分为三个阶段图上以不同颜色表示。按惯例时间为从左向右流逝。这里忽略了许多细节我们只关注时钟频率、RAS与CAS信号、地址总线和数据总线。首先内存控制器将行地址放在地址总线上并降低RAS信号读周期开始。所有信号都在时钟(CLK)的上升沿读取因此只要信号在读取的时间点上保持稳定就算不是标准的方波也没有关系。设置行地址会促使RAM芯片锁住指定的行。CAS信号在tRCD(RAS到CAS时延)个时钟周期后发出。内存控制器将列地址放在地址总线上降低CAS线。这里我们可以看到地址的两个组成部分是怎么通过同一条总线传输的。至此寻址结束是时候传输数据了。但RAM芯片任然需要一些准备时间这个时间称为CAS时延(CL)。在图2.8中CL为2。这个值可大可小它取决于内存控制器、主板和DRAM模块的质量。CL还可能是半周期。假设CL为2.5那么数据将在蓝色区域内的第一个下降沿准备就绪。既然数据的传输需要这么多的准备工作仅仅传输一个字显然是太浪费了。因此DRAM模块允许内存控制指定本次传输多少数据。可以是2、4或8个字。这样就可以一次填满高速缓存的整条线而不需要额外的RAS/CAS序列。另外内存控制器还可以在不重置行选择的前提下发送新的CAS信号。这样读取或写入连续的地址就可以变得非常快因为不需要发送RAS信号也不需要把行置为非激活状态(见下文)。是否要将行保持为“打开”状态是内存控制器判断的事情。让它一直保持打开的话对真正的应用会有不好的影响(参见[highperfdram])。CAS信号的发送仅与RAM模块的命令速率(Command Rate)有关(常常记为Tx其中x为1或2高性能的DRAM模块一般为1表示在每个周期都可以接收新命令)。在上图中SDRAM的每个周期输出一个字的数据。这是第一代的SDRAM。而DDR可以在一个周期中输出两个字。这种做法可以减少传输时间但无法降低时延。DDR2尽管看上去不同但在本质上也是相同的做法。对于DDR2不需要再深入介绍了我们只需要知道DDR2更快、更便宜、更可靠、更节能(参见[ddrtwo])就足够了。
2.2.2 预充电与激活
图2.8并不完整它只画出了访问DRAM的完整循环的一部分。在发送RAS信号之前必须先把当前锁住的行置为非激活状态并对新行进行预充电。在这里我们主要讨论由于显式发送指令而触发以上行为的情况。协议本身作了一些改进在某些情况下是可以省略这个步骤的但预充电带来的时延还是会影响整个操作。 图2.9: SDRAM的预充电与激活 图2.9显示的是两次CAS信号的时序图。第一次的数据在CL周期后准备就绪。图中的例子里是在SDRAM上用两个周期传输了两个字的数据。如果换成DDR的话则可以传输4个字。即使是在一个命令速率为1的DRAM模块上也无法立即发出预充电命令而要等数据传输完成。在上图中即为两个周期。刚好与CL相同但只是巧合而已。预充电信号并没有专用线某些实现是用同时降低写使能(WE)线和RAS线的方式来触发。这一组合方式本身没有特殊的意义(参见[micronddr])。发出预充电信命令后还需等待tRP(行预充电时间)个周期之后才能使行被选中。在图2.9中这个时间(紫色部分)大部分与内存传输的时间(淡蓝色部分)重合。不错。但tRP大于传输时间因此下一个RAS信号只能等待一个周期。
如果我们补充完整上图中的时间线最后会发现下一次数据传输发生在前一次的5个周期之后。这意味着数据总线的7个周期中只有2个周期才是真正在用的。再用它乘于FSB速度结果就是800MHz总线的理论速率6.4GB/s降到了1.8GB/s。真是太糟了。第6节将介绍一些技术可以帮助我们提高总线有效速率。程序员们也需要尽自己的努力。SDRAM还有一些定时值我们并没有谈到。在图2.9中预充电命令仅受制于数据传输时间。除此之外SDRAM模块在RAS信号之后需要经过一段时间才能进行预充电(记为tRAS)。它的值很大一般达到tRP的2到3倍。如果在某个RAS信号之后只有一个CAS信号而且数据只传输很少几个周期那么就有问题了。假设在图2.9中第一个CAS信号是直接跟在一个RAS信号后免的而tRAS为8个周期。那么预充电命令还需要被推迟一个周期因为tRCD、CL和tRP加起来才7个周期。 DDR模块往往用w-z-y-z-T来表示。例如2-3-2-8-T1意思是 w 2 CAS时延(CL) x 3 RAS-to-CAS时延(t RCD) y 2 RAS预充电时间(t RP) z 8 激活到预充电时间(t RAS) T T1 命令速率 当然除以上的参数外还有许多其它参数影响命令的发送与处理。但以上5个参数已经足以确定模块的性能。在解读计算机性能参数时这些信息可能会派上用场。而在购买计算机时这些信息就更有用了因为它们与FSB/SDRAM速度一起都是决定计算机速度的关键因素。喜欢冒险的读者们还可以利用它们来调优系统。有些计算机的BIOS可以让你修改这些参数。SDRAM模块有一些可编程寄存器可供设置参数。BIOS一般会挑选最佳值。如果RAM模块的质量足够好我们可以在保持系统稳定的前提下将减小以上某个时延参数。互联网上有大量超频网站提供了相关的文档。不过这是有风险的需要大家自己承担可别怪我没有事先提醒哟。
2.2.3 重充电
谈到DRAM的访问时重充电是常常被忽略的一个主题。在2.1.2中曾经介绍DRAM必须保持刷新。……行在充电时是无法访问的。[highperfdram]的研究发现“令人吃惊DRAM刷新对性能有着巨大的影响”。根据JEDEC规范DRAM单元必须保持每64ms刷新一次。对于8192行的DRAM这意味着内存控制器平均每7.8125µs就需要发出一个刷新命令(在实际情况下由于刷新命令可以纳入队列因此这个时间间隔可以更大一些)。刷新命令的调度由内存控制器负责。DRAM模块会记录上一次刷新行的地址然后在下次刷新请求时自动对这个地址进行递增。对于刷新及发出刷新命令的时间点程序员无法施加影响。但我们在解读性能参数时有必要知道它也是DRAM生命周期的一个部分。如果系统需要读取某个重要的字而刚好它所在的行正在刷新那么处理器将会被延迟很长一段时间。刷新的具体耗时取决于DRAM模块本身。
2.2.4 内存类型
我们有必要花一些时间来了解一下目前流行的内存以及那些即将流行的内存。首先从SDR(单倍速)SDRAM开始因为它们是DDR(双倍速)SDRAM的基础。SDR非常简单内存单元和数据传输率是相等的。 图2.10: SDR SDRAM的操作 在图2.10中DRAM单元阵列能以等同于内存总线的速率输出内容。假设DRAM单元阵列工作在100MHz上那么总线的数据传输率可以达到100Mb/s。所有组件的频率f保持相同。由于提高频率会导致耗电量增加所以提高吞吐量需要付出很高的的代价。如果是很大规模的内存阵列代价会非常巨大。{功率 动态电容 x 电压2 x 频率}。而且提高频率还需要在保持系统稳定的情况下提高电压这更是一个问题。因此就有了DDR SDRAM(现在叫DDR1)它可以在不提高频率的前提下提高吞吐量。 图2.11 DDR1 SDRAM的操作 我们从图2.11上可以看出DDR1与SDR的不同之处也可以从DDR1的名字里猜到那么几分DDR1的每个周期可以传输两倍的数据它的上升沿和下降沿都传输数据。有时又被称为“双泵(double-pumped)”总线。为了在不提升频率的前提下实现双倍传输DDR引入了一个缓冲区。缓冲区的每条数据线都持有两位。它要求内存单元阵列的数据总线包含两条线。实现的方式很简单用同一个列地址同时访问两个DRAM单元。对单元阵列的修改也很小。SDR DRAM是以频率来命名的(例如对应于100MHz的称为PC100)。为了让DDR1听上去更好听营销人员们不得不想了一种新的命名方案。这种新方案中含有DDR模块可支持的传输速率(DDR拥有64位总线): 100MHz x 64位 x 2 1600MB/s 于是100MHz频率的DDR模块就被称为PC1600。由于1600 100营销方面的需求得到了满足听起来非常棒但实际上仅仅只是提升了两倍而已。{我接受两倍这个事实但不喜欢类似的数字膨胀戏法。} 图2.12: DDR2 SDRAM的操作 为了更进一步DDR2有了更多的创新。在图2.12中最明显的变化是总线的频率加倍了。频率的加倍意味着带宽的加倍。如果对单元阵列的频率加倍显然是不经济的因此DDR2要求I/O缓冲区在每个时钟周期读取4位。也就是说DDR2的变化仅在于使I/O缓冲区运行在更高的速度上。这是可行的而且耗电也不会显著增加。DDR2的命名与DDR1相仿只是将因子2替换成4(四泵总线)。图2.13显示了目前常用的一些模块的名称。 阵列频率总线频率数据率名称(速率)名称 (FSB)133MHz266MHz4,256MB/sPC2-4200DDR2-533166MHz333MHz5,312MB/sPC2-5300DDR2-667200MHz400MHz6,400MB/sPC2-6400DDR2-800250MHz500MHz8,000MB/sPC2-8000DDR2-1000266MHz533MHz8,512MB/sPC2-8500DDR2-1066 图2.13: DDR2模块名 在命名方面还有一个拧巴的地方。FSB速度是用有效频率来标记的即把上升、下降沿均传输数据的因素考虑进去因此数字被撑大了。所以拥有266MHz总线的133MHz模块有着533MHz的FSB“频率”。DDR3要求更多的改变(这里指真正的DDR3而不是图形卡中假冒的GDDR3)。电压从1.8V下降到1.5V。由于耗电是与电压的平方成正比因此可以节约30%的电力。加上管芯(die)的缩小和电气方面的其它进展DDR3可以在保持相同频率的情况下降低一半的电力消耗。或者在保持相同耗电的情况下达到更高的频率。又或者在保持相同热量排放的情况下实现容量的翻番。DDR3模块的单元阵列将运行在内部总线的四分之一速度上DDR3的I/O缓冲区从DDR2的4位提升到8位。见图2.14。 图2.14: DDR3 SDRAM的操作 一开始DDR3可能会有较高的CAS时延因为DDR2的技术相比之下更为成熟。由于这个原因DDR3可能只会用于DDR2无法达到的高频率下而且带宽比时延更重要的场景。此前已经有讨论指出1.3V的DDR3可以达到与DDR2相同的CAS时延。无论如何更高速度带来的价值都会超过时延增加带来的影响。DDR3可能会有一个问题即在1600Mb/s或更高速率下每个通道的模块数可能会限制为1。在早期版本中这一要求是针对所有频率的。我们希望这个要求可以提高一些否则系统容量将会受到严重的限制。图2.15显示了我们预计中各DDR3模块的名称。JEDEC目前同意了前四种。由于Intel的45nm处理器是1600Mb/s的FSB1866Mb/s可以用于超频市场。随着DDR3的发展可能会有更多类型加入。 阵列频率总线频率数据速率名称(速率)名称 (FSB)100MHz400MHz6,400MB/sPC3-6400DDR3-800133MHz533MHz8,512MB/sPC3-8500DDR3-1066166MHz667MHz10,667MB/sPC3-10667DDR3-1333200MHz800MHz12,800MB/sPC3-12800DDR3-1600233MHz933MHz14,933MB/sPC3-14900DDR3-1866 图2.15: DDR3模块名 所有的DDR内存都有一个问题不断增加的频率使得建立并行数据总线变得十分困难。一个DDR2模块有240根引脚。所有到地址和数据引脚的连线必须被布置得差不多一样长。更大的问题是如果多于一个DDR模块通过菊花链连接在同一个总线上每个模块所接收到的信号随着模块的增加会变得越来越扭曲。DDR2规范允许每条总线又称通道连接最多两个模块DDR3在高频率下只允许每个通道连接一个模块。每条总线多达240根引脚使得单个北桥无法以合理的方式驱动两个通道。替代方案是增加外部内存控制器如图2.2但这会提高成本。这意味着商品主板所搭载的DDR2或DDR3模块数将被限制在最多四条这严重限制了系统的最大内存容量。即使是老旧的32位IA-32处理器也可以使用64GB内存。即使是家庭对内存的需求也在不断增长所以某些事必须开始做了。一种解法是在处理器中加入内存控制器我们在第2节中曾经介绍过。AMD的Opteron系列和Intel的CSI技术就是采用这种方法。只要我们能把处理器要求的内存连接到处理器上这种解法就是有效的。如果不能按照这种思路就会引入NUMA架构当然同时也会引入它的缺点。而在有些情况下我们需要其它解法。Intel针对大型服务器方面的解法(至少在未来几年)是被称为全缓冲DRAM(FB-DRAM)的技术。FB-DRAM采用与DDR2相同的器件因此造价低廉。不同之处在于它们与内存控制器的连接方式。FB-DRAM没有用并行总线而用了串行总线(Rambus DRAM had this back when, too, 而SATA是PATA的继任者就像PCI Express是PCI/AGP的继承人一样)。串行总线可以达到更高的频率串行化的负面影响甚至可以增加带宽。使用串行总线后
每个通道可以使用更多的模块。每个北桥/内存控制器可以使用更多的通道。串行总线是全双工的(两条线)。
FB-DRAM只有69个脚。通过菊花链方式连接多个FB-DRAM也很简单。FB-DRAM规范允许每个通道连接最多8个模块。在对比下双通道北桥的连接性采用FB-DRAM后北桥可以驱动6个通道而且脚数更少——6×69对比2×240。每个通道的布线也更为简单有助于降低主板的成本。全双工的并行总线过于昂贵。而换成串行线后这不再是一个问题因此串行总线按全双工来设计的这也意味着在某些情况下仅靠这一特性总线的理论带宽已经翻了一倍。还不止于此。由于FB-DRAM控制器可同时连接6个通道因此可以利用它来增加某些小内存系统的带宽。对于一个双通道、4模块的DDR2系统我们可以用一个普通FB-DRAM控制器用4通道来实现相同的容量。串行总线的实际带宽取决于在FB-DRAM模块中所使用的DDR2(或DDR3)芯片的类型。我们可以像这样总结这些优势 DDR2 FB-DRAM DDR2FB-DRAM脚24069通道26每通道DIMM数28最大内存16GB192GB吞吐量~10GB/s~40GB/s 如果在单个通道上使用多个DIMM会有一些问题。信号在每个DIMM上都会有延迟(尽管很小)也就是说延迟是递增的。不过如果在相同频率和相同容量上进行比较FB-DRAM总是能快过DDR2及DDR3因为FB-DRAM只需要在每个通道上使用一个DIMM即可。而如果说到大型内存系统那么DDR更是没有商用组件的解决方案。
2.2.5 结论
通过本节大家应该了解到访问DRAM的过程并不是一个快速的过程。至少与处理器的速度相比或与处理器访问寄存器及缓存的速度相比DRAM的访问不算快。大家还需要记住CPU和内存的频率是不同的。Intel Core 2处理器运行在2.933GHz而1.066GHz FSB有11:1的时钟比率(注: 1.066GHz的总线为四泵总线)。那么内存总线上延迟一个周期意味着处理器延迟11个周期。绝大多数机器使用的DRAM更慢因此延迟更大。在后续的章节中我们需要讨论延迟这个问题时请把以上的数字记在心里。前文中读命令的时序图表明DRAM模块可以支持高速数据传输。每个完整行可以被毫无延迟地传输。数据总线可以100%被占。对DDR而言意味着每个周期传输2个64位字。对于DDR2-800模块和双通道而言意味着12.8GB/s的速率。但是除非是特殊设计DRAM的访问并不总是串行的。访问不连续的内存区意味着需要预充电和RAS信号。于是各种速度开始慢下来DRAM模块急需帮助。预充电的时间越短数据传输所受的惩罚越小。硬件和软件的预取(参见第6.3节)可以在时序中制造更多的重叠区降低延迟。预取还可以转移内存操作的时间从而减少争用。我们常常遇到的问题是在这一轮中生成的数据需要被存储而下一轮的数据需要被读出来。通过转移读取的时间读和写就不需要同时发出了。
2.3 主存的其它用户
除了CPU外系统中还有其它一些组件也可以访问主存。高性能网卡或大规模存储控制器是无法承受通过CPU来传输数据的它们一般直接对内存进行读写(直接内存访问DMA)。在图2.1中可以看到它们可以通过南桥和北桥直接访问内存。另外其它总线比如USB等也需要FSB带宽即使它们并不使用DMA但南桥仍要通过FSB连接到北桥。DMA当然有很大的优点但也意味着FSB带宽会有更多的竞争。在有大量DMA流量的情况下CPU在访问内存时必然会有更大的延迟。我们可以用一些硬件来解决这个问题。例如通过图2.3中的架构我们可以挑选不受DMA影响的节点让它们的内存为我们的计算服务。还可以在每个节点上连接一个南桥将FSB的负荷均匀地分担到每个节点上。除此以外还有许多其它方法。我们将在第6节中介绍一些技术和编程接口它们能够帮助我们通过软件的方式改善这个问题。最后还需要提一下某些廉价系统它们的图形系统没有专用的显存而是采用主存的一部分作为显存。由于对显存的访问非常频繁(例如对于1024×768、16bpp、60Hz的显示设置来说需要95MB/s的数据速率)而主存并不像显卡上的显存并没有两个端口因此这种配置会对系统性能、尤其是时延造成一定的影响。如果大家对系统性能要求比较高最好不要采用这种配置。这种系统带来的问题超过了本身的价值。人们在购买它们时已经做好了性能不佳的心理准备。继续阅读
第2节: CPU的高速缓存第3节: 虚拟内存第4节: NUMA系统第5节: 程序员可以做什么 – 高速缓存的优化第6节: 程序员可以做什么 - 多线程的优化第7节: 内存性能工具第8节: 未来的技术第9节: 附录与参考书目