时钟统一的概念及意义

  G80图形核心采用TSMC 90NM工艺制造,并采用全新概念的统一渲染架构,核心与外置分离的视频处理及输出单元共拥有高达680M个晶体管。核心代号为G80-300的GPU制成GeForce8800GTX,拥有多大128个Streaming

  类似于CPU,G80图形核心也有一层IHS保护,这是为了防止压坏脆弱的核心部分,NV还动用了夸张的12颗螺丝用做核心位置的固定(下图)。

□-NVIO,外置的视频处理及输出单元

  和过去不同,GeForce 8800系列还把视频处理及输出电路都从GPU中剥离出来,这颗芯片称为NVIO,提供了400MHz RAMDAC并支持Dual-Link DVI输出,TV Out 及HDCP功能。独立NVIO芯片是在现有工艺手段下提高G80的产能的有效方式之一。

   G80图形核心支持DDR1、DDR2、DDR3、DDR4这四种显存,不过综合成本和性能等因素后,G80图形核心最终选择了供货稳定的GDDR3。我们并没有发现8800GTX和8800GTS使用显寸有什么区别,他们都是编号为Samsung K4J52324DE-BC12的GDDR3颗粒,只是在默认频率的设置有所不同。

  我们知道,虽然NV驱动在软件运行SLI模式的启动,但是在效能损失相比采用SLI Bridge的形式多得多,因此依然坚持采用传统的SLI搭桥模式。在GeForce 8800GTS上,我们依然见到熟悉的一个MIO接口,但在GTX上面,却可以看到有两个,NV并没有解释如此设计的原因,我们猜想这可能在NF680i芯片组上物理卡提速所需。

G80——革命性全新图形架构

  作为DX10硬件的要求,G80图形核心具备Geometry Shader(几何着色器)的单元。G80图形核心取消了传统的绑定式纹理单元设计,而采用了不同比例的纹理拾取单元(Texture Address Unit,负责传统TMU的纹理拾取操作,一共有32个)和纹理过滤单元(Texture Filtering Unit,负责浮点纹理过滤,一共有64个)来适应未来3D程序发展的要求。

  G80图形核心的后端渲染单元(ROP)和显存控制部分也被彻底的重新设计,GeForce8800一共6个64bit的交错式显存控制器,总位宽为384bit,支持带抗锯齿的浮点高动态范围渲染。G80图形核心所进行的重大改进还有很多,下文我们将给大家详细讲解这些改进的目的。


G80的工作流程猜想图

  发布前,业内大多数则认为G80图形核心会选择保守的分离式架构,但G80图形核心最终是一个彻底的Unified Shaders架构。所谓统一渲染架构,最容易的理解方式就是Shader单元不再分离,显示核心不再为Shader类型不同而配置不同类型的Shader单元,所有的Shader单元都可以为需要处理的数据进行处理,不管是Pixel Shader数据还是Vertex Shader数据。而调配哪几组Shader单元负责处理什么数据或者进行什么样子类型的计算,则由一个被称为small sets of instructions(SSI)的部分来控制。这样在硬件上,设计者就无需为不同的着色引擎设计不同的执行单元,只要按照所对应的接口以及操作方式全部融为一体,这无疑节约了宝贵的硬件资源。

  作为一款面向未来的GPU架构,G80图形核心对新一代DirectX10 API的通用Shader架构给予了完整的原生支持。实际上,DX10并没有对硬件的Unified Shaders架构做出硬性要求,只是在API层上将不区分各种Shader。虽然现在DX10的Vertex Shader、Pixel

  在很多时候如果我们处理的一个场景是以Pixel Shader计算为主,Vertex Shader计算仅占一小部分,如果采用分离Shader设计的显示核心,就可能存在Vertex Shader计算单元空闲,但Pixel Shader单元过渡计算的现象。同样的也可能存在Pixel Shader数据比较少,但Vertex Shader计算数据过多的问题。这就往往造成了一方面着色单元闲置,另一方着色单元资源紧缺的情况。


目前的分离式渲染架构实际上浪费了不少硬件资源


采用统一Shader后所有的渲染单元都可以得到充分利用

  像游戏《上古卷轴4:埋没》中常见的大树林或者草丛,树叶是由庞大数量的多边形构成,对GPU的顶点渲染提出了严酷的要求,这时候并不需要太多像素操作。统一渲染架构显示了它智能的一面,不受限制地使用纹理资源,并可以使用任意长度的着色指令,游戏速度即时提升了。

  毫无疑问,GPU架构的革新也带来直接的速度提升。通常的做法是增加GPU内部的渲染管线(Pipeline)来获得更多的计算能力,但G80图形核心的实现方式截然不同。

Shader时代图形渲染的基本操作是以各种指令为基本单位的,而这些操作可以分为两类:vector(矢量)和scalar(标量)。scalar是基本的单位,一个scalar操作可以包括一个mad或mul这种基本指令,被称为1D操作;一个vector就是n个标量,一般来说n = 4,因为现在最流行的RGBA中,3D刚好满足RGB操作,1D则适应Alpha通道的操作要求。因为指令是3D+1D,所以被称做4D操作,传统的GPU管线中ALU的操作能力就是这样。

  像R5XX和G7X的每个ALU都有能力单周期完成一个3D+1D(也就是3D矢量+1D标量指令,它们如果并行,被称做co-issue)或者2D+2D(一般是贴图坐标的处理, 因为一般是2d的坐标, 所以可以两个同时进行)总共4D这样的指令操作。而观察G80图形核心的着色单元设计,我们会发现最大的不同就是G80图形核心将3D+1D或者2D+2D这样执行能力为4D的“大”ALU拆分为单个执行能力为1D的“小”ALU,然后将这些ALU组成8个阵列(TCP),每个TCP拥有16个ALU,它们被称做1D Scalar Streaming Processors。通过独特的内部分频技术,这些着色单元阵列能以超过时钟频率2倍多的频率(1.35GHz)运行。

  G80的纹理单元也被重新设计。前面已经提到,G80取消了传统的纹理单元。将传统纹理单元(TMU)的功能拆分为两种单元-- 纹理拾取单元(Texture Address Unit)和纹理过滤单元(TexTure Filtering Unit),它们以跟核心频率相同的频率(575MHz)运作,以1:2的比例组成了新的纹理渲染阵列(Tex Array)。


G80图形核心的TCP阵列

  那么,这种“瘦身高频”的做法到底有什么好处呢?

1D单元遇到4D指令虽然要4个周期才能完成,但期间的延迟通过G80图形核心庞大的Thread资源完全可以掩盖。但是,shader中的操作数不可能每时每刻都为4D,更多的是常用的mul或add这种简单指令,这种操作对于传统4D流水线就有些划不来了,因为不管操作数是多少,单个周期内它们都要占据一个ALU的运算资源。而对于G80图形核心来说,不管是多么“细小”的操作,它的这种分散式流水线都有很强的适应能力。


G80的单个标量处理单元示意图

  另外,即使是标准的3D+1D的标量+矢量并行操作,co-issue也不是每时每刻都有效的,当前的各个GPU,在内部指令执行方面都有一定限制。比如,R4XX、R5XX要求程序对RGB+Alpha的co-issue进行配对优化;R580这种非对称架构需要着色器指令的顺序针对不同的过滤方式、不同的纹理格式使用不同的TMU、ALU指令配对比例;G7X要求nrm_pp指令紧挨着TMU指令执行..…等等等等。一旦出现不符合它们“胃口”的Shader顺序或者指令搭配方式,那么它们的理论指标就会大打折扣。


G80的单个纹理渲染阵列示意图

  在纹理渲染方面,G80也充分为未来进行了考虑。传统的GPU中TMU兼负纹理拾取和纹理过滤两重功能,而G80对这两种功能采用了分离设计,能最大限度的适应当前shader中需要对主内存进行操作的纹理拾取指令减少,而与HDR等相关的纹理过滤指令增多的情况。

  除了在底层执行单元效率上的彻底革新外,G80图形核心还为整个流水线引入了一道更加严格科学且富有效率的管理体系——Gigathread技术。实际上,G80图形核心整个架构动力的源泉就来自于此。

  当数据从游戏引擎传送至G80图形核心的流水线之后,会首先被送入一个叫做Thread Control Unit的地方,相当于G80图形核心整个流水线的仲裁和控制机构。这个类似于处理器的线程管理单元会将所有的着色单元的数据平行的拆成超过1000个平行的Threading(线程),并动态监测整个shader array流水线的工作状况。一旦它发现其下某个着色单元由于等待数据或工作完成而处于闲置状态,就会马上递交一个新的线程供其执行。这样让整个流水线保持极高的运行效率。

  Gigathread带来的另一个好处就是,能够减缓SIMD流水线中指令间的依存。在之前的G7X或R5XX架构中,由于SIMD流水线本身指令发射数有限,而它们能保有的threading数目又受到限制,流水线中的Alu算术指令和Tex纹理指令必须按照一定的比例配对,才能发挥出全部的运算能力。而G80上,由于Gigathread能硬件的将各个thread的stage(如Register file与各种控制的现况)保存下来,所以整个流水线对非规则指令的“忍耐”能力大大提高,即使是未经过优化排序的指令,也能被高效方便的执行。

  由于DX10将所有的Pixel和Vertex视做同等的Shader操作,因此这个管理器也负责进行高效的VS or PS的鉴别。它同时还会判定当前需要渲染场景的具体Vertex以及Pixel Shader的计算需求量,根据这个数据动态的调整所有通用着色单元中PS和VS的比例。

  Gigathread除了负责整个流水线的动态管理外,还担负着另一项重要使命,那就是对抗延迟。对于当今频率越来越高的GPU而言,如何对抗外部存储器延迟成为了一个关系到最终性能发挥的重要课题。一个以500MHz时钟频率运行的GPU,它的单个GPU时钟周期的长度在2ns以下。而我们可怜的外部存储器(显存),还在以200MHZ左右的速度工作。这就意味着,如果碰到一个Texture Fetch(纹理拾取指令,它需要GPU去读取显存里某个部位的数据),GPU从开始存取外部DRAM,到DRAM的第一个数据包实际送出之间,大概有十几到几十个ns的时间差,换算成GPU内部的时钟,就是几十乃至数百个周期的数据等待延迟,这种延迟会让整个流水线都陷入停顿,对于高速运行的GPU来说无疑是灾难性的。


完善的Threading管理体系可以隐藏存取显存动作所造成的延迟 

file(临时寄存器)里,然后立刻将另外的新Threading提交给这个Shader Unit执行,直到Texture Fetch完成为止,从而保证了整个流水线的效率不受DRAM延迟的影响。可以说,是Gigathread保证了整个流水线的运行效率,使它们得以免受延迟等因素的困扰。 

□-维持SIMD正常运转

  在之前的GPU架构中,除了运算能力之外,GPU本身对3D渲染流程的进行还有很多限制。比如,面对阴影作边缘柔和取样的时候,通过合理的使用if / else指令可以让Shader在不需要取样的像素直接跳过,节省出大量的Pixel shader资源,这就是动态分支(Flow Control)。

  对于大多数DX9规格的GPU而言,如何保持SIMD流水线的平行度是个很令人头痛的问题。因为SIMD流水线单指令,多数据流的特点,位于同一个Quad内的4条Pixel Shader着色管线都共享同一套指令分配体系,并处于同一个控制电路的控制之下。所以,在执行动态分支的时候,这个Quad内的4条Pixel Shader着色管线在每个时钟周期只有跑同一种指令,才能让所有ALU都保持工作。如果遇到不同的指令介入,那么因为指令发射器和控制单元共享的问题,实际无法在单周期完成。

  那么,对于要尽量维持并行能力的SIMD体系来说,这种状况就毫无意义了。G80图形核心的Gigathread架构能够为流水线维持尽量多的平行线程,从而能将进行分支时的Batch Size(可以理解为分支工作的区域)做得很小(G80图形核心最小能够做到对单个连续的4*4象素块进行分支处理而保持峰值性能)。这样就能尽量减少在一个象素块里遇到两条不同指令的可能性, 从而避免SIMD流水线的并行度被破坏。

NVIDIA于全新一代G80图形核心绘图核心中进一步强化3D游戏画面质量,称为Lumenex引擎。

   G80图形核心所解放的特性中,一个比较重要的就是带MSAA的高动态范围光照(HDR)。在R5XX VS G7X的时候,闹得沸沸扬扬的HDR和MSAA冲突就源自于此。DX9没有FP格式的Backbuffer(后端缓存,用于存储一些中间运算的结果),因此从NV40到G71,NV的硬件在ROP中提供HDR功能的同时干脆一直没有做MSAA。ATI看中了这点,它们改善了自己X1000系列产品的ROP,允许程序建立一个MSAA的FP16 render


采用了HDR渲染技术在开启MSAA后拥有惊人的画面效果

Buffer”(直译为“正交帧缓存”)的功能,现在公开的资料还难于了解其详细情况,不过从《The art of unix programming》对于Orthogonalized的定义中我们可以窥其一二:“在纯粹的正交设计中,任何操作均无副作用;每一个动作只改变一件事,不会影响其它”。这意味着,任何对于Frame Buffer的操作都是独立的,而不会影响其他操作的进行。直接消除了FP32

Anti-aliasing(MSAA)模式相约,但画面质量将有明显加强。CSAA采用了一种全新的算法实现了几何取样和覆盖取样的结合。对于通常的超级取样抗锯齿(SSAA)来说,每一个被采样的象素都将被进行更细分的采样和计算,但对同一物体材质内部将可能得到非常相近的色彩,这毫无疑问会造成资源浪费,所以现在流行的MSAA模式将首先会对像素的位置进行检测,对于材质内部将不进行处理,只发生一次采制读取。CSAA将MSAA颜色和深度值(Color/Z)的取样点位置延伸到了被取样的Pixel之内,颜色值在被取样的Pixel点中心得到, 并且将Color和Z数据存放在缓存中,这样,我们能在保存同样的Color/Z数据下得到更多的取样点。实际上16XCSAA只需要保存4份Color和Z数据,却可以得到16个sample points(取样点)。


CSAA和MSAA存放相同取样点信息分别所占用的显存空间

  相对于通常的采样模式,CSAA的好处是,在覆盖取样点数相同的情况下,它所要保存的Z数据(深度缓冲数据)和Color数据(颜色数据)将减小50%至75%,这大大节约了宝贵的显存带宽。不过,当游戏采用大量的Stencil运算(比如容积阴影)时,CSAA模式将不会被启动而被降至4xMSAA模式。

  QAA源自NVIDIA很早就有的一个名为的“五点梅花排列采样法”(Quincunx)抗锯齿算法。这个算法在上采样时对每个象素只产生两个子采样点,而下采样的时候却对5个子采样点进行了运算(通过从临近象素那里去“借用”子采样点的方法来获得多出的3个子采样点),这样以用2倍MSAA的性能损耗达到类似4AA的取样效果。NV这次在G80上改变了QAA的取样模板,将最大取样倍数提高到了16X,实现了更高的效果。


各类AA的取样信息和所占资源比较

  由于G80图形核心拥有高达64个浮点纹理过滤单元(Texture Filtering Unit),在各种纹理格式下都提供比现今任何一款G80图形核心高得多的性能,其各向异性过滤(AF)操作也变成了免费的功能。NV还特意取消了之前一直用使用的角度优化功能,来展示它的强大。

  G80图形核心的ROP(后端处理单元)也进行了重大改进,具备新型Early Z技术的GeForce 8800 GTX GPU可以相对于7900 GTX提升4倍的无用像素选择速度,最大限度的利用了带宽资源,配合高达384bit的外部存储器提供的超过80GB/S的带宽,足够免费为4倍MSAA这个级别的应用提供强力支持。

  技术部分中Lumenex引擎章节中,我们已经向大家介绍了NV GeForce 8800GTX引入了全新的抗锯齿模式。现在我们利用游戏《半条命2——失落的海岸线》来演示开启不同模式抗锯齿以及速度方面的影响。测试画面是由分辨率设置为的画面按照1:1截取。由于gif格式的固有特性,画质有部分损失。

  读者也可直接下载压缩包(),解压后用幻灯片模式浏览图片效果更佳。

SLI的32xAA选项,因此没有进行进一步的测试。

  在抗锯齿的画质和速度综合考虑,我们建议GF8800系列显卡用户在游戏中使用8xQAA模式。


G80提供了真正的全角度各向异性纹理过滤品质,是目前这个项目表现最为出众的显卡,不需任何加工。

、量子物理、视频引擎----G80的通用计算架构


当前GPU架构的通用计算潜力

  GPU通用计算(GPGPU)扩展,在NV30时代就曾被提起。CPU由于主要针对复杂控制和低延迟环境设计,而使得它的运算单元偏少,带宽也不足。相比之下,GPU本身就是一个SIMD(单指令多数据流)的“”,拥有众多的运算单元和强悍的外部存储器带宽,更加适于高并行性、高密集度的计算。随着GPU本身可编程特性和灵活度方面的改善和越发强大的浮点处理,越来越多的人开始用GPU做一些非绘制方面的事。另外,Cg、HLSL以及GLSL等实时的高级绘制语言的出现和发展也对改善GPU通用计算的编程环境起到了推波助澜的作用。

  ATi R5XX系列发布后,因为它宽松的编程环境,这个词语更多的出现在各大媒体的报道中。GPU的通用计算,从长远来看是GPU功能扩展的另一个要点,也是下一轮GPU竞赛热点功能。现在,挟DX10余威的G80又能在通用计算领域又将代来一次飞跃。统一渲染流水线架构、多达上千个线程的高平行度流水线、32位精度的无损浮点象素混合、强化的Render Target多目标输出、几乎无损的动态分支和循环shader操作、整数的支持...这一切都为它的通用计算能力打下了良好的基础,让我们来看看G80目前已经列在纸上的通用计算技术有哪些。

  由于游戏的发展,游戏引擎中的物理效果已经越来越引起开发商和玩家的兴趣。能在一个拥有真实物理效果的游戏场景里驰骋肯定是每个玩家的愿望。目前游戏里的物理运算一般都由CPU负责,但物理运算所消耗的大量浮点处理能力并不适合于CPU,将它从游戏里剥离出来已经是大势之趋。NV这次又占得先机,为G80图形核心引入了名为“Quantum”的GPGPU(GPU通用化计算)物理加速技术。G80图形核心将会是业内第一个支持3D图形和视频处理以外功能的GPU。


基于SLI架构的物理渲染流程

□-CUDA通用计算架构


G80的多线程通用计算管线构成

  CUDA是Compute Unified Device Architecture的缩写,直译就是通用计算架构。CUDA是一项新的基础计算体系,它不但支持图形芯片模拟物理计算,也加入了GPU的首个C编译开发环境。
实际上,CUDA提供了一个基于C语言的底层函数库, 可以直接使用一些D3D或者OpenGL不包含的特性。这项技术能让GPU的芯片核心同步协调地进行通用计算,速度比传统方式大有提升。另一方面,首个面向GPU的C编译开发环境让开发者拥有了新的解决方案,一些复杂的计算例如产品设计、数据分析、技术处理、游戏物理应用等方面现在都是游刃有余。

  GeForce 8800目前已经应用到这项技术,NVIDIA未来的Quadro专业图形解决方案也会随之而来,CUDA打破了传统的GPU处理数据方式的局限性,让GPU的核心能联合、同步地共享数据。


G80基于并发多线程的通用计算

Reduction(高清影讯除噪声)及HD edge enhancement( 高清影讯边缘强化) 。难怪在HQV高清影像测试中,拿下现今独立图形卡最高分。

3.0只允许16个);材质texture格式变为硬件支持的RGBE格式,其中的"E"是Exponent的省略,是RGB共同的说明,这在HDR的处理上有很大的作用,摒弃了以往需要专门decoding处理HDR渲染的流程。 另外,对于纹理的尺寸Shader Model4.0也有惊人的提升,的最高纹理分辩率比原先最高的分辩率要高出4倍。G80图形核心对以上规格都给予了完整的硬件支持。


带几何着色器和流输出的流水线架构图

  原来的Vertex Shader和Pixel Shader只是对逐个顶点或像素进行处理,而新的Geometry Shader可以批量进行几何处理,快速的把模型类似的顶点结合起来进行运算。虽然其操作不会象Vertex Shader那样完整,只是处理器单个顶点的相关函数操作,但是这种操作却可以确定整个模型的物理形状。这将大大加速处理器速度,因为其它Shader单元将不再去需要判定数据所存在的位置,而只是需要简单的为特定区域进行操作就可以了。

  Geometry Shader可以把点、线、三角等多边形联系起来快速处理、同时创造新的多边形,在很短时间内直接分配给其他Shader和显存而无需经过CPU,烟雾、爆炸等复杂图象不再需要CPU来处理。从而极大的提高了CPU速度和显卡速度。游戏图象中可以出现许多精细场景,如不锈钢茶壶上清楚的反射出周围物体、超精细的人物皮肤等。

  为了最大程度的发挥Geometry Shader的威力,DX10硬件还专门设置了一个名为流输出层(Stream Output State)的部件来配合它使用。这个层的功能是将Vertex Shader和Pixel Shader处理完成的数据输出给用户,由用户进行处理后再反馈给流水线继续处理。我们可以通过Stream Out把GPU拆成两段,只利用前面的一段几何运算单元。对某些科学研究,也许可以通过stream out来利用GPU的数学运算能力,等于在CPU之外又平白多得了一个数学协处理器。举个例子,Doom3常用的Stencil shadow,因为CPU负担很重,广受批评。但是因为GS可以计算轮廓线, 还可以动态插入新的多边形,有了Stream out之后,Shadow volume的生成就可以放到GPU端进行,实现Stencil shadow的硬件化,这将大大降低CPU占用。

特别章节:如何组装适合你的G80系统

组装一台合适的G80系统并不是一件容易的事情,在接下来的篇章我们陆续向大家提供一些建议。

  在主板平台的选择方面,我们将在这里省去相关的章节,因为下周我们将发布全面的有关NF680i及975X等系列芯片组的横向评比测试,敬请留意。

  在nVIDIA给我们的技术文档中,清楚的标明了新显卡对电源的要求,GF8800GTX要求使用450W和以上功率电源,12V实际电流应达30A;GF8800GTS要求使用400W或更大功率电源,12V实际电路应达26A。除此之外,NV还提供了推荐GF8800GTX用户使用的电源名单(如下图)。在这份列表中,除Zippy、Silverstone和Thermaltake外,其余品牌并未于大陆开展销售。值得强调的是,列表中仅有Zippy PSL-6850P通过了SLI认证且正在销售,但是符合认证的应该是型号后缀为(G1)-PCIE4的产品,目前我们手中的样品还只是配备2个PCIe电源接口。

下一页,我们将会通过实际测试来说明问题。

EXP观点:NVIDIA进行的内存、主板,电源等多项认证不过是其牟利和彰显影响力的表现。

  特别说明,下文录得的所有数据皆为整机功耗数值,单位瓦特。测试中,我们让OC 1.500V的C2D E6600满载时整机功耗就可达265W,但是用户的实际运用中CPU和GPU同时达满载的情况不多,在不同操作环境中会有不同的负载情况出现(对CPU/GPU需求波动),这也是为什么整机功耗不能单纯依靠理论值累加的原因。那么我们该如何选择我们所需的电源?

  在完全标准的状态下(即不进行任何超频动作),使用的全汉300W和康舒400W电源都没有出现问题,可以看到,海韵Power Angle录得的最大功耗读数也仅仅是240W而已。对于“循规蹈矩”的用户,市面上足功率的300~400W电源就可提供丰余的动力;对于超频玩家,实际情况有些不同。

  除了上面的测试,我们还让CPU和8800GTX负载最大化(分别运行Superpi和ATi tool小球模式,并指派不同CPU核心),此时录得的最大读数也仅仅是372W而已。值得注意,负载时的X1950XTX能耗与NV旗舰8800GTX非常接近,高于剩下的其他NV显卡。

另:电源的转换效率也要充分考虑。

对于GF8800GTX SLI用户而言,Silverstone SST-ST60F不失为第一选择,这是我们目前从市面上唯一可以直接购买到配备4个PCIe电源接口(实际上应该是5个)的模组化电源,它的额定功率为600W,市场售价为¥1300(),上文GTX SLI系统的测试就使用了这个电源来完成的。


是否担心你的CPU成为瓶颈?

  作为史上最强大的娱乐型显卡,GeForce880GTX是否会因为CPU瓶颈而限制发挥了?我们用8款游戏的成绩进行测试。

Heroes对于CPU的依赖程度不高。可以看出新一代显示卡发挥不多受限于CPU效能,这也和Intel Core 2 Duo处理器强大的游戏效能不无关系。

  以GF8800GTX为例,它的卡长达26.8cm,这比大部分标准ATX主板的宽度值都要大,在选购机箱的时候如果不多加心眼,难免就会遇到尴尬的情况。

  映众(Inno3d)作为香港的一个AIC,也是NVIDIA的官方发布伙伴(Official Launch Partner)之一,其显卡统一采用NV图形核心。这次映众在第一时间就为我们提供了测试所需的样品,对于专题的顺利完成功不可没。目前映众GeForce8800 GTX和8800GTS的售价分别为人民币5999元和4999元。

  讯景(XFX)GeForce8800 GTX和8800GTS再次重演天价本色,成对销售价为19999和16999(成对购买送七盟 ST-750EAJ 750W电源),不过这次和过去7950GX2的情况不同(XFX显卡出厂BIOS就让显卡超频),讯景不得不遵守NVIDIA的规则,除了一年保换的优质服务外之外,这厮天价不知有谁愿意“买单”。

  或许是仓促推出驱动的原因,现有的驱动只能侦测到核心的温度表现,环境温度和保护温度设置仍然无效。在针对GF8800系列显卡的96.94beta驱动上,我们已经看不到传统的控制面版,尚不清楚NV是否会学习ATi的“陋习”强制用户使用全新的控制面板。

  默认情况下,GeForce8800系列显卡的风扇是以59%转速进行运转,噪音很小,要进行转速设置必须安装驱动之外的nTune软件。我们使用ATi Tool 0.25 beta16的小球模式让GPU处在高负载的状态下。

  在实际的散热效果方面,全新的GeForce8800系列显卡的表现让人满意,即便是全负载下的GeForce8800GTX,它的核心侦测读数也不过是86℃而已,如果能手动将风扇调至全速,那么温度将会下降接近10℃(8800GTS达15℃),效果非常显著,只是此时风扇噪音相较之下增加不少。由于nTune支持的风扇转速模式是从0~100%,用户可自行寻找风扇转速(等同于噪音)和散热效果的平衡性。考虑到目前室温较低和裸机测试等因素,我们还是建议用户配备散热良好的机箱。

  虽然我们这次没有提供详细的实际噪音读数,但是测试中还是可以直观的发现GeForce8800系列显卡要较Radeon X1950XTX更安静一些,后者在满载时,软件读取的核心温度在83℃左右。

  我们将显卡散热器调至全速(参照上一页)来进行超频,最终把GeForce8800GTX显卡核心/显存频率从默认的575/1800MHz成功超频至650MHz/2080MHz。超频幅度很小,这和BIOS的限制也有一定关系,在G80发布之前,NV就勒令任何厂商不得擅自将显卡超频。除了驱动程序自带的超频选项之外,部分玩家还喜欢用ATi Tool进行超频。

  测试所用的主板是NVIDIA NF680i参考版,除7950GX2外,其他显卡会自动启用“Linkboost”总线加速功能。我们将在稍晚时候放出这款主板的评测时会作出更多介绍。为了让系统受CPU影响的可能性降低,我们将C2D E6600超频至400MHzX9,此时内存运行在同步1:1的DDR2-800 4-4-4-12 1T timing下。

  除了3Dmark系列进行了或等默认测试采用的分辨率外,其他测试的起步分辨率统一为并开启HDR或者抗锯齿设置。特别指出的是,我们的CPU已经被超频至400MHzX9了,而内存1:1同步运行在DDR2-800 4-4-4-12 1T timing下,其他选项默认。为贴近用户实际操作环境,板载的HDA声卡也被我们启动。

  测试时NV可提供的最新驱动为96.94 beta,这也是我们当前可以启用GeForce8800系列显卡的的唯一驱动(9日当天,NV又发布了G80专用的97.02),而对比的7950GX2使用最新的93.71驱动由于无法开启多GPU工作,成绩最终在96.94beta驱动下取得,这个驱动的分值要比93.71要高一些。

  测试所用的主板是NVIDIA NF680i参考版,除7950GX2外,其他显卡会自动启用“Linkboost”总线加速功能。我们将在稍晚时候放出这款主板的评测时会作出更多介绍。为了让系统受CPU影响的可能性降低,我们将C2D E6600超频至400MHzX9,此时内存运行在同步1:1的DDR2-800 4-4-4-12 1T timing下。

  除了3Dmark系列进行了或等默认测试采用的分辨率外,其他测试的起步分辨率统一为并开启HDR或者抗锯齿设置。特别指出的是,我们的CPU已经被超频至400MHzX9了,而内存1:1同步运行在DDR2-800 4-4-4-12 1T timing下,其他选项默认。为贴近用户实际操作环境,板载的HDA声卡也被我们启动。

  测试时NV可提供的最新驱动为96.94 beta,这也是我们当前可以启用GeForce8800系列显卡的唯一驱动(9日当天,NV又发布了G80专用的97.02)。在进行7900GTX SLI系统的测试时,NVIDIA提供的最新93.71驱动虽然具备SLI的开启选项,但是分值表现与单卡无异,最后我们使用前一个版本的官方正式驱动(91.47)完成测试。我们保留了7950GX2(使用96.94beta驱动)的成绩作为参考,由于样卡数量问题,最终无法加入成绩。对于NV SLI用户而言,93.71驱动应该直接删除,单卡用户不受此影响。

特别注明:我们漏了进行7900GTX SLI的英雄连测试。


参考报价由影驰(AIC)及蓝宝石(AIB)提供

  在比较单卡双核的7950GX2时,GeForce8800GTX的领先幅度在两成左右;GeForce8800GTS在和7950GX2的较量中没有什么优势,除了3DMark06和游戏英雄连微弱领先不到5%,大多数时候反倒落后8%。在目前价格一致的情况下,8800GTS唯一的优势一是对DX10的完全硬件支持,同时在游戏画质方面提供更大的保证。

  反观对手ATi X1950XTX,仅仅实在面对GeForce7900GTX时才难得见到一丝的胜利曙光,后者便宜足足人民币1000元,对比NV其他的情况,我想最好还是忽略不提。在新世代对比中,我们不得不遗憾的将ATi(AMD?)从神台上请下去,将掌握大权的胜利旗帜插在NVIDIA上面。

  从前文的成绩列图可以看到,GeForce 8800GTX SLI系统的游戏效能极其出众,除了游戏COD2外,所有游戏的FPS都超过100,不愧为骨灰级游戏玩家首选。

  作为首款完全符合Microsoft DirectX10的通用Shader架构图形处理器,G80顾及了效率与未来性,是让人惊叹的"革新结构的第一代作品",在接下来的全新Windows Vista操作系统及DX10游戏中,它的强大效能将首先爆发。围绕着强大的G80核心,制成的GeForce 8800GTX和GeForce 8800GTS两款显卡在发布后就为我们展现了威力,他们组成了当之无愧的王者组合。

  在上文冗长的测试中,GeForce8800 GTX领先对手旗舰产品X1950XTX达50%以上,还是首款在3DMark06基准测试中突破一万分的娱乐型显示卡。不论在3D图形处理的速度,面面质量,甚至是每瓦特性能,GeForce8800系列显卡都取得最出色的成绩,我们再次看到了NVIDIA的无与伦比。


唯一遗憾的是目前GeForce8800系列显卡仍然没有一个真正完善的驱动。

  在革命的架构背后,GeForce 8800系列显卡也有一些不足,问题集中在稍显仓促的驱动。除了无法运行倍受关注的Splinter Cell Double Agent,不少拥有HDR技术的游戏依然无法正常的开启抗锯齿效果,过去同样的问题也一度困扰着ATi,但愿这次黄仁勋(NVIDIA CEO)也能效仿ATi,对于率先解决让游戏支持HDR+AA问题的程序员作出奖励,或许能缩短这个进程所需要的时间。

  最后提醒各位GeForce8800显卡的玩家,在急着把显卡装进机箱时千万别忘了插上PCIe电源接口,和过去善良的警报不同,这次NVIDIA将喇叭的音量调得很大,别让可怕得“啸叫”把你吓个半死......enjoy yourself!!!

我在六年前的一个令人兴奋的时刻加入到LinkedIn公司。从那个时候开始我们就破解单一的、集中式数据库的限制,并且启动到特殊的分布式系统套件的转换。这是一件令人兴奋的事情:我们构建、部署,而且直到今天仍然在运行的分布式图形数据库、分布式搜索后端、Hadoop安装以及第一代和第二代键值数据存储。

从这一切里我们体会到的最有益的事情是我们构建的许多东西的核心里都包含一个简单的理念:日志。有时候也称作预先写入日志或者提交日志或者事务日志,日志几乎在计算机产生的时候就存在,同时它还是许多分布式数据系统和实时应用结构的核心。

不懂得日志,你就不可能完全懂得数据库,NoSQL存储,键值存储,复制,paxos,Hadoop,版本控制以及几乎所有的软件系统;然而大多数软件工程师对它们不是很熟悉。我愿意改变这种现状。在这篇博客文章里,我将带你浏览你必须了解的有关日志的所有的东西,包括日志是什么,如何在数据集成、实时处理和系统构建中使用日志等。

日志是一种简单的不能再简单的存储抽象。它是一个只能增加的,完全按照时间排序的一系列记录。日志看起来如下:

我们可以给日志的末尾添加记录,并且可以从左到右读取日志记录。每一条记录都指定了一个唯一的有一定顺序的日志记录编号。

日志记录的排序是由“时间”来确定的,这是因为位于左边的日志记录比位于右边的要早些。日志记录编号可以看作是这条日志 记录的“时间戳”。在一开始就把这种排序说成是按时间排序显得有点多余 ,不过 ,与任何一个具体的物理时钟相比,时间 属性是非常便于使用的属性。在我们运行多个分布式系统的时候,这个属性就显得非常重要。

对于这篇讨论的目标而言,日志记录的内容和格式不怎么重要。另外提醒一下,在完全耗尽存储空间的情况下,我们不可能 再给日志添加记录。稍后我们将会提到这个问题。

日志并不是完全不同于文件或者数据表的。文件是由一系列字节组成,表是由一系列记录组成,而日志实际上只是按照时间顺序存储记录的 一种数据表或者文件。

此时,你可能奇怪为什么要讨论这么简单的事情呢? 不同环境下的一个只可增加的有一定顺序的日志记录是怎样与数据系统关联起来的呢?答案是日志有其特定的应用目标:它记录了什么时间发生了什么事情。 而对分布式数据系统许多方面而言, 这才是问题的真正核心。

不过,在我们进行更加深入的讨论之前,让我先澄清有些让人混淆的概念。每个编程人员都熟悉另一种日志记录-应用使用syslog或者log4j可能写入到本地文件里的没有结构的错误信息或者追踪信息。为了区分开来,我们把这种情形的日志记录称为“应用日志记录”。应用日志记录是我在这儿所说的日志的一种低级的变种。最大的区别是:文本日志意味着主要用来方便人们阅读,而我所说明的“日志”或者“数据日志”的建立是方便程序访问。

(实际上,如果你对它进行深入的思考,那么人们读取某个机器上的日志这种理念有些不顺应时代潮流。当涉及到许多服务和服务器的时候,这种方法很快就变成一个难于管理的方式,而且为了认识多个机器的行为,日志的目标很快就变成查询和图形化这些行为的输入了-对多个机器的某些行为而言,文件里的英文形式的文本同这儿所描述的这种结构化的日志相比几乎就不适合了。)

我不知道日志概念起源于何处-可能它就像二进制搜索一样:发明者认为它太简单而不能当作一项发明。它早在IBM的系统R出现时候就出现了。数据库里的用法是在崩溃的时候用它来同步各种数据结构和索引。为了保证操作的原子性和持久性,在对数据库维护的所有各种数据结构做更改之前,数据库把即将修改的信息誊写到日志里。日志记录了发生了什么,而且其中的每个表或者索引都是一些数据结构或者索引的历史映射。由于日志是即刻永久化的,可以把它当作崩溃发生时用来恢复其他所有永久性结构的可信赖数据源。

随着时间的推移,日志的用途从实现ACID细节成长为数据库间复制数据的一种方法。利用日志的结果就是发生在数据库上的更改顺序与远端复制数据库上的更改顺序需要保持完全同步。Oracle,MySQL 和PostgreSQL都包括用于给备用的复制数据库传输日志的日志传输协议。Oracle还把日志产品化为一个通用的数据订阅机制,这样非Oracle数据订阅用户就可以使用XStreams和GoldenGate订阅数据了,MySQL和PostgreSQL上的类似的实现则成为许多数据结构的关键组件。

正是由于这样的起源,机器可识别的日志的概念大部分都被局限在数据库内部。日志用做数据订阅的机制似乎是偶然出现的,不过要把这种 抽象用于支持所有类型的消息传输、数据流和实时数据处理是不切实际的。

日志解决了两个问题:更改动作的排序和数据的分发,这两个问题在分布式数据系统里显得尤为重要。协商出一致的更改动作的顺序(或者说保持各个子系统本身的做法,但可以进行存在副作用的数据拷贝)是分布式系统设计的核心问题之一。

以日志为中心实现分布式系统是受到了一个简单的经验常识的启发,我把这个经验常识称为状态机复制原理:如果两个相同的、确定性的进程从同一状态开始,并且以相同的顺序获得相同的输入,那么这两个进程将会生成相同的输出,并且结束在相同的状态

这也许有点难以理解,让我们更加深入的探讨,弄懂它的真正含义。

确定性意味着处理过程是与时间无关的,而且任何其他“外部的“输入不会影响到处理结果。例如,如果一个程序的输出会受到线程执行的具体顺序影响,或者受到gettimeofday调用、或者其他一些非重复性事件的影响,那么这样的程序一般最有可能被认为是非确定性的。

进程状态是进程保存在机器上的任何数据,在进程处理结束的时候,这些数据要么保存在内存里,要么保存在磁盘上。

以相同的顺序获得相同输入的地方应当引起注意-这就是引入日志的地方。这儿有一个重要的常识:如果给两段确定性代码相同的日志输入,那么它们就会生成相同的输出。

分布式计算这方面的应用就格外明显。你可以把用多台机器一起执行同一件事情的问题缩减为实现分布式一致性日志为这些进程输入的问题。这儿日志的目的是把所有非确定性的东西排除在输入流之外,来确保每个复制进程能够同步地处理输入。

当你理解了这个以后,状态机复制原理就不再复杂或者说不再深奥了:这或多或少的意味着“确定性的处理过程就是确定性的”。不管怎样,我都认为它是分布式系统设计里较常用的工具之一。

这种方式的一个美妙之处就在于索引日志的时间戳就像时钟状态的一个副本——你可以用一个单独的数字描述每一个副本,这就是经过处理的日志的时间戳。时间戳与日志一一对应着整个副本的状态。

由于写进日志的内容的不同,也就有许多在系统中应用这个原则的不同方式。举个例子,我们记录一个服务的请求,或者服务从请求到响应的状态变化,或者它执行命令的转换。理论上来说,我们甚至可以为每一个副本记录一系列要执行的机器指令或者调用的方法名和参数。只要两个进程用相同的方式处理这些输入,这些进程就会保持副本的一致性。

一千个人眼中有一千种日志的用法。数据库工作者通常区分物理日志和逻辑日志。物理日志就是记录每一行被改变的内容。逻辑日志记录的不是改变的行而是那些引起行的内容被改变的SQL语句(insert,update和delete语句)。

分布式系统通常可以宽泛分为两种方法来处理数据和完成响应。“状态机器模型”通常引用一个主动-主动的模型——也就是我们为之记录请求和响应的对象。对此进行一个细微的更改,称之为“预备份模型”,就是选出一个副本做为leader,并允许它按照请求到达的时间来进行处理并从处理过程中输出记录其状态改变的日志。其他的副本按照leader状态改变的顺序而应用那些改变,这样他们之间达到同步,并能够在leader失败的时候接替leader的工作。

为了理解两种方式的不同,我们来看一个不太严谨的例子。假定有一个算法服务的副本,保持一个独立的数字作为它的状态(初始值为0),并对这个值进行加法和乘法运算。主动-主动方式应该会输出所进行的变换,比如“+1”,“*2”等。每一个副本都会应用这些变换,从而得到同样的解集。主动-被动方式将会有一个独立的主体执行这些变换并输出结果日志,比如“1”,“3”,“6”等。这个例子也清楚的展示了为什么说顺序是保证各副本间一致性的关键:一次加法和乘法的顺序的改变将会导致不同的结果。

分布式日志可以理解为一致性问题模型的数据结构。因为日志代表了后续追加值的一系列决策。你需要重新审视Paxos算法簇,尽管日志模块是他们最常见的应用。 在Paxos算法中,它通常通过使用称之为多paxos的协议,这种协议将日志建模为一系列的问题,在日志中每个问题都有对应的部分。在ZAB, RAFT等其它的协议中,日志的作用尤为突出,它直接对维护分布式的、一致性的日志的问题建模。

我怀疑的是,我们就历史发展的观点是有偏差的,可能是由于过去的几十年中,分布式计算的理论远超过了其实际应用。在现实中,共识的问题是有点太简单了。计算机系统很少需要决定单个值,他们几乎总是处理成序列的请求。这样的记录,而不是一个简单的单值寄存器,自然是更加抽象。

此外,专注于算法掩盖了 抽象系统需要的底层的日志。我怀疑,我们最终会把日志中更注重作为一个商品化的基石,不论其是否以同样的方式 实施的,我们经常谈论一个哈希表而不是纠结我们 得到是不是具体某个细节的哈希表,例如线性或者带有什么什么其它变体哈希表。日志将成为一种大众化的接口,为大多数算法和其实现提升提供最好的保证和最佳的性能。

让我们继续聊数据库。数据库中存在着大量变更日志和表之间的二相性。这些日志有点类似借贷清单和银行的流程,数据库表就是当前的盈余表。如果你有大量的变更日志,你就可以使用这些变更用以创建捕获当前状态的表。这张表将记录每个关键点(日志中一个特别的时间点)的状态信息。这就是为什么日志是非常基本的数据结构的意义所在:日志可用来创建基本表,也可以用来创建各类衍生表。同时意味着可以存储非关系型的对象。

这个流程也是可逆的:如果你正在对一张表进行更新,你可以记录这些变更,并把所有更新的日志发布到表的状态信息中。这些变更日志就是你所需要的支持准实时的克隆。基于此,你就可以清楚的理解表与事件的二相性: 表支持了静态数据而日志捕获变更。日志的魅力就在于它是变更的完整记录,它不仅仅捕获了表的最终版本的内容,它还记录了曾经存在过的其它版本的信息。日志实质上是表历史状态的一系列备份。

这可能会引起你对源代码的版本管理。源代码管理和数据库之间有密切关系。版本管理解决了一个大家非常熟悉的问题,那就是什么是分布式数据系统需要解决的--- 时时刻刻在变化着的分布式管理。版本管理系统通常以补丁的发布为基础,这实际上可能是一个日志。您可以直接对当前 类似于表中的代码做出“快照”互动。你会注意到, 与其他分布式状态化系统类似,版本控制系统 当你更新时会复制日志,你希望的只是更新补丁并将它们应用到你的当前快照中。

最近,有些人从Datomic --一家销售日志数据库的公司得到了一些想法。这些想法使他们对如何 在他们的系统应用这些想法有了开阔的认识。 当然这些想法不是只针对这个系统,他们会成为 十多年分布式系统和数据库文献的一部分。

这可能似乎有点过于理想化。但是不要悲观!我们会很快把它实现。

在这篇文章的其余部分,我将试图说明日志除了可用在分布式计算或者抽象分布式计算模型内部之外,还可用在哪些方面。其中包括:

数据集成-让机构的全部存储和处理系统里的所有数据很容易地得到访问。

实时数据处理-计算生成的数据流。

分布式系统设计-实际应用的系统是如何通过使用集中式日志来简化设计的。

所有这些用法都是通过把日志用做单独服务来实现的。

在上面任何一种用法里,日志的用途开始都是使用了日志所能提供的某个简单功能:生成永久的、可重现的历史记录。令人意外的是,问题的核心是可以让多少台机器以特定的方式,按照自身的速度重现历史记录的能力。

请让我首先解释 一下“数据集成”是什么意思,还有为什么我觉得它很重要,之后我们再来看看它和日志有什么关系。

数据集成就是将数据组织起来,使得在与其有关的服务和系统中可以访问它们。“数据集成”(data integration)这个短语应该不止这么简单,但是我找不到一个更好的解释。而更常见的术语 ETL 通常只是覆盖了数据集成的一个有限子集(译注:ETL,Extraction-Transformation-Loading的缩写,即数据提取、转换和加载)——相对于关系型数据仓库。但我描述的东西很大程度上可以理解为,将ETL推广至实时系统和处理流程。

你一定不会听到数据集成就兴趣盎然屏住呼吸,并且天花乱坠的想到关于大数据的概念,不过,我相信世俗的问题“让数据可被访问” 是一个组织应该关注的有价值的事情。

对数据的高效使用遵循一种  。金字塔的基础部分包括捕获所有相关数据,能够将它们全部放到适当的处理环境(那个环境应该是一个奇妙的实时查询系统,或者仅仅是文本文件和python脚本)。这些数据需要以统一的方式建模,这样就可以方便读取和数据处理。如果这种以统一的方式捕获数据的基本需求得到满足,那么就可以在基础设施上以若干种方法处理这些数据——映射化简(MapReduce),实时查询系统,等等。

很明显,有一点值得注意:如果没有可靠的、完整的数据流,Hadoop集群除了象昂贵的且难于安装的空间取暖器哪样外不会做更多事情了。一旦数据和处理可用,人们就会关心良好数据模型和一致地易于理解的语法哪些更细致的问题。最后,人们才会关注更加高级的处理-更好的可视化、报表以及处理和预测算法。 

以我的经验,大多数机构在数据金字塔的底部存在巨大的漏洞-它们缺乏可靠的、完整的数据流-而是打算直接跳到高级数据模型技术上。这样做完全是反着来做的。

因此,问题是我们如何构建通过机构内所有数据系统的可靠的数据流。

两种趋势使数据集成变得更困难。

第一个趋势是增长的事件数据(event data)。事件数据记录的是发生的事情,而不是存在的东西。在web系统中,这就意味着用户活动日志,还有为了可靠的操作以及监控数据中心的机器的目的,所需要记录的机器级别的事件和统计数字。人们倾向称它们为“日志数据”,因为它们经常被写到应用的日志中,但是这混淆了形式与功能。这种数据位于现代web的中心:归根结底,Google的资产是由这样一些建立在点击和映像基础之上的相关管道所生成的——那也就是事件。

这些东西并不是仅限于网络公司,只是网络公司已经完全数字化,所以它们更容易用设备记录。财务数据一直是面向事件的。RFID(无线射频识别)将这种跟踪能力赋予物理对象。我认为这种趋势仍将继续,伴随着这个过程的是传统商务活动的数字化。

这种类型的事件数据记录下发生的事情,而且往往比传统数据库应用要大好几个数量级。这对于处理提出了重大挑战。

第二个趋势来自于专门的数据系统的爆发,通常这些数据系统在最近的五年中开始变得流行,并且可以免费获得。专门的数据系统是为OLAP, 搜索, 简单 在线 存储, 批处理, 图像分析, 等 等 而存在的。

更多的不同类型数据的组合,以及将这些数据存放到更多的系统中的愿望,导致了一个巨大的数据集成问题。

为了处理系统之间的数据流,日志是最自然的数据结构。其中的秘诀很简单:

将所有组织的数据提取出来,并将它们放到一个中心日志,以便实时查阅。

每个逻辑数据源都可以建模为它自己的日志。一个数据源可以是一个应用程序的事件日志(如点击量或者页面浏览量),或者是一个接受修改的数据库表。每个订阅消息的系统都尽可能快的从日志读取信息,将每条新的记录保存到自己的存储,并且提升其在日志中的地位。订阅方可以是任意一种数据系统 —— 一个缓存,Hadoop,另一个网站中的另一个数据库,一个搜索系统,等等。

例如,日志针对每个更改给出了逻辑时钟的概念,这样所有的订阅方都可以被测量。推导不同的订阅系统的状态也因此变得相对简单的多,因为每个系统都有一个读取动作的“时间点”。

为了让这个显得更具体,我们考虑一个简单的案例,有一个数据库和一组缓存服务器集群。日志提供了一种同步更新所有这些系统,并推导出每一个系统的接触时间点的方法。我们假设写了一条日志X,然后需要从缓存做一次读取。如果我们想保证看到的不是陈旧的数据,我们只需保证没有从任何尚未复制X的缓存中读取即可。

日志也起到缓存的作用,使数据生产与数据消费相同步。由于许多原因这个功能很重要,特别是在多个订阅方消费数据的速度各不相同的时候。这意味着一个订阅数据系统可以宕机,或者下线维护,之后重新上线以后再赶上来:订阅方按照自己控制的节拍来消费数据。批处理系统,如Hadoop或者是一个数据仓库,或许只是每小时或者每天消费一次数据,而实时查询系统可能需要及时到秒。由于无论是原始数据源还是日志,都没有各种目标数据系统的相关知识,因此消费方系统可以被添加和删除,而无需传输管道的变化。

"每个工作数据管道设计得就像是一个日志;每个损坏的数据管道以其自己的方式损坏。"—Count Leo Tolstoy (由作者翻译)

特别重要的是:目标系统只知道日志,不知道数据源系统的任何细节。消费方系统自身无需考虑数据到底是来自于一个RDBMS(关系型数据库管理系统Relational Database Management System),一种新型的键值存储,或者它不是由任何形式的实时查询系统所生成的。这似乎是一个小问题,但实际上是至关重要的。

这里我使用术语“日志”取代了“消息系统”或者“发布-订阅”,因为它在语义上更明确,并且对支持数据复制的实际实现这样的需求,有着更接近的描述。我发现“发布订阅”并不比间接寻址的消息具有更多的含义——如果你比较任何两个发布-订阅的消息传递系统的话,你会发现他们承诺的是完全不同的东西,而且大多数模型在这一领域都不是有用的。你可以认为日志是一种消息系统,它具有持久性保证和强大的订阅语义。在分布式系统中,这个通信模型有时有个(有些可怕的)名字叫做原子广播。

值得强调的是,日志仍然只是基础设施。这并不是管理数据流这个故事的结束:故事的其余部分围绕着元数据,模式,兼容性,以及处理数据结构的所有细节及其演化。除非有一种可靠的,一般的方法来处理数据流运作,语义在其中总是次要的细节。

在LinkedIn从集中式关系数据库向分布式系统集合转化的过程中,我看到这个数据集成问题迅速演变。

现在主要的数据系统包括:

 (键值存储)(译注:一种分布式数据库)

OLAP查询引擎(译注:OLAP联机分析技术)

 (监控图表和指标服务)

这些都是专门的分布式系统,在其专业领域提供先进的功能。

这种使用日志作为数据流的思想,甚至在我到这里之前就已经与LinkedIn相伴了。我们开发的一个最早的基础设施之一,是一种称为databus 的服务,它在我们早期的Oracle表上提供了一种日志缓存抽象,可伸缩订阅数据库修改,这样我们就可以很好支持我们的社交网络和搜索索引。

我会给出一些历史并交代一下上下文。我首次参与到这些大约是在2008年左右,在我们转移键值存储之后。我的下一个项目是让一个工作中的Hadoop配置演进,并给其增加一些我们的推荐流程。由于缺乏这方面的经验,我们自然而然的安排了数周计划在数据的导入导出方面,剩下的时间则用来实现奇妙的预测算法。这样我们就开始了长途跋涉。

我们本来计划是仅仅将数据从现存的Oracle数据仓库中剖离。但是我们首先发现将数据从Oracle中迅速取出是一种黑暗艺术。更糟的是,数据仓库的处理过程与我们为Hadoop而计划的批处理生产过程不适合——其大部分处理都是不可逆转的,并且与即将生成的报告具体相关。最终我们采取的办法是,避免使用数据仓库,直接访问源数据库和日志文件。最后,我们为了加载数据到键值存储 并生成结果,实现了另外一种管道。

这种普通的数据复制最终成为原始开发项目的主要内容之一。糟糕的是,在任何时间任意管道都有一个问题,Hadoop系统很大程度上是无用的——在错误的数据基础上运行奇特的算法,只会产生更多的错误数据。

虽然我们已经以一种通用的方式创建事物,但是每个数据源都需要自定义配置安装。这也被证明是巨量错误与失败的根源。我们在Hadoop上实现的网站功能已经开始流行起来,同时我们发现我们有一长串感兴趣的工程师。每个用户都有他们想要集成的一系列系统,他们想要的一系列新数据源.

有些东西在我面前开始渐渐清晰起来。

首先,我们已建成的通道虽然有一些杂乱,但实质上它们是很有价值的。在采用诸如Hadoop的新的处理系统生成可用数据的过程,它开启了大量的可能性。 基于这些数据过去很难实现的计算,如今变为可能。 许多新的产品和分析技术都来源于把分片的数据放在一起,这些数据过被锁定在特定的系统中。

第二, 众所周知,可靠的数据加载需要数据通道的深度支持。如果我们可以捕获所有我们需要的结构,我就就可以使得Hadoop数据全自动的加载,这样就不需要额外的操作来增加新的数据源或者处理模式变更--数据就会自动的出现在HDFS,Hive表就会自动的生成对应于新数据源的恰当的列。

第三,我们的数据覆盖率仍然非常低。如果你查看存储于Hadoop中的可用的Linked 数据的全部百分比,它仍然是不完整的。花费大量的努力去使得各个新的数据源运转起来,使得数据覆盖度完整不是一件容易的事情。

我们正在推行的,为每个数据源和目标增建客户化数据加载,这种方式很显然是不可行的。我们有大量的数据系统和数据仓库。把这些系统和仓库联系起来,就会导致任意一对系统会产生如下所示的客户化通道。

需要注意的是:数据是双向流动的:例如许多系统诸如数据库和Hadoop既是数据转化的来源又是数据转化的目的地。这就意味着我们我们不必为每个系统建立两个通道:一个用于数据输入,一个用于数据输出。

这显然需要一大群人,而且也不具有可操作性。随着我们接近完全连接,最终我们将有差不多O(N2)条管道。

替代的,我们需要像这样通用的东西:

我们需要尽可能的将每个消费者与数据源隔离。理想情形下,它们应该只与一个单独的数据仓库集成,并由此让他们能访问到所有东西。

这个思想是增加一个新的数据系统——或者它是一个数据源或者它是一个数据目的地——让集成工作只需连接到一个单独的管道,而无需连接到每个数据消费方。

这种经历使得我关注创建Kafka来关联我们在消息系统所见的与数据库和分布式系统内核所发布的日志。我们希望一些实体作为中心的通道,首先用于所有的活动数据,逐步的扩展到其他用途,例如Hadoop外的数据实施,数据监控等。

在相当长的时间内,Kafka是独一无二的底层产品,它既不是数据库,也不是日志文件收集系统,更不是传统的消息系统。但是最近Amazon提供了非常类似Kafka的服务,称之为Kinesis.相似度包括了分片处理的方式,数据的保持,甚至包括在Kafka API中,有点特别的高端和低端消费者分类。我很开心看到这些,这表明了你已经创建了很好的底层协议,AWS已经把它作为服务提供。他们对此的期待与我所描述的吻合:通道联通了所有的分布式系统,诸如DynamoDB, RedShift, S3等,它同时作为使用EC2进行分布式流处理的基础。

我们再来聊聊数据仓库。数据仓库是清洗和归一数据结构用于支撑数据分析的仓库。这是一个伟大的理念。对不熟悉数据仓库概念的人来说,数据仓库方法论包括了:周期性的从数据源抽取数据,把它们转化为可理解的形式,然后把它导入中心数据仓库。对于数据集中分析和处理,拥有高度集中的位置存放全部数据的原始副本是非常宝贵的资产。在高层级上,也许你抽取和加载数据的顺序略微调整,这个方法论不会有太多变化,无论你使用传统的数据仓库Oracle还是Teradata或者Hadoop。

数据仓库是极其重要的资产,它包含了原始的和规整的数据,但是实现此目标的机制有点过时了。

对以数据为中心的组织关键问题是把原始的归一数据联结到数据仓库。数据仓库是批处理的基础查询:它们适用于各类报表和临时性分析,特别是当查询包含了简单的计数、聚合和过滤。但是如果一个批处理系统仅仅包含了原始的完整的数据的数据仓库,这就意味着这些数据对于实时数据处理、搜索索引和系统监控等实时的查询是不可用的。

依我之见,ETL包括两件事:首先,它是抽取和数据清洗过程--特别是释放被锁在组织的各类系统中的数据,移除系统专有的无用物。第二,依照数据仓库的查询重构数据。例如使其符合关系数据库类型系统,强制使用星号、雪花型模式,或者分解为高性能的柱状格式等。合并这两者是有困难的。这些规整的数据集应当可以在实时或低时延处理中可用,也可以在其它实施存储系统索引。

在我看来,正是因为这个原因有了额外好处:使得数据仓库ETL更具了组织级的规模。数据仓库的精典问题是数据仓库负责收集和清洗组织中各个组所生成的全部数据。各组织的动机是不同的,数据的生产者并不知晓在数据仓库中数据的使用情况,最终产生的数据很难抽取,或者需要花费规模化的转化才可以转化为可用的形式。当然, 中心团队不可能恰到好处的掌握规模,使得这规模刚好与组织中其它团队相匹配,因此数据的覆盖率常常差别很大,数据流是脆弱的同时变更是缓慢的。

较好的方法是有一个中心通道,日志和用于增加数据的定义良好的API。与通道集成的且提供良好的结构化的数据文件的职责依赖于数据的生产者所生成的数据文件。这意味着在设计和实施其它系统时应当考虑数据的输出以及输出的数据如何转化为结构良好的形式并传递给中心通道。增加新的存储系统倒是不必因为数据仓库团队有一个中心结点需要集成而关注数据仓库团队。数据仓库团队仅需处理简单的问题,例如从中心日志中加载结构化的数据,向其它周边系统实施个性化的数据转化等。

如图所示:当考虑在传统的数据仓库之外增加额外的数据系统时,组织结构的可扩展性显得尤为重要。例如,可以考虑为组织的完整的数据集提供搜索功能。或者提供二级的数据流监控实时数据趋势和告警。无论是这两者中的哪一个,传统的数据仓库架构甚至于Hadoop聚簇都不再适用。更糟的是,ETL的流程通道的目的就是支持数据加载,然而ETL似乎无法输出到其它的各个系统,也无法通过引导程序,使得这些外围的系统的各个架构成为适用于数据仓库的重要资产。这就不难解释为什么组织很难轻松的使用它的全部数据。反之,如果组织已建立起了一套标准的、结构良好的数据,那么任何新的系统要使用这些数据仅仅需要与通道进行简单的集成就可以实现。

这种架构引出了数据清理和转化在哪个阶段进行的不同观点:

由数据的生产者在把数据增加到公司全局日志之前。

在日志的实时转化阶段进行,这将会产生一个新的转化日志。

在向目标系统加载数据时,做为加载过程的一部分进行。

理想的模形是:由数据的生产者在把数据发布到日志之前对数据进行清理。这样可以确保数据的权威性,不需要维护其它的遗留物例如为数据产生的特殊处理代码或者维护这些数据的其它的存储系统。这些细节应当由产生数据的团队来处理,因为他们最了解他们自己的数据。这个阶段所使用的任何逻辑都应该是无损的和可逆的。

任何可以实时完成的增值转化类型都应当基于原始日志进行后期处理。这一过程包括了事件数据的会话流程,或者增加大众感兴趣的衍生字段。原始的日志仍然是可用的,但是这种实时处理产生的衍生日志包含了参数数据。

最终,只有针对目标系统的聚合需要做了加载流程的一部分。它包括了把数据转化成特定的星型或者雪花状模式,从而用于数据仓库的分析和报表。因为在这个阶段,大部分自然的映射到传统的ETL流程中,而现在它是在一个更加干净和规整的数据流集在进行的,它将会更加的简单。

我们再来聊聊这种架构的优势:它支持解耦和事件驱动的系统。

在网络行业取得活动数据的典型方法是把它记为文本形式的日志,这些文本文件是可分解进入数据仓库或者Hadoop,用于聚合和查询处理的。由此产生的问题与所有批处理的ETL的问题是相同的:它耦合了数据流进入数据仓库系统的能力和流程的调度。

在LinkedIn中,我们已经以中心日志的方式构建了事件数据处理。我们正在使用Kafka做为中心的、多订阅者事件日志。我们已经定义了数百种事件类型,每种类型都会捕获用于特定类型动作的独特的属性。这将会覆盖包括页面视图、表达式、搜索以及服务调用、应用异常等方方面面。

为了进一步理解这一优势:设想一个简单的事务--在日志页面显示已发布的日志。这个日志页面应当只包括显示日志所需要的逻辑。然而,在相当多的动态站点中,日志页面常常变的添加了很多与显示日志无关的逻辑。例如,我们将对如下的系统进行集成:

需要把数据传送到Hadoop和数据仓库中用于离线数据处理。

需要对视图进行统计,确保视图订阅者不会攻击一些内容片段。

需要聚合这些视图,视图将用于作业发布者的分析页面显示。

需要记录视图以确保我们为作业推荐的使用者提供了恰当的印象覆盖,我们不想一次次的重复同样的事情。

推荐系统需要记录日志用于正确的跟踪作业的普及度。

不久,简单的作业显示变得相当的复杂。我们增加了作业显示的其它终端--移动终端应用等--这些逻辑必须继续存在,复杂度不断的增加。更糟的是我们需要与之做接口交互的系统现在是错综复杂的--在为显示日作业而工作的工程师们需要知晓多个其它系统和它们的特征,才可以确保它们被正确的集成了。这仅仅是问题的简单版本,真实的的应用系统只会更加的复杂。

“事件驱动”的模式提供了一种简化这类问题的机制。作业显示页面现在只显示作业并记录与正在显示的作业,作业订阅者相关的其它属性,和其它与作业显示相关的其它有价值的属性。每个与此相关的其它系统诸如推荐系统、安全系统、作业推送分析系统和数据仓库,所有这些只是订阅种子文件,并进行它们的操作。显示代码并不需要关注其它的系统,也不需要因为增加了数据的消费者而相应的进行变更。

当然,把发布者与订阅者分离不再是什么新鲜事了。但是如果你想要确保提交日志的行为就像多个订阅者实时的分类日志那样记录网站发生的每件事时,可扩展性就会成为你所面临的首要挑战。如果我们不能创建快速、高性价比和可扩展性灵活的日志以满足实际的可扩展需求,把日志做为统一的集成机制不再是美好的想像,

人们普遍认为分布式日志是缓慢的、重量经的概念(并且通常会把它仅仅与“原数据”类型的使用联系起来,对于这类使用Zookeeper可以适用)。但是深入实现并重点关注分类记录大规模的数据流,这种需求是不切实际的。在LinkedIn, 我们现在每天通过Kafka运行着超过600亿个不同的消息写入点(如果统计镜相与数据中心之间的写入,那么这个数字会是数千亿。)

我们在Kafk中使用了一些小技巧来支持这种可扩展性:

通过批处理读出和写入优化吞吐力

为了确保水平可扩展性,我们把日志进行切片:

个切片都是一篇有序的日志,但是各片之间没有全局的次序(这个有别于你可能包含在消息中的挂钟时间)。把消息分配到特定的日志片段这是由写入者控制的,大部分使用者会通过用户ID等键值来进行分片。分片可以把日志追加到不存在协作的片段之间,也可以使系统的吞吐量与Kafka聚簇大小成线性比例关系。

每个分片都是通过可配置数量的复制品复制的,每个复制品都有分片的一份完全一致的拷贝。无论何时,它们中的任一个都可以做为主分片,如果主分片出错了,任何一个复制品都可以接管并做为主分片。

缺少跨分片的全局顺序是这个机制的局限性,但是我们不认为它是最主要的。事实上,与日志的交互主要来源于成百上千个不同的流程,以致于对于它们的行为排一个总体的顺序是没什么意义的。相反,我们可以确保的是我们提供的每个分片都是按顺序保留的。Kafka保证了追加到由单一发送者送出的特定分片会按照发送的顺序依次处理。

日志,就像文件系统一样,是容易优化成线性可读可写的样式的。日志可以把小的读入和写出组合成大的、高吞吐量的操作。Kafka一直至立于实现这一优化目标。批处理可以发生在由客户端向服务器端发送数据、写入磁盘;在服务器各端之间复制;数据传递给消费者和确认提交数据等诸多环节。

最终,Kafka使用简单的二进制形式维护内存日志,磁盘日志和网络数据传送。这使得我们可以使用包括“Zero Copy”在内的大量的优化机制。

这些优化的积累效应是你常常进行的写出和读入数据的操作可以在磁盘和网络上得到支持,甚至于维护内存以外的大量数据集。

这些详细记述并不意味着这是关于Kafka的主要内容,那么我就不需要了解细节了。你可阅读到更多的关于LinkedIn的方法在这个链接,和Kafka的设计总述在这个链接。

到此为止,我只是描述从端到端数据复制的理想机制。但是在存储系统中搬运字节不是所要讲述内容的全部。最终我们发现日志是流的另一种说法,日志是流处理的核心。

但是,等等,什么是流处理呢?

如果你是90年代晚期或者21世纪初数据库文化或者数据基础架构产品的爱好者,那么你就可能会把流处理与建创SQL引擎或者创建“箱子和箭头”接口用于事件驱动的处理等联系起来。

如果你关注开源数据库系统的大量出现,你就可能把流处理和一些开源数据库系统关联起来,这些系统包括了:Storm,Akka,S4和Samza.但是大部分人会把这些系统作为异步消息处理系统,这些系统与支持群集的远程过程调用层的应用没什么差别(而事实上在开源数据库系统领域某些方面确实如此)。

这些视图都有一些局限性。流处理与SQL是无关的。它也局限于实时流处理。不存在内在的原因限制你不能处理昨天的或者一个月之前的流数据,且使用多种不同的语言表达计算。

我把流处理视为更广泛的概念:持续数据流处理的基础架构。我认为计算模型可以像MapReduce或者分布式处理架构一样普遍,但是有能力处理低时延的结果。

处理模型的实时驱动是数据收集方法。成批收集的数据是分批处理的。数据是不断收集的,它也是按顺序不断处理的。

美国的统计调查就是成批收集数据的良好典范。统计调查周期性的开展,通过挨门挨户的走访,使用蛮力发现和统计美国的公民信息。1790年统计调查刚刚开始时这种方式是奏效的。那时的数据收集是批处理的,它包括了骑着马悠闲的行进,把信息写在纸上,然后把成批的记录传送到人们统计数据的中心站点。现在,在描述这个统计过程时,人们立即会想到为什么我们不保留出生和死亡的记录,这样就可以产生人口统计信息这些信息或是持续的或者是其它维度的。

这是一个极端的例子,但是大量的数据传送处理仍然依赖于周期性的转储,批量转化和集成。处理大容量转储的唯一方法就是批量的处理。但是随着这些批处理被持续的供给所取代,人们自然而然的开始不间断的处理以平滑的处理所需资源并且消除延迟。

例如LinkedIn几乎没有批量数据收集。大部分的数据或者是活动数据或者是数据库变更,这两者都是不间断发生的。事实上,你可以想到的任何商业,正如:Jack Bauer告诉我们的,低层的机制都是实时发生的不间断的流程事件。数据是成批收集的,它总是会依赖于一些人为的步骤,或者缺少数字化或者是一些自动化的非数字化流程处理的遗留信息。当传送和处理这些数据的机制是邮件或者人工的处理时,这一过程是非常缓慢的。首轮自动化总是保持着最初的处理形式,它常常会持续相当长的时间。

每天运行的批量处理作业常常是模拟了一种一天的窗口大小的不间断计算。当然,低层的数据也经常变化。在LinkedIn,这些是司空见贯的,并且使得它们在Hadoop运转的机制是有技巧的,所以我们实施了一整套管理增量的Hadoop工作流的架构。

由此看来,对于流处理可以有不同的观点。流处理包括了在底层数据处理的时间概念,它不需要数据的静态快照,它可以产生用户可控频率的输出,而不用等待数据集的全部到达。从这个角度上讲,流处理就是广义上的批处理,随着实时数据的流行,会儿更加普遍。

这就是为什么从传统的视角看来流处理是利基应用。我个人认为最大的原因是缺少实时数据收集使得不间断的处理成为了学术性的概念。

我想缺少实时数据收集就像是商用流处理系统注定的命运。他们的客户仍然需要处理面向文件的、每日批量处理ETL和数据集成。公司建设流处理系统关注的是提供附着在实时数据流的处理引擎,但是最终当时极少数人真正使用了实时数据流。事实上,在我在LinkedIn工作的初期,有一家公司试图把一个非常棒的流处理系统销售给我们,但是因为当时我们的全部数据都按小时收集在的文件里,当时我们提出的最好的应用就是在每小时的最后把这些文件输入到流处理系统中。他们注意到这是一个普遍性的问题。这些异常证明了如下规则:流处理系统要满足的重要商业目标之一是:财务, 它是实时数据流已具备的基准,并且流处理已经成为了瓶颈。

甚至于在一个健康的批处理系统中,流处理作为一种基础架构的实际应用能力是相当广泛的。它跨越了实时数据请求-应答服务和离线批量处理之间的鸿沟。现在的互联网公司,大约25%的代码可以划分到这个类型中。

最终这些日志解决了流处理中绝大部分关键的技术问题。在我看来,它所解决的最大的问题是它使得多订阅者可以获得实时数据。对这些技术细节感兴趣的朋友,我们可以用开源的Samza,它是基于这些理念建设的一个流处理系统。这些应用的更多技术细节我们在此文档中有详细的描述。

流处理最有趣的角度是它与流处理系统内部无关,但是与之密切相关的是如何扩展了我们谈到的早期数据集成的数据获取的理念。我们主要讨论了基础数据的获取或日志--事件和各类系统执行中产生的数据等。但是流处理允许我们包括了计算其它数据的数据。这些衍生的数据在消费者看来与他们计算的原始数据没什么差别。这些衍生的数据可以按任意的复杂度进行压缩。

让我们再深入一步。我们的目标是:流处理作业可以读取任意的日志并把日志写入到日志或者其它的系统中。他们用于输入输出的日志把这些处理关联到一组处理过程中。事实上,使用这种样式的集中日志,你可以把组织全部的数据抓取、转化和工作流看成是一系列的日志和写入它们的处理过程。

流处理器根本不需要理想的框架:它可能是读写日志的任何处理器或者处理器集合,但是额外的基础设施和辅助可以提供帮助管理处理代码。

日志集成的目标是双重的:

首先,它确保每个数据集都有多个订阅者和有序的。让我们回顾一下状态复制原则来记住顺序的重要性。为了使这个更加具体,设想一下从数据库中更新数据流--如果在处理过程中我们把对同一记录的两次更新重新排序,可能会产生错误的输出。 TCP之类的链接仅仅局限于单一的点对点链接,这一顺序的持久性要优于TCP之类的链接,它可以在流程处理失败和重连时仍然存在。

第二,日志提供了流程的缓冲。这是非常基础的。如果处理流程是非同步的,那么上行生成流数据的作业比下行消费流数据的作业运行的更快。这将会导致处理流程阻塞,或者缓冲数据,或者丢弃数据。丢弃数据并不是可行的方法,阻塞将会导致整个流程图立即停止。 日志实际上是一个非常大的缓冲,它允许流程重启或者停止但不会影响流程图其它部分的处理速度。如果要把数据流扩展到更大规模的组织,如果处理作业是由多个不同的团队提供的,这种隔离性是极其重的。我们不能容忍一个错误的作业引发后台的压力,这种压力会使得整个处理流程停止。

Storm和Sama这两者都是按非同步方式设计的,可以使用Kafka或者其它类似的系统作为它们的日志。

一些实时流处理在转化时是无状态的记录。在流处理中大部分的应用会是相当复杂的统计、聚合、不同窗口之间的关联。例如有时人们想扩大包含用户操作信息的事件流(一系列的单击动作)--实际上关联了用户的单击动作流与用户的账户信息数据库。不变的是这类流程最终会需要由处理器维护的一些状态信息。例如数据统计时,你需要统计到目前为止需要维护的计数器。如果处理器本身失败了,如何正确的维护这些状态信息呢?

最简单的替换方案是把这些状态信息保存在内存中。但是如果流程崩溃,它就会丢失中间状态。如果状态是按窗口维护的,流程就会回退到日志中窗口开始的时间点上。但是,如果统计是按小时进行的,那么这种方式就会变得不可行。

另一个替换方案是简单的存储所有的状态信息到远程的存储系统,通过网络与这些存储关联起来。这种机制的问题是没有本地数据和大量的网络间通信。

我们如何支持处理过程可以像表一样分区的数据呢?

回顾一下关于表和日志二相性的讨论。这一机制提供了工具把数据流转化为与处理过程协同定位的表,同时也提供了这些表的容错处理的机制。

流处理器可以把它的状态保存在本地的表或索引--bdb,或者leveldb,甚至于类似于Lucene 或fastbit一样不常见的索引。这些内容存储在它的输入流中(或许是使用任意的转化)。生成的变更日志记录了本地的索引,它允许存储事件崩溃、重启等的状态信息。流处理提供了通用的机制用于在本地输入流数据的随机索引中保存共同分片的状态。

当流程运行失败时,它会从变更日志中恢复它的索引。每次备份时,日志把本地状态转化成一系列的增量记录。

这种状态管理的方法有一个优势是把处理器的状态也做为日志进行维护。我们可以把这些日志看成与数据库表相对应的变更日志。事实上,这些处理器同时维护着像共同分片表一样的表。因为这些状态它本身就是日志,其它的处理器可以订阅它。如果流程处理的目标是更新结点的最后状态,这种状态又是流程的输出,那么这种方法就显得尤为重要。

为了数据集成,与来自数据库的日志关联,日志和数据库表的二象性就更加清晰了。变更日志可以从数据库中抽取出来,日志可以由不同的流处理器(流处理器用于关联不同的事件流)按不同的方式进行索引。

我们可以列举在Samza中有状态流处理管理的更多细节和大量实用的例子。

当然,我们不能奢望保存全部变更的完整日志。除非想要使用无限空间,日志不可能完全清除。为了澄清它,我们再来聊聊Kafka的实现。在Kafka中,清理有两种选择,这取决于数据是否包括关键更新和事件数据。对于事件数据,Kafka支持仅维护一个窗口的数据。通常,配置需要一些时间,窗口可以按时间或空间定义。虽然对于关键数据而言,完整日志的重要特征是你可以重现源系统的状态信息,或者在其它的系统重现。

随着时间的推移,保持完整的日志会使用越来越多的空间,重现所耗费的时间越来越长。因些在Kafka中,我们支持不同类型的保留。我们移除了废弃的记录(这些记录的主键最近更新过)而不是简单的丢弃旧日志。我们仍然保证日志包含了源系统的完整备份,但是现在我们不再重现原系统的全部状态,而是仅仅重现最近的状态。我们把这一特征称为日志压缩。

我们最后要讨论的是在线数据系统设计中日志的角色。

在分布式数据库数据流中日志的角色和在大型组织机构数据完整中日志的角色是相似的。在这两个应用场景中,日志是对于数据源是可靠的,一致的和可恢复的。组织如果不是一个复杂的分布式数据系统呢,它究竟是什么?

如果换个角度,你可以看到把整个组织系统和数据流看做是单一的分布式数据系统。你可以把所有的子查询系统(诸如Redis, SOLR,Hive表等)看成是数据的特定索引。你可以把Storm或Samza一样的流处理系统看成是发展良好的触发器和视图具体化机制。我已经注意到,传统的数据库管理人员非常喜欢这样的视图,因为它最终解释了这些不同的数据系统到底是做什么用的--它们只是不同的索引类型而已。

不可否认这类数据库系统现在大量的出现,但是事实上,这种复杂性一直都存在。即使是在关系数据库系统的鼎盛时期,组织中有大量的关系数据库系统。或许自大型机时代开始,所有的数据都存储在相同的位置,真正的集成是根本不存在的。存在多种外在需求,需要把数据分解成多个系统,这些外在需求包括:规模、地理因素、安全性,性能隔离是最常见的因素。这些需求都可以由一个优质的系统实现:例如,组织可以使用单一的Hadoop聚簇,它包括了全部的数据,可以服务于大型的和多样性的客户。

因此在向分布式系统变迁的过程中,已经存在一种处理数据的简便的方法:把大量的不同系统的小的实例聚合成为大的聚簇。许多的系统还不足以支持这一方法:因为它们不够安全,或者性能隔离性得不到保证,或者规模不符合要求。不过这些问题都是可以解决的。

依我之见,不同系统大量出现的原因是建设分布式数据库系统很困难。通过削减到单一的查询或者用例,每个系统都可以把规模控制到易于实现的程度。但是运行这些系统产生的复杂度依然很高。

未来这类问题可能的发展趋势有三种:

第一种可能是保持现状:孤立的系统还会或长或短的持续一段时间。这是因为建设分布式系统的困难很难克服,或者因为孤立系统的独特性和便捷性很难达到。基于这些原因,数据集成的核心问题仍然是如何恰当的使用数据。因此,集成数据的外部日志非常的重要。

第二种可能是重构:具备通用性的单一的系统逐步融合多个功能形成超极系统。这个超级系统表面看起来类似关系数据库系统,但是在组织中你使用时最大的不同是你只需要一个大的系统而不是无数个小系统。在这个世界里,除了在系统内已解决的这个问题不存在什么真正的数据集成问题。我想这是因为建设这样的系统的实际困难。

虽然另一种可能的结果对于工程师来说是很有吸引力的。新一代数据库系统的特征之一是它们是完全开源的。开源提供了一种可能性:数据基础架构不必打包成服务集或者面向应用的系统接口。在Java栈中,你可以看到在一定程度上,这种状况已经发生了。

用于处理多个系统之间的协调,或许会从诸如 或者等高级别的抽象中得到一些帮助。

和用于处理流程可视化和资源管理。

和等嵌入式类库做为索引。

,和,等封装成高级别的用于处理远程通信。

,,和等其它类库用于处理序列化。

如果你把这些堆放在一起,换个角度看,它有点像是简化版的分布式数据库系统工程。你可以把这些拼装在一起,创建大量的可能的系统。显而易见,现在探讨的不是最终用户所关心的API或者如何实现,而是在不断多样化和模块化的过程中如何设计实现单一系统的途径。因为随着可靠的、灵活的模块的出现,实施分布式系统的时间周期由年缩减为周,聚合形成大型整体系统的压力逐步消失。

那些提供外部日志的系统如今已允许个人电脑抛弃他们自身复杂的日志系统转而使用共享日志。在我看来,日志可以做到以下事情:

通过对节点的并发更新的排序处理数据的一致性(无论在及时还是最终情况下)

提供节点之间的数据复制

提供”commit“语法(只有当写入器确保数据不会丢失时才会写入)

位系统提供外部的数据订阅资源

提供存储失败的复制操作和引导新的复制操作的能力

这实际上是一个数据分发系统最重要的部分,剩下的大部分内容与终端调用的API和索引策略相关。这正是不同系统间的差异所在,例如:一个全文本查询语句需要查询所有的分区,而一个主键查询只需要查询负责键数据的单个节点就可以了。

下面我们来看下该系统是如何工作的。系统被分为两个逻辑区域:日志和服务层。日志按顺序捕获状态变化,服务节点存储索引提供查询服务需要的所有信息(键-值的存储可能以B-tree或SSTable的方式进行,而搜索系统可能存在与之相反的索引)。写入器可以直接访问日志,尽管需要通过服务层代理。在写入日志的时候会产生逻辑时间戳(即log中的索引),如果系统是分段式的,那么就会产生与段数目相同数量的日志文件和服务节点,这里的数量和机器数量可能会有较大差距。

服务节点订阅日志信息并将写入器按照日志存储的顺序尽快应用到它的本地索引上。

客户端只要在查询语句中提供对应的写入器的时间戳,它就可以从任何节点中获取”读写“语义。服务节点收到该查询语句后会将其中的时间戳与自身的索引比较,如果必要,服务节点会延迟请求直到对应时间的索引建立完毕,以免提供旧数据。

服务节点或许根本无需知道”控制“或”投标选择(leader election)“的概念,对很多简单的操作,服务节点可以爱完全脱离领导的情况下提供服务,日志即是信息的来源。

分发系统所需要做的其中一个比较复杂的工作,就是修复失败节点并移除几点之间的隔离。保留修复的数据并结合上各区域内的数据快照是一种较为典型的做法,它与保留完整的数据备份并从垃圾箱内回收日志的做法几乎等价。这就使得服务层简单了很多,日志系统也更有针对性。

有了这个日志系统,你可以订阅到API,这个API提供了把ETL提供给其它系统的数据内容。事实上,许多系统都可以共享.相同的日志同时提供不同的索引,如下所示:

这样一个以日志为中心的系统是如何做到既数据流的提供者又同时加载其它系统的数据的呢?因为流处理器既可以消费多个输入的数据流,随后又可以通过其它系统对数据做索引为它们提供服务。

这个系统的视图可以清晰的分解到日志和查询API,因为它允许你从系统的可用性和一致性角度分解查询的特征。这可以帮助我们对系统进行分解,并理解那些并没按这种方式设计实施的系统。

虽然Kafka和Bookeeper都是一致性日志,但这不是必须的,也没什么意义。你可以轻松的把Dynamo之类的数据构分解为一致性的AP日志和键值对服务层。这样的日志使用起来灵活,因为它重传了旧消息,像Dynamo一样,这样的处理取决于消息的订阅者。

在很多人看来,在日志中另外保存一份数据的完整复本是一种浪费。事实上,虽然有很多因素使得这件事并不困难。首先,日志可以是一种有效的存储机制。我们在Kafka生产环境的服务器上存储了5 TB的数据。同时有许多的服务系统需要更多的内存来提供有效的数据服务,例如文本搜索,它通常是在内存中的。服务系统同样也需样硬盘的优化。例如,我们的实时数据系统或者在内存外提供服务或者使用固态硬盘。相反,日志系统只需要线性的读写,因此,它很乐于使用TB量级的硬盘。最终,如上图所示,由多个系统提供的数据,日志的成本分摊到多个索引上,这种聚合使得外部日志的成本降到了最低点。

LinkedIn就是使用了这种方式实现它的多个实时查询系统的。这些系统提供了一个数据库(使用数据总线做为日志摘要,或者从Kafka去掉专用的日志),这些系统在顶层数据流上还提供了特殊的分片、索引和查询功能。这也是我们实施搜索、社交网络和OLAP查询系统的方式。事实上这种方式是相当普遍的:为多个用于实时服务的服务系统提供单一的数据(这些来自Hadoop的数据或是实时的或是衍生的)。这种方式已被证实是相当简洁的。这些系统根本不需要外部可写入的API,Kafka和数据库被用做系统的记录和变更流,通过日志你可以查询系统。持有特定分片的结点在本地完成写操作。这些结点盲目的把日志提供的数据转录到它们自己的存储空间中。通过回放上行流日志可以恢复转录失败的结点。

这些系统的程度则取决于日志的多样性。一个完全可靠的系统可以用日志来对数据分片、存储结点、均衡负载,以及用于数据一致性和数据复制等多方面。在这一过程中,服务层实际上只不过是一种缓存机制,这种缓存机制允许直接写入日志的流处理。

如果你对于本文中所谈到的关于日志的大部内容,如下内容是您可以参考的其它资料。对于同一事务人们会用不同的术语,这会让人有一些困惑,从数据库系统到分布式系统,从各类企业级应用软件到广阔的开源世界。无论如何,在大方向上还是有一些共同之处。

学术论文、系统、评论和博客:

是实施微软基于日志的分布式存储系统的通用架构。

——并不是每个人都支持把逻辑时间用于他们的日志,Google最新的数据库就尝试使用物理时间,并通过把时间戳直接做为区间来直接建时钟迁移的不确定性。

。我发现这个有助于引入容错处理和数据库以外的应用系统日志恢复。

——事实上我并不清楚反应编程的确切涵义,但是我想它和“事件驱动”指的是同一件事。这个链接并没有太多的讯息,但由久富盛史的Martin Odersky讲授的课程是很有吸引力的。

原始文章在此。Leslie Lamport有一个有趣的历史:在80年代算法是如何发现的,但是直到1998年才发表了,因为评审组不喜欢论文中的希腊寓言,而作者又不愿修改。

甚至于论文发布以后,它还是不被人们理解。Lamport再次尝试,这次它包含了一些并不有趣的小细节,这些细节是关于如何使用这些新式的自动化的计算机的。它仍然没有得到广泛的认可。

我发现所有关于Paxos的论文理解起来很痛苦,但是值得我们费大力气弄懂。你不必忍受这样的痛苦了,因为日志结构的文件系统的大师John Ousterhout的这个让这一切变得相当的容易。这些一致性算法用展开的通信图表述的更好,而不是在论文中通过静态的描述来说明。颇为讽刺的是,这个视频录制的初衷是告诉人们Paxos很难理解。

。这是一篇很棒的介绍使用日志来构造数据存储的文章,Jun是文章的共同作者之一,他也是Kafka最早期的工程师之一。

Paxos有很多的竞争者。如下诸项可以更进一步的映射到日志的实施,更适合于实用性的实施。

由Barbara Liskov提出的是直接进行日志复现建模的较早的算法。

是易于理解的一致性算法之一。由John Ousterhout讲授的这个非常的棒。

你可以的看到在不同的实时分布式数据库中动作日志角色:

是探索在大规模的传统的分布式数据库系统中实施以日志为中心设计理念的系统。

和Bigtable都是在目前的数据库系统中使用日志的样例。

LinkedIn自己的分布式数据库和PNUTs一样,使用日志来复现,但有一个小的差异是它使用自己底层的表做为日志的来源。

如果你正在做一致性算法选型,这篇会对你所有帮助。

,这是收录了分布式系统一致性的大量论文的一本巨著。网上有该书的诸多章节。

流处理:这个话题要总结的内容过于宽泛,但还是有几件我所关注的要提一下:

:这篇论文讨论了Spark的流式系统。

:它可能是研究这一领域的最佳概述之一。

企业级软件存在着同样的问题,只是名称不同,或者规模较小,或者是XML格式的。哈哈,开个玩笑。

--据我所知:它就是企业级应用的工程师们常说的“状态机的复现”。有趣的是同样的理念会用在如此迥异的场景中。事件驱动关注的是小的、内存中的使用场景。这种机制在应用开发中看起来是把发生在日志事件中的“流处理”和应用关联起来。因此变得不那么琐碎:当处理的规模大到需要数据分片时,我关注的是流处理作为独立的首要的基础设施。

--在数据库之外会有些对于数据的舍入处理,这些处理绝大多数都是日志友好的数据扩展。

,当你有一些现成的类似客户类系管理CRM和供应链管理SCM的软件时,它似乎可以解决数据集成的问题。

没有人知道它的确切涵义或者它与流处理有什么不同。这些差异看起来集中在无序流和事件过滤、发现或者聚合上,但是依我之见,差别并不明显。我认为每个系统都有自己的优势。

--我认为企业服务总线的概念类似于我所描述的数据集成。在企业级软件社区中这个理念取得了一定程度的成功,对于从事网络和分布式基础架构的工程师们这个概念还是很陌生的。

是把日志作为服务的一个项目,它是后边所列各项的基础。

 和Hedwig 另外的两个开源的“把日志作为服务”的项目。它们更关注的是数据库系统内部构件而不是事件数据。

是提供类似日志的数据库表的覆盖层的系统。

 是用于Scala的动作者架构。它有一个事件驱动的插件,它提供持久化和记录。

 是我们在LinkedIn中用到的流处理框架,它用到了本文论述的诸多理念,同时与Kafka集成来作为底层的日志。

是广泛使用的可以很好的与Kafka集成的流处理框架之一。

一个流处理框架,它是的一部分。

是在Storm或Hadoop之上的一层,它提供了便洁的计算摘要。

对于这一领域,我将持续的关注,如何您知道一些我遗漏的内容,请您告知,谢谢。

    编者按:目前CPU用的内存正在从DDR2向DDR3过渡,而GPU用的显存则是以GDDR2/3为主、跳过GDDR4、直奔GDDR5而去。或许很多朋友一时还难以接受GDDR5那夸张的频率、不明白GDDR相比DDR发展速度为何如此“超前”、甚至搞不清楚GDDR1/2/3/4/5和DDR1/2/3之间“说不清道不明”的关系。

    如果您是一位求知欲很强的电脑爱好者,那么本文非常适合您,笔者特意搜集了大量官方技术文档,为大家献上内存和显存鲜为人知的奥秘……

    近年来CPU(中央处理器)和GPU(图形处理器)的发展速度之快让人目不暇接,新产品的运算能力成倍提升,此时就对内存子系统提出了严峻的需求,因为CPU/GPU运算所需的数据和生成的数据都是来自于内存/显存,如果存储速度跟不上,那么就会浪费很多时间在数据等待上面,从而影响CPU/GPU的性能发挥。

    为了让内存/显存不至于造成瓶颈,芯片厂商都在想方设法的提高带宽:AMD和Intel相继将内存控制器整合在了CPU内部,大大降低了延迟并提高存储效率,Intel旗舰级CPU能够支持三通道内存,带宽提升50%;ATI和NVIDIA也先后使用了512Bit的显存控制器,总带宽倍增。

    是何原因导致业界三大巨头如此大费周折呢?这是因为内存技术的发展速度,其实并不如大家想象中的那么快,受到很多技术难题和传统因素的制约,本文就对内存和显存的发展历程及相关技术进行详细分析。


为让评测文章更具参考价值,同时也让广大网友能够看到自己最感兴趣的内容,泡泡网DIY评测室特意开设了“You ”,或加QQ群共同探讨,我们会有针对性地挑选网友关注的热点进行评测,一旦您的建议被采纳,我们会为您送出精美礼物一份!

内存的存取原理及难以逾越的频障:

    在半导体科技极为发达的台湾省,内存和显存被统称为记忆体(Memory),全名是动态随机存取记忆体(Dynamic Random Access Memory,DRAM)。基本原理就是利用电容内存储电荷的多寡来代表0和1,这就是一个二进制位元(bit),内存的最小单位。


DRAM的存储单元结构图

    DRAM的结构可谓是简单高效,每一个bit只需要一个晶体管加一个电容。但是电容不可避免的存在漏电现象,如果电荷不足会导致数据出错,因此电容必须被周期性的刷新(预充电),这也是DRAM的一大特点。而且电容的充放电需要一个过程,刷新频率不可能无限提升(频障),这就导致DRAM的频率很容易达到上限,即便有先进工艺的支持也收效甚微。

“上古”时代的FP/EDO内存,由于半导体工艺的限制,频率只有25MHz/50MHz,自SDR以后频率从66MHz一路飙升至133MHz,终于遇到了难以逾越的障碍。此后所诞生的DDR1/2/3系列,它们存储单元官方频率(JEDEC制定)始终在100MHz-200MHz之间徘徊,非官方(超频)频率也顶多在250MHz左右,很难突破300MHz。事实上高频内存的出错率很高、稳定性也得不到保证,除了超频跑简单测试外并无实际应用价值。

    既然存储单元的频率(简称内核频率,也就是电容的刷新频率)不能无限提升,那么就只有在I/O(输入输出)方面做文章,通过改进I/O单元,这就诞生了DDR1/2/3、GDDR1/2/3/4/5等形形色色的内存种类,首先来详细介绍下DDR1/2/3之间的关系及特色。

    通常大家所说的DDR-400、DDR2-800、DDR3-1600等,其实并非是内存的真正频率,而是业界约定俗成的等效频率,这些DDR1/2/3内存相当于老牌SDR内存运行在400MHz、800MHz、1600MHz时的带宽,因此频率看上去很夸张,其实真正的内核频率都只有200MHz而已!

    内存有三种不同的频率指标,它们分别是核心频率、时钟频率和有效数据传输频率。核心频率即为内存Cell阵列(Memory Cell Array,即内部电容)的刷新频率,它是内存的真实运行频率;时钟频率即I/O Buffer(输入/输出缓冲)的传输频率;而有效数据传输频率就是指数据传送的频率(即等效频率)。


常见DDR内存频率对照表

    通过上表就能非常直观的看出,近年来内存的频率虽然在成倍增长,可实际上真正存储单元的频率一直在133MHz-200MHz之间徘徊,这是因为电容的刷新频率受制于制造工艺而很难取得突破。而每一代DDR的推出,都能够以较低的存储单元频率,实现更大的带宽,并且为将来频率和带宽的提升留下了一定的空间。

    虽然存储单元的频率一直都没变,但内存颗粒的I/O频率却一直在增长,再加上DDR是双倍数据传输,因此内存的数据传输率可以达到核心频率的8倍之多!通过下面的示意图就能略知一二:

    那么,内存IO频率为什么能达到数倍于核心频率呢?

    相信很多人都知道,DDR1/2/3内存最关键的技术就是分别采用了2/4/8bit数据预取技术(Prefetch),由此得以将带宽翻倍,与此同时I/O控制器也必须做相应的改进。

DDR1/2/3数据预取技术原理:

    预取,顾名思义就是预先/提前存取数据,也就是说在I/O控制器发出请求之前,存储单元已经事先准备好了2/4/8bit数据。简单来说这就是把并行传输的数据转换为串行数据流,我们可以把它认为是存储单元内部的Raid/多通道技术,可以说是以电容矩阵为单位的。


内存数据预取技术示意图:并行转串行

    这种存储阵列内部的实际位宽较大,但是数据输出位宽却比较小的设计,就是所谓的数据预取技术,它可以让内存的数据传输频率倍增。试想如果我们把一条细水管安装在粗水管之上,那么水流的喷射速度就会翻几倍。

    明白了数据预取技术的原理之后,再来看看DDR1/2/3内存的定义,以及三种频率之间的关系,就豁然开朗了:

    之所以被称为“同步”,因为SDR内存的存储单元频率、I/O频率及数据传输率都是相同的,比如经典的PC133,三种频率都是133MHz。

    SDR在一个时钟周期内只能读/写一次,只在时钟上升期读/写数据,当同时需要读取和写入时,就得等待其中一个动作完成之后才能继续进行下一个动作。

    双倍是指在一个时钟周期内传输两次数据,在时钟的上升期和下降期各传输一次数据(通过差分时钟技术实现),在存储阵列频率不变的情况下,数据传输率达到了SDR的两倍,此时就需要I/O从存储阵列中预取2bit数据,因此I/O的工作频率是存储阵列频率的两倍。

    DQ频率和I/O频率是相同的,因为DQ在时钟上升和下降研能传输两次数据,也是两倍于存储阵列的频率。

    DDR2在DDR1的基础上,数据预取位数从2bit扩充至4bit,此时上下行同时传输数据(双倍)已经满足不了4bit预取的要求,因此I/O控制器频率必须加倍。

    DDR3就更容易理解了,数据预取位数再次翻倍到8bit,同理I/O控制器频率也加倍。此时,在存储单元频率保持133-200MHz不变的情况下,DDR3的实际频率达到了533-800MHz,而(等效)数据传输率高达MHz。

综上可以看出,DDR1/2/3的发展是围绕着数据预取而进行的,同时也给I/O控制器造成了不小的压力,虽然存储单元的工作频率保持不变,但I/O频率以级数增长,我们可以看到DDR3的I/O频率已逼近1GHz大关,此时I/O频率成为了新的瓶颈,如果继续推出DDR4(注意不是GDDR4,两者完全不是同一概念,后文会有详细解释)的话,将会受到很多未知因素的制约,必须等待更先进的工艺或者新解决方案的出现才有可能延续DDR的生命。

    前面介绍的是关于历代内存的技术原理,可以说是比较微观的东西,反映在宏观上,就是常见的内存颗粒及内存条了,这都是些看得见摸得着的东西,但有些概念还是不容易理解,这里一一进行说明:

    内存模组的设计取决于内存控制器(集成在北桥或者CPU内部),理论上位宽可以无限提升,但受制因素较多:高位宽将会让芯片组变得十分复杂,对主板布线提出严格要求,内存PCB更是丝毫马虎不得,内存颗粒及芯片设计也必须作相应的调整。可谓是牵一发而动全身,所以多年来业界都是墨守成规,维持64bit的设计不变。

    相比之下,显卡作为一个整体就没有那么多的顾忌,只需重新设计GPU内部的显存控制器,然后PCB按照位宽要求布线,焊更多的显存颗粒上去就行了,虽然成本也很高但实现512bit并没有太大难度。

多通道内存——双通道/三通道

    既然实现高位宽内存条太难,那么就退而求其次,让两条内存并行传输数据,同样可以让位宽翻倍。目前流行的双通道技术就是如此,北桥或者CPU内部整合了两个独立的64bit内存控制器,同时传输数据等效位宽就相当于128bit。

    Intel Nehalem核心CPU直接整合三通道内存控制器,位宽高达192bit。但由于CPU、主板、内存方面成本都增加不少,因此在主流Lynnfield核心CPU上面又回归了双通道设计。事实上服务器芯片组已经能够支持四通道内存,对服务器来说成本方面不是问题,只是对稳定性和容错性要求很高。

    理论上,完全可以制造出一颗位宽为64bit的芯片来满足一条内存使用,但这种设计对技术要求很高,良品率很低导致成本无法控制,应用范围很窄。

    所以内存芯片的位宽一般都很小,台式机内存颗粒的位宽最高仅16bit,常见的则是4/8bit。这样为了组成64bit内存的需要,至少需要4颗16bit的芯片、8颗8bit的芯片或者16颗4bit的芯片。

    而显卡对位宽要求很高,容量反而退居其次,所以显存颗粒的位宽普遍比内存颗粒大(这就是显存和内存主要区别之一),比如GDDR3/4/5颗粒都是32bit,4颗就能满足低端卡128bit的需要,8颗可以满足高端卡256bit的需要;而低端GDDR2颗粒为16bit,需要8颗才能组成低端卡128bit的需要。

● 内存芯片的逻辑Bank

    在芯片的内部,内存的数据是以bit为单位写入一张大的矩阵中,每个单元称为CELL阵列,只要指定一个行一个列,就可以准确地定位到某个CELL,这就是内存芯片寻址的基本原理。这个阵列我们就称为内存芯片的BANK,也称之为逻辑BANK(Logical BANK)。

    不可能只做一个全容量的逻辑Bank,因为单一的逻辑Bank将会造成非常严重的寻址冲突,大幅降低内存效率。所以大容量内存颗粒都是由多个逻辑Bank叠加而成的。简单来说,我们可以把一个Bank看作是一片平面的矩阵纸,而内存颗粒是由多片这样的纸叠起来的。

    一个Bank的位宽就是内存颗粒的位宽,内存控制器一次只允许对一个Bank进行操作,由于逻辑Bank的地址线是公用的,所以在读写时需要加一个逻辑Bank的编号,这个动作被称为片选。

● 内存条的物理Bank

    内存控制器的位宽必须与内存条的位宽相等,这样才能在一个时钟周期内传输所有数据,这个位宽就被成为一个物理Bank(通常是64bit),每条内存至少包含一个Bank,多数情况下拥有二个物理Bank。

    一个物理Bank不会造成带宽浪费,理论上是最合理的配置,但为了实现大容量内存,单条内存多物理Bank也是允许的,但内存控制器所能允许的最大Bank数存在上限,常见的是双物理Bank设计,只有特殊内存或者服务器内存才会使用四Bank以上的设计,因为这种内存兼容性不好,“挑”芯片组。

    事实上显卡上也存在双物理Bank设计,目的就是为了实现超大显存容量,比如1GB的9800GT,正反两面共有16颗16M×32bit的GDDR3显存,总位宽达512bit,实际上显存控制器只支持256bit,这样就是双物理Bank。

    早在SDRAM时代,显卡上用的“显存颗粒”与内存条上的“内存颗粒”是完全相同的。在那个时候,GPU本身的运算能力有限,对数据带宽的要求自然也不高,所以高频的SDRAM颗粒就可以满足要求。


某TNT2显卡,使用的是PC166的SDR内存颗粒

内存满足不了显卡的需求,显存应运而生

    本是同根生的状况一直持续到SDR和DDR交接的时代,其实最早用在显卡上的DDR颗粒与用在内存上的DDR颗粒仍然是一样的。后来由于GPU特殊的需要,显存颗粒与内存颗粒开始分道扬镳,这其中包括了几方面的因素:

    1. GPU需要比CPU更高的带宽。GPU不像CPU那样有大容量二三级缓存,GPU与显存之间的数据交换远比CPU频繁,而且大多都是突发性的数据流,因此GPU比CPU更加渴望得到更高的显存带宽支持。

    位宽×频率=带宽,因此提高带宽的方法就是增加位宽和提高频率,但GPU对于位宽和频率的需求还有其它的因素。

    2.显卡需要高位宽的显存。显卡PCB空间是有限的,在有限的空间内如何合理的安排显存颗粒,无论高中低端显卡都面临这个问题。从布线、成本、性能等多种角度来看,显存都需要达到更高的位宽。

    最早的显存是单颗16bit的芯片,后来升级到32bit,将来甚至还会有更高的规格出现。而内存则没有那么多要求,多年来内存条都是64bit,所以单颗内存颗粒没必要设计成高位宽,只要提高容量就行了,所以位宽一直维持在4/8bit。

    3.显卡能让显存达到更高的频率。显存颗粒与GPU配套使用时,一般都经过专门的设计和优化,而不像内存那样有太多顾忌。GPU的显存控制器比CPU或北桥内存控制器性能优异,而且显卡PCB可以随意的进行优化,因此显存一般都能达到更高的频率。而内存受到内存PCB、主板走线、北桥CPU得诸多因素的限制很难冲击高频率

  由此算来,显存与内存“分家”既是意料之外,又是情理之中的事情了。为了更好地满足显卡GPU的特殊要求,一些厂商(如三星等)推出了专门为图形系统设计的高速DDR显存,称为“Graphics Double Data Rate DRAM”,也就是我们现在常见的GDDR。

GDDR——显存和内存正式分家

    GDDR作为第一代专用的显存芯片,其实在技术方面与DDR没有任何区别,同样采用了2bit预取技术,理论频率GDDR并不比DDR高多少。不过后期改进工艺的GDDR有了优秀PCB的显卡支持之后,GDDR显存最高冲刺至900MHz,而DDR内存只能达到600MHz左右,显存和内存的差距从此逐渐拉开。

    TSOP封装的GDDR颗粒,外观规格特性都与DDR内存颗粒没有什么区别,所以在很多人看来“GDDR”与“DDR”是可以“划等号”的。其实两者还是有些差别:

  • GDDR采用4K循环32ms的刷新周期,而DDR采用8K循环64ms的刷新周期;

  • GDDR为了追求频率在延迟方面放的更宽一些,毕竟GPU对延迟不太敏感;

  • GDDR颗粒的容量小、位宽大,一般是8×16Bit(16MB)的规格,而DDR颗粒的容量大、位宽小,虽然也有16Bit的颗粒,但最常见的还是8Bit和4Bit,单颗容量32MB或64MB。

    为了实现更大的位宽,并进一步提升GDDR的性能,后期很多厂商改用了电气性能更好的MBGA封装,当然也有内存颗粒使用MBGA封装,但规格已有了较大差异,主要是颗粒位宽不同。

    GDDR显存的这两种封装:MBGA与TSOP构成的高低配,曾一度一统显卡市场。虽然GDDR已经退出历史舞台,但32Bit主攻中高端、16Bit主攻低端的局面,时至今日依然得到了延续。

GDDR2第一版:短命的早产儿 高压高发热

    GDDR2源于DDR2技术,也就是采用了4Bit预取,相比DDR1代可以将频率翻倍。虽然技术原理相同,但GDDR2要比DDR2早了将近两年时间,首次支持DDR2内存的915P主板于2004年中发布,而首次搭载GDDR2显存的FX5800Ultra于2003年初发布,但早产儿往往是短命的。

    NVIDIA在设计NV30芯片时依然保持128Bit显存位宽,为了提高带宽必须使用高频显存,700MHz的GDDR已经无法满足需求了,于是冒险尝试GDDR2。第一代GDDR2受制造工艺限制,电压规格还是和DDR/GDDR一样的2.5V,虽然勉强将频率提升至1GHz左右,但功耗发热出奇的大。


FX5800Ultra需要为显存专门安装厚重的散热片

    GDDR2虽然坏毛病一大堆,但它也拥有一些新的特性,比如首次使用片内终结电阻,PCB设计比GDDR更加简洁,这个特性被后来的gDDR2和GDDR3继承。

    由于第一代GDDR2的失败,高端显卡的显存是直接从GDDR跳至GDDR3的,但GDDR2并未消亡,而是开始转型。几大DRAM大厂有针对性的对GDDR2的规格和特性做了更改(说白了就是DDR2的显存版),由此gDDR2第二版正式登上显卡舞台,时至今日依然活跃在低端显卡之上。

  • 工作电压从2.5V降至1.8V,功耗发热大降;

  • 制造工艺有所进步,功耗发热进一步下降,成本降低,同时良率和容量有所提升;

  • 颗粒位宽从32Bit降至16Bit,只适合低端显卡使用;

  • 封装形式从144Ball MBGA改为84Ball FBGA,外观上来看从正方形变成长方形或者长条形;


注意三星官方网站对于显存的分类

    相信很多朋友也注意到了,本页gDDR2的第一个字母为小写,几大DRAM厂商在其官方网站和PDF中就都是这么写的,以示区分。我们可以这么认为:大写G表示显卡专用,32bit定位高端的版本;而小写g表示为显卡优化,16bit定位低端的版本,本质上与内存颗粒并无区别。

无论GDDR还是GDDR2,由于在技术方面与DDR/DDR2并无太大差别,因此最终在频率方面GDDR并不比DDR高太多。在经历了GDDR2的失败之后,两大图形巨头NVIDIA和ATI对JEDEC组织慢如蜗牛般的标准制订流程感到越来越失望,认为他们制定的显存不能适应GPU快节奏的产品更新换代周期,于是NVIDIA和ATI的工作人员积极参与到了JEDEC组织当中,以加速显存标准的起草及制定。

    双方一致认为,显存与内存在数据存储的应用方面完全不同,在内存核心频率(电容刷新频率)无法提升的情况下,单纯提高I/O频率来获得高带宽很不现实。因此,必须要有一种针对高速点对点环境而重新定义的I/O接口。于是GDDR3诞生了,这是第一款真正完全为GPU设计的存储器。

    GDDR2只有一条数据选择脉冲(DQS),是单一双向的,而GDDR3则拥有读与写两条独立的DQS,而且是点对点设计。这样做的好处在于,在读取之后如果马上进行写入时,不必再等DQS的方向转变,由此实现读写操作的快速切换。

    相比GDDR2/DDR2,GDDR3的读写切换动作可以少一个时钟周期,如果需要对某一个连续的区块同时读写数据时,GDDR3的速度就要比GDDR2快一倍。

由于存储单元自身的特性,内存颗粒的逻辑Bank是无法同时读写数据的,并不存在“全双工”一说,但GDDR3的这项改进让顺序读写成为可能。GPU本身缓存很小,与显存之间的数据交换极其频繁,读写操作穿插进行,因此GDDR3点对点设计的DQS可以让显存存储效率大增。但对于CPU来说,读写切换并不如GPU那么频繁,而且CPU拥有大容量的二三级缓存,所以GDDR3这种设计并不能极大的提升内存带宽,也没有引入到下一代DDR3当中。

    同时GDDR3也对I/O控制电路和终结电阻进行了修改,它不再沿用GDDR2的“推式(Push Pull)”接收器,而将其改为虚拟开极逻辑方式(Pseudo Open Drain Logic),并且通过将所有的三相数据信号转移到本位电路上,来简化数据处理,将DC电流压至最小,只有当逻辑LOW移至总线上时才会消费电力,从而很好的控制了功耗和发热。

    GDDR3的频率能达到现在这么高,其实并没有什么诀窍,凭借的就是不断改进的工艺制程,来暴力拉升频率。资历稍老点的玩家应该知道,GDDR3于2004年初次登台亮相时,6600GT的显存频率仅为1GHz,并不比GDDR2高,5年过去了,GDDR3从1GHz一路攀升至2GHz甚至2.5GHz,生命力得到了延续。

    为了提高电气性能和环保水平,从2005年开始,GDDR3开始采用全新的136Ball FBGA封装,并统一使用无铅封装工艺。新封装使得显卡PCB必须重新设计,但也为GDDR3的腾飞铺平了道路。

  • 伴随着制造工艺的进步,额定电压从2.0V进一步降至1.8V,但一些高频颗粒可适当加压;

  • 速度从1.4ns起跳,经过1.2ns、1.1ns、1.0ns一路发展至0.8ns、0.7ns,最快速度可突破2500MHz,但这是以牺牲延迟为代价的,好在GPU对延迟不太敏感;

    当GDDR3的频率首次达到2000MHz时,很多人都认为离极限不远了,于是未雨绸缪的抓紧制定GDDR4规范,但没想到在DRAM厂商的努力及新工艺的支持下,GDDR3的生命得到了延续,0.8ns 0.7ns的型号相继量产,而且容量更大的32M×32Bit颗粒也成为主流,基本上能够满足高中低端所有显卡的需要。

    当年2.2ns GDDR最高可达900MHz,核心频率和I/O频率止步于450MHz。经过5年时间的发展,GDDR3凭借新工艺终于在核心频率和I/O频率方面取得突破,核心频率可达600MHz以上,I/O频率超过1200MHz,此时过高的I/O频率成为了新的瓶颈。

    GDDR3采用了DDR2的4bit预取技术,所以采用DDR3 8bit预取技术的显存只能按顺序命名为GDDR4。GDDR4是在GDDR3的基础上发展而来的,它继承了GDDR3的两大技术特性,但内核改用DDR3的8bit预取技术,并加入了一些新的技术来提升频率。

GDDR4的技术特性:

  • 使用DDR3的8bit预取技术,以较低的核心频率达到更高带宽,但延迟增加;

  • 采用数据总线转位技术(DBI,Data Bus Inversion,下文做详细介绍),提高数据精度,降低功耗;

  • 地址线只有GDDR3的一半,多余线用于电源和接地,有利于提升频率,但导致延迟增加;

  • 采用多重同步码(Multi-Preamble)技术,解决了GDDR3存在的爆发限制(Burst LimitATIon),从连续地址读取少量数据时的性能大幅提升;


GDDR4的确更好超,但性能提升有限

    由于采用了8bit预取技术,因此在相同频率下GDDR4的核心频率(即电容刷新频率)只有GDDR3的一半,理论上来讲GDDR4最高频率可达GDDR3的两倍。但值得注意的是,虽然核心频率通过8bit预取技术减半,但GDDR4与GDDR3的I/O频率是完全相同的,因此GDDR4频率提升的瓶颈在于I/O频率而不是核心频率。

    由于制造工艺和技术水平的限制,虽然三星官方宣称早已生产出3GHz以上的GDDR4,但实际出货的GDDR4只有2GHz-2.5GHz,此后改进工艺的GDDR3也追平了这一频率。在相同频率下,GDDR4比起GDDR3虽然功耗发热低,但延迟大性能稍弱,再加上成本高产量小,GDDR4遭受冷落并不意外。

导致GDDR4失败的非技术方面原因

    GDDR3是NVIDIA和ATI参与JEDEC组织后共同制定的显存标准,而GDDR4在标准制定过程中双方产生了较大的分歧。NVIDIA较为保守,认为应该保持DDR2 4bit预取技术不变,继续改进I/O控制器来提升频率;而ATI则比较激进,准备直接使用DDR3 8bit预取技术。

    双方争执的结果就是在JEDEC组织中德高望重的ATI获胜(据称ATI有位高层在JEDEC身居要职),而NVIDIA则明确表示不支持GDDR4。因此GDDR4其实就是ATI一手策划的,但得不到NVIDIA支持的话,GDDR4立马就失去了6成以上的市场,由此导致DRAM厂不敢贸然投产。

    最终只有三星一家生产了少量的GDDR4显存,其他家都在观望。当然其他DRAM厂商都没闲着,它们把精力都投在了深挖GDDR3的潜力当中,于是我们看到了GDDR3的频率节节攀升,GDDR4在没有成本优势的情况下,也没有频率优势,恰好当时的几代A卡更没有性能优势,GDDR4自然只有死路一条。

    只有ATI生产过搭载GDDR4的显卡,数量虽然不多但横跨了三代产品:X1950XTX、HD2600XT和HD3870(也包括对应的专业卡)——与当年NVIDIA使用GDDR2的显卡数量相等。NVIDIA在遭遇滑铁卢后果断放弃了GDDR2,而ATI对于GDDR4则是难以割舍,三年时间三代产品都有使用,但一直都是非主流。

    GDDR4的失败并不是技术原因,和当年的GDDR2相比它要成熟很多,没推起来的原因主要是对手太强:ATI的对手NVIDIA很强大,另外GDDR4的对手GDDR3生命力太顽强了。

    即便使用了8bit预取技术,可GDDR4还是没有与GDDR3拉开频率差距,因为瓶颈在I/O控制器上面而不是内核,而GDDR5就是用来解决这一瓶颈的。

GDDR5:恐怖的频率是如何达成的

    和GDDR4一样,GDDR5采用了DDR3的8bit预取技术,核心频率显然不是瓶颈,如何提升I/O频率才是当务之急。但GDDR5并没有让I/O频率翻倍,而是使用了两条并行的DQ总线,从而实现双倍的接口带宽。


GDDR5各项总线工作频率示意图

    双DQ总线的结果就是,GDDR5的针脚数从GDDR3/4的136Ball大幅增至170Ball,相应的GPU显存控制器也需要重新设计。GDDR5显存拥有多达16个物理Bank,这些Bank被分为四组,双DQ总线交叉控制四组Bank,达到了实时读写操作,一举将数据传输率提升至4GHz以上!

以往GDDR1/2/3/4和DDR1/2/3的数据总线都是DDR技术(通过差分时钟在上升沿和下降沿各传输一次数据),官方标称的频率X2就是数据传输率,也就是通常我们所说的等效频率。而GDDR5则不同,它有两条数据总线,相当于Rambus的QDR技术,所以官方标称频率X4才是数据传输率。比如HD4870官方显存频率是900MHz,而大家习惯称之为3600MHz。

    GDDR4的失败并没有阻挡ATI前进的脚步,在意识到GDDR4频率提升的瓶颈之后,GDDR5草案的制定就被提上日程,ATI和NVIDIA技术人员重新聚首,开展第二次合作共商大计。GDDR5吸取了前辈们的诸多优点,可谓是取其精华弃其糟粕,在I/O改进方面双方也不再有太多矛盾。

    技术方面的问题不难解决,最难的是时间和进度。ATI在R600上面冒险使用512Bit显存控制器来提升显存带宽,结果输得一败涂地,于是RV670只好回归256Bit,导致性能原地踏步。而GDDR4相比GDDR3没有频率优势,因此ATI迫切的需要GDDR5迅速投产以满足新一代GPU的需要,RV770只有256Bit,急需高频显存的支持。

    以256Bit对抗512Bit,ATI只能将筹码全部押在GDDR5身上,于是在GDDR5标准尚未完全确立之前,ATI已经在紧锣密鼓的测试性能,并督促DRAM厂投产。可以说GDDR5和GDDR2/4一样也是个早产儿,但失败乃成功之母,有了完善的技术规格和制造工艺的支持,GDDR5一出世便令人刮目相看。

    GDDR5在GDDR3/4优秀特性的基础上,还有诸多改进和新特性,下面就对它们进行详细分析。

  • 数据和地址总线转位技术:信号质量高、功率消耗少

    在1Byte数据中的8个值中,如果超过一半的数值是0,那么GDDR5就会自动执行转位传输,把0变成1、1变成0,通过1个附加的DBI(数据总线转位值)来判定数据流是正位还是反位。GDDR5的这项技术是从GDDR4继承发展而来的。

    DRAM在传输数据时,只有0会消耗电能,减少0的传输数量,既能保证信号质量,也能减少内部终结电阻和外部终结电路的功率消耗。GDDR5的地址总线也使用了类似的技术,通过额外的ABI通道来转位数据流,从而较少信号噪声,并降低功耗。

  • 智能的可编程I/O控制接口:简化PCB设计和成本

    GDDR5对I/O控制器做了很多改进,加入了全新的自动校准引擎,保证GDDR5显存颗粒更好的适应GPU显存控制器的需求,确保数据传输稳定可靠。

    自动校准引擎可以监控电压和温度变化,通过校验数据输出驱动器导通电阻与ODT终结电阻值来作出补偿,数据、地址、指令终结电阻都可以被软件或驱动控制。


GDDR5的针脚更多,但布线更简洁

    此外GDDR5还能支持时间延迟和信号强度调整,灵活的协调数据同步,以往通过“蛇形走线”平衡延迟的方法彻底成为历史,GDDR5没有这种顾虑,因此能极大的简化PCB布线和成本,并有利于冲击更高频率。

  • 数据遮盖技术:减轻数据总线压力

    GDDR5的Burst Length(对相邻存储单元连续进行数据传输的周期数)是8bit,也就是说GDDR5颗粒一次至少要传输256bit数据,但很多时候并不是所有的数据都需要被改写,导致无效的数据传输。

    为此,GDDR5使用了一项数据遮盖技术,通过地址线传输保护信息,所有被保护的数据在传输过程中就不会被改写,只有暴露的数据才会被写入新的数据。如此以来,GDDR5的数据线压力减轻不少,功耗发热也得到进一步控制。

  • 误差补偿技术:提高传输效率,避免灾难性错误

    为了保证数据在高速传输过程中的有效性,GDDR5新增一项错误侦测与修正技术。GDDR5使用了成熟的CRC(循环冗余校验),通过DQ和DBI总线,实时检查错误,第一时间重新发送数据。

    这项技术对于高频率传输数据尤为重要,它能有效的减少数据传输错误导致系统崩溃的概率,大幅减少了由超频或高温导致的一系列问题,而且能够一定程度上提升数据传输效率。

    GDDR5作为高端显卡专用的显卡,只有32bit的颗粒。由于GDDR5拥有两条并行的数据总线,这就使得GDDR5的工作模式变得更加灵活,它既可以工作在32bit模式下也可以工作在16bit模式下。这样一个32bit显存控制器就可以控制两颗GDDR5显存,显存容量可以轻松翻倍。

    其实,GDDR3/4都可以通过这种方式扩充显存容量,但原理则完全不同。此前必须GPU的显存控制器在设计时支持双Bank模式才能支持更多的显存颗粒。而现在,8颗GDDR5显存总计256bit可以直接被128bit的GPU使用,从而简化了显存控制器设计,HD4770就是很好的例子。

    之前我们分析过,TSOP封装的GDDR1还有gDDR2显存,其实在技术上与DDR1/2内存没有本质区别,高位宽(16bit)的内存颗粒可以直接当作显存使用。随着DDR3颗粒大量投产,成本接近DDR2,于是在DDR3内存取代DDR2的同时,也将顺便取代老旧的gDDR2。

gDDR3:把内存颗粒改装成显存用

    以目前的情况来看,DDR3比gDDR2频率高很多,但成本比GDDR3要低,所以gDDR2被取代是板上钉钉的事。AMD率先将DDR3使用在了显卡上,随后得到了业界的一致认可。

可以看出,在高端GDDR5将会取代GDDR3,而低端gDDR3将会取代gDDR2,中端则会出现三代共存的局面。虽然gDDR3单颗位宽只有GDDR3的一半,但存储密度却是GDDR3的两倍,而且在相同频率下(比如2000MHz),gDDR3的核心频率是GDDR3的一半,因此功耗发热要低很多。对于位宽不高的中低端显卡来说,gDDR3大容量、低成本、低功耗发热的特性简直相当完美!

    上图就是现代官方网站列出的gDDR3和GDDR3两种显存的规格参数表,注意它们的全称,是否有"G",真的是差之毫厘谬以千里。

    在前文的内存部分,关于内存颗粒的位宽、通道、Bank等做了一些介绍,这些技术参数对于显存同样适用,但显存也有自己的一套规格定义,下面就逐一介绍:

● 规格:16M×32Bit是什么意思?

    当您浏览网站或者查看显卡规格时,往往都会看到类似“某某显卡使用了4颗16M×32Bit的GDDR3显存”这样的文字,这其中16M×32Bit就是该显存颗粒的主要规格,是国际统一的命名标准,可以到存储厂商官方网站上查到。

    16M×32Bit中,16M表示显存存储单元的容量为16Mbit,32Bit是单颗显存的数据位宽,这种标称不容易理解,需要经过换算才能得到符合我们使用习惯的规格。

● 容量:单颗显存容量=存储单元容量×数据位宽/8

    很多人可能会纳闷上面的公式中为何要除以8,因为官方规格中的16M的单位是Megabit(兆位)而不是MegaByte(兆字节),它两之间的换算需要除以8。

● 速度:显存理论频率=1000/时钟周期×2

    大家常说某某显卡采用了1.4ns颗粒,另一个显卡用了更快的1.2ns颗粒,超频更猛等等……这个1.2ns就是显存的时钟周期,同样的我们需要换算成更容易理解的数字。

    套用以上公式,我们来算算主流规格显存的理论频率是多少:

    为什么要乘以2,因为DDR系列存储颗粒属于双倍传输,在工作频率和数据位宽相同的情况下,显存带宽是SDRAM的2倍,因此大家习惯于在基础频率上乘2,超高的频率确实比较好看。

● 位宽:显存位宽=单颗显存数据位宽×显存数量

    这个不难理解,比如显卡使用了4颗16M×32bit GDDR3显存,那么位宽就是32bit×4=128bit。需要注意的是,并非所有情况下这个公式都成立,除了显存数量之外,GPU显存控制器的位宽决定了显卡位宽上限。

    低端显卡核心拥有128Bit显存控制器,因此4颗GDDR3显存就能满足位宽需求,即便PCB上集成了8颗显存,显卡位宽依然是128bit。如果是中端显卡的话,8颗显存正好是256Bit,与核心相吻合。

● 带宽:显存带宽=显存位宽×显存工作频率/8

    单纯看显存位宽意义并不大,最终影响显卡性能的其实是带宽。我们可以把带宽比作是马路的车行流量,显然马路越宽(显存位宽),车速越高(显存频率),最终的带宽就越高。

    带宽是显存速度的最终衡量,有些显卡的显存频率高,但是位宽低,最典型的就是使用GDDR5显存的HD4870,位宽256bit但频率高达3600MHz,最终计算得带宽就是115GB/s,和GTX260相当。

● 显存引领DRAM发展,未来内存将以显存为蓝本开发

    纵观近年来内存与显存的发展,就会发现显存的发展速度已经远远超越了内存,显存带宽几乎达到了内存带宽的10倍之多,而且这个差距还在不断的加大。目前三通道DDR3已经足够桌面CPU用好一阵子了,而GPU对显存带宽的渴求似乎是个永远都填不满的无底洞。

    正因为如此,显存逐渐脱离了内存的发展轨迹,在经过几次并不成功的尝试之后,从内存的配角/附属品,开始走向了反客为主的道路。GDDR2提前DDR2近两年、GDDR4提前DDR3一年多,虽然都以失败而告终,但却为GDDR5的成功打下了坚实的基础。

    在内存领域,如今DDR3才刚刚站稳脚跟,至少将统治PC两至三年,但DDR4的标准已经在积极制定当中,而其技术规格将会以GDDR5为蓝本——也就是说保持DDR3 8bit预取技术不变,改进I/O控制器,个中原因相信认真阅读了本文的朋友们应该知道吧。

我要回帖

更多关于 什么叫双时钟 的文章

 

随机推荐