计算机组成与设计-阅读笔记(一)

计算机体系结构中的8个伟大思想

  1. 面向摩尔定律的设计
    摩尔定律(Moore's law) 指出单块芯片上所集成的晶体管的数量每18至24个月翻一番。通常计算机芯片的设计需要花费数年的时间,因此在项目结束的时候,每块芯片上所集成的晶体管数量相较于最初的设计可轻易地实现双倍甚至四倍的增长。因此,计算机架构师必须预测其设计完成时的工艺水平,而不是设计开始时的工艺水平。
  2. 使用抽象简化设计
    计算机架构师和程序员都必须发明新技术来提高自己的工作效率,否则根据摩尔定律,设计时间会随着资源的增长而显著延长。而提高硬件和软件的生产率的主要技术之一就是使用抽象(abstraction) 来表示不同的设计层次,即隐藏底层细节以提供给高层一个更简单的模型。
  3. 加速经常性事件
    加速经常性事件(make the common case fast)对性能的提升远比优化罕见事件要好。
  4. 通过并行提高性能
  5. 通过流水线提高性能
    流水线是并行性的一种体现,它在计算机体系结构中被普遍使用。
  6. 通过预测提高性能
    假设从预测错误中恢复的代价并不高,并且预测相对准确,则平均来说进行预测并开始工作可能会比等到明确结果后再执行更快。
  7. 存储层次
    存储器的速度会影响性能,其容量限制了可被解决的问题的规模,并且内存成本是计当今计算机成本的主要部分,程序员希望存储器有着更快的速度、更大的容量以及更低的位单价。计算机架构师通过存储层次(hierachy of memory)来处理这些冲突的需求。在存储层次中,速度快、容量小、位单价高的存储器位于顶层,而速度慢、容量大、位单价低的存储器位于底层。这种层次结构给了程序员这样的错觉:主存与顶层的存储器一样快,且与底层的存储器有着几乎一样大的容量和便宜的价格。
  8. 通过冗余提高可靠性
    计算机不仅要求速度快,还需要工作是可靠的。由于任何物理设备都可能发生故障,因此我们引入冗余组件来使系统可靠。

程序的表象之下

一个典型的应用程序可能包含数万至数百万行代码构成,并且依靠函数库来实现异常复杂的功能。总所周知,硬件只能执行极为简单的低级指令。从复杂的应用程序到原始的指令的过程涉及了若干软件层次来将高层次的操作解释或翻译为简单的计算机指令。

下面是一个简化了的软硬件结构层次图:

系统软件有很多种,其中操作系统编译器是现代计算机系统所必须具备的。操作系统是用户程序与硬件之间的接口;而编译器的功能是将高级语言编写的程序翻译成硬件能执行的指令。

从硬件语言到高级语言

计算机由很多个电子元器件组成,在这些电子元器件种高电平表示1,低电平表示0,这就是二进制表示,因此我们通常认为计算机语言就是二进制数。计算机使用一串二进制位来表示数据或指令,数据不必多解释,指令是能被计算机识别并执行的位串。

编程语言的不断抽象:

  1. 机器语言
    第一代程序员直接使用二进制数与计算机交互,这是一种非常乏味的工作。
  2. 汇编语言
    为了避免阅读大串的0和1的痛苦,程序员开始使用助记符来代替机器指令,并且开发了一种叫做汇编器的软件来将助记符自动翻译为相应的机器指令。但是,由于汇编指令与机器指令是一一对应的关系,程序员仍然需要像机器一样思考问题。
  3. 高级语言
    为了摆脱按机器思维编写代码的模式,人们提出了高级语言,并开发了相应的翻译软件——编译器,它可以将高级语言指令翻译成机器指令。高级语言的出现使得程序员可以使用更自然的语言来思考、编写代码;软件开发效率提高;提高了程序与具体硬件的之间的独立性,因为同样的高级语言代码只需有合适的编译器,它就可以运行在各个平台上。

箱盖后的硬件

构成计算机的五个经典部件是:运算器、控制器、存储器、输入和输出。

无论是硬件还是软件都可以使用抽象将其分成多个层次,下层对上层隐藏实现细节。计算机系统抽象层次中的一个关键接口是指令系统层次结构——硬件和底层软件之间的接口。通过这一抽象接口,同意软件可以由成本不同、性能也不同的实现方法来完成。

软件或硬件组成成分 该部分如何影响性能
算法 决定了源码级语句的数量和执行I/O操作的数量
编程语言、编译器和体系结构 决定了每条源码级语句对应的计算机指令数量
处理器和存储系统 决定了指令执行速度
I/O系统(硬件和操作系统) 决定了I/O操作可能的执行速度

性能

性能的定义

两个性能的度量参数:

  1. 响应时间:也称执行时间,是计算机完成某任务所需的总时间(包括IO、内存访问、操作系统开销和CPU执行时间)
  2. 吞吐量:也称带宽,表示单位时间内完成的任务数量

现在主要考虑响应时间,对于某个机器(X),我们可将其性能和执行时间的关系表示为:

[性能_X = frac{1}{执行时间_X} ]

这意味着,如果有两台计算机(X)(Y)(X)的性能比(Y)的性能更好,则有

[性能_X > 性能_Y ]

[frac{1}{执行时间_X} > frac{1}{执行时间_Y} ]

综上可以写出计算机(X)(Y)的性能之比为两者执行时间之比的倒数:

[frac{性能_X}{性能_Y} = frac{执行时间_Y}{执行时间_X} = n ]

性能的度量

时间是计算机性能的度量标准:完成相同的计算任务,需要时间最少的就是性能最高的。
计算机常常被共享使用,一个处理器也可能同时运行多个程序。在这种情况下,系统可能更侧重于优化吞吐率,而不是优化单个程序的执行时间。因此使用CPU时间来表示该任务在CPU上花费的时间,而不包括等待I/O或运行其他程序的时间。CPU时间还可进一步分为用于用户程序的时间和操作系统为用户程序执行相关任务所花去的CPU时间,前者称为用户CPU时间,后者称为系统CPU时间

计算机各硬件通过时钟信号来协同工作,时钟信号之间的时间间隔称为时钟周期长度或简称为时钟周期,这些离散时间间隔个数被称为时钟周期数。在涉及人员提及时钟周期时,可能使用完整的时钟周期时间(250ps),也可能使用时钟周期的倒数,即时频率(如4GHz)

下面的公式将时钟周期、时钟周期数与CPU时间联系起来:

[程序的CPU执行时间=程序的CPU时钟周期数 imes 时钟周期=frac{程序的CPU时钟周期数}{时钟频率}]

这个公式清晰地表明,想要改进性能,就需要减少程序执行所需的CPU时钟周期数或者缩短时钟周期的长度。对其中之一的修改可能会影响另一方,因此设计者经常要面对此二者的权衡。

指令的性能

上述性能公式并未涉及程序所需的指令数量,然而计算机必须通过执行指令来运行程序,因此执行时间的计算必须依赖程序种的指令数。一种考虑方法是执行时间等于指令数乘以每条指令的平均时间,公式如下:

[CPU时钟周期数 = 程序的指令数 imes 指令平均时钟周期数 ]

不同的指令有着不同的CPI(指令平均时钟周期),因此CPU时钟周期可写为:

[CPU时钟周期数 = sum_{i=1}^{n}({CPI}_i imes C_i) ]

求CPI的公式为:

[CPI = frac{CPU时钟周期数}{指令数} ]

经典的CPU性能公式

现在,我们可以使用指令数、CPI和时钟周期来写出基本性能公式:

[CPU时间 = 指令数 imes CPI imes 时钟周期 ]

考虑到时钟频率和时钟周期长度互为倒数,该式可写为:

[CPU时间 = frac{指令数 imes CPI}{时钟频率} ]

硬件或软件指标 影响什么 如何影响
算法 指令数 CPI 算法决定源程序语句的数目,从而决定了CPU执行指令的数目。算法也可能通过较快或较慢的指令影响CPI。例如当算法使用更多的除法运算时,将会导致CPI增大
编程语言 指令数 CPI 编程语言显然会影响指令数,因为编程语言种的语句翻译为指令,从而决定了指令数。编程语言也可影响CPI,例如Java语言充分支持数据抽象,因此将进行间接调用,需要使用CPI较高的指令
编译器 指令数 CPI 编译器决定了源程序到计算机指令的翻译过程,所以编译器的效率级映像指令数又影响CPI。编译器的角色可能时分复杂,并以多种方式影响CPI
指令系统体系结构 指令数 时钟频率 CPI 指令系统体系结构影响CPU性能的所有三个方面,因为它影响完成某功能所需的指令数、每条指令的周期数以及处理器的时钟频率

虽然时钟周期的长度通常是固定的,但是为了节省能量或者暂时提升性能,如今的计算机可以使用不同的时钟频率,因此我们需要对程序使用平均时钟频率。例如Intel Core I7处理器在过热之前会暂时将时钟频率提高10%。

功耗墙

当前在集成电路技术中占统治地位的是CMOS(互补性金属氧化半导体),其主要能耗来源是动态能耗,即晶体管开关过程中产生的能耗,即晶体管状态从0翻转到1或从1翻转到0所消耗的能量。动态能耗取决于每个晶体管的负载电容和工作电压:

[能耗propto负载电容 imes电压^2 ]

上式表示每个晶体管进行一次(0 ightarrow 1 ightarrow 0)(1 ightarrow 0 ightarrow 1)的逻辑转换过程中消耗的能量。

每个晶体管进行一次反转需要消耗的能量是:

[能耗propto frac{1}{2} imes负载电容 imes电压^2 ]

每个晶体管需要的功耗是能耗与开关频率的乘积:

[功耗propto frac{1}{2} imes负载电容 imes电压^2 imes开关频率 ]

其中的开关频率是时钟频率的函数,负载电容是连接到输出上的晶体管数量的函数(扇出)和工艺的函数。

一个问题:
为什么在过去30年里处理器的时钟频率增长非常快的同时,功耗却能保持相对较低的增长?

答:由上述功耗公式可知,功耗与电压的平方成正比。因此,尽管功耗和能耗可以通过降低电压来大幅减少,在每次公式更新换代时,处理器设计者都会这样做。

目前的问题是,如果电压继续下降会加大晶体管的电流的泄露,就像水龙头不能被完全关闭一样,目前有40%的功耗是由于电流泄漏造成的,如果晶体管的泄漏电流进一步增大,情况会变得难以处理。这就是计算机设计者所遇到的功耗墙

从单处理器向多处理器的转变

功耗的极限迫使处理器的设计产生了巨大的变化。处理器设计者开始在单个微处理器中加入多个处理器——核(core)。因此,“四核”微处理器指包含了4个处理器或者4个核的芯片。

多核处理器的出现,使得想要提高软件性能的程序员必须会编写并行程序。而并行程序的编写是困难的,为什么呢?
为了发挥并行硬件的优势,程序员必须将程序划分为多个部分,每个部分都有着相同数量的任务,并能同时进行。还要尽可能地减小调度开销,不浪费并行性能。

Amdahl定律

(Amdahl)定律由计算机科学家Gene Amdahl在1967年提出,它是给出了在系统的固定部分性能提升时,整体系统的性能最多能提高多少。即该定律描述的是数据规模固定时,渐进加速比的变化趋势。它常被用于并行计算领域。

若系统执行某应用程序需要的时间为(T_{old}),假设系统某部分所需的执行时间占总时间的比例为(a),若该部分性能提升的比例为(k),即该部分所需的时间为(aT_{old}),现在所需的时间为((aT_{old})/k)。因此总时间为:

[T_{new} = (1-a)T_{old} + aT_{old}/k = T_{old}[(1-a) + a/k] ]

由此,可以计算出加速比为:

[S=frac{T_{old}}{T_{new}} = frac{1}{(1-a)+a/k} ]

其中几个重要的变量:(a)为并行计算部分占整体的比例,(k)为并行处理的个数。

  • (a = 1)时,即整体都采用并行,最大加速比为(s=k)
  • (a = 0)时,即整体都采用串行,最大加速比为(s=1)
  • (k ightarrowinfty)时,(s ightarrowfrac{1}{1-a}),即加速比的上限;

假设并行代码占整个代码的(25\%),那么根据(Amdahl)定律可计算出它的极限加速比为:

[S = frac{1}{(1-25\%)+25\%/k} ]

(k ightarrow infty)时,(25\%/k ightarrow 0),即:

[S = frac{1}{3/4} = frac{4}{3} ]

那么改进后的代码运行时间:

[T_{new} = frac{T_{old}}{S} = frac{3}{4} imes T_{old} ]

可见在并行部分固定后,整体性能的提升是存在上限的,而非无止境地提升。

小结

  1. 硬件和软件设计者采用分层的方法构建计算机系统,下层总是向上层隐藏本层的细节。
  2. 性能计算公式:

[性能= frac{1}{执行时间} = frac{1}{指令数 imes CPI imes 时钟周期} = frac{时钟频率}{指令数 imes CPI} ]

执行时间是唯一有效且不可推翻的性能度量指标。在上述性能公式中包含三个因子,需要注意的是,任何一个独立的因子都不能决定性能,只有三个因子同时存在时,才能称为可靠的性能度量标准。

  1. 计算机组成中的两个新思想:程序中的并行性,当前的典型方法是借助多核处理器;开发存储器层次结构的访问局部性,当前的典型方法是使用高速缓存。
CS专业在读,热爱编程。
专业之外,喜欢阅读,尤爱哲学、金庸、马尔克斯。
原文地址:https://www.cnblogs.com/jmhwsrr/p/14259386.html