《Computer Architecture A Quantitative Approach》部分读书笔记
本书的第二版,相比前面的版本,经过了一些修整。第一版的基础内容,第一章(Fundamentals of Computer Design)和第二章(Performance and Cost)在这个版本中,是在开篇第一章中(Fundamentals of Computer Design)介绍的,编者认为两者放在一起比独立的分开讲解效果更好一些。
介绍
计算机出现的几十年间,从20世纪70年代开始,微处理器的性能增长率逐年增长,从最开始的每年25%左右,20世纪70年代末期的35%。
这样的增长速率以及大规模微处理器生产的成本优势,导致微处理器在计算机事业中的因素占比越来越大。同时,计算机市场的两个明显改变使得在一种新型的体系结构下实现了商业成功:
首先,虚拟技术消除了原本的编程语言的装配过程,减少了母必爱代码的兼容性要求;另外标准化的创作、独立的操作系统(例如Unix)以及更低的成本和风险带来了一种新型的体系结构。
这些改变使得继往开来,开发了一种新的体系结构集合,我们称它RISC体系结构。
从RISC出现,80年代微处理器的性能增长率达到了50%以上,而且还在不断的提升。
这种亮眼的增长率影响是双重的,首先明显的增强了能够提供给用户的发挥空间,用户可以享受在更低的时间内得到相同的结果;其次,这种恐怖的增长率导致了在计算机设计领域微处理为基础的主导地位:工作站和PC开始成为计算机行业的主要产品,小型机甚至超级计算机也逐渐采用利用多个微处理器代替原本的处理器。
计算机设计的任务
计算机设计者的任务客观来见是比较复杂的,一般有下面必须遵守:
确定属性对于新机器的重要程度
在成本约数条件下最大限度的提高性能、设计机器
虽然寥寥几个字,但是涉及到了很多方面:指令集的设计、功能组织、逻辑设计以及最后的实现部分。而实现部分又可能包括集成电路的设计、封装技术、电源甚至是温度管控等。
设计者们设计一台计算机需要同时满足功能需求以及价格和性能上的目标。通常,他们也是功能需求的决策者,这可能是主要的任务。功能需要也许是来自市场方面的特别的特性,也许是计算机应用反向的驱动设计者考虑如何设计计算机。
到之后的优化设计,需要的知识同样很多,编译器、操作系统、逻辑设计和包装等等,这些不仅需要计算机设计人员消耗大量的时间与精力,同时要求有很丰富的经验与知识广度。
优化设计的指标有很多,最常见的指标设计成本和性能,在某些领域中,可靠性和容错性往往时相比成本和性能有更重要的地位,这里我们专注于常规领域,着重考虑机器的成本和性能。
计算机与技术的使用趋势
评判一个指令集的成功,一个重要的指标就是指令集必须能够在硬件技术、软件技术和应用特性的改变下表现不俗,所以设计师必须要特别了解计算机以及计算机技术的使用趋势。
计算机使用趋势
计算机的设计受两个方面影响:使用方式以及底层的实现技术特性。
使用和实现技术的改变以不同的方式影响计算机设计,包括激励指令集的改变、流水方式还有类似缓存机制技术等等。
软件技术的趋势以及程序是如何使用计算机对指令集结构有着长期的影响。
程序所需的内存每年以1.5倍以上的速率增长,这种快速增长由程序的需求以及DRAM技术驱动。
高级语言逐渐替换汇编语言。这种趋势导致编译器承担更重要的作用,编译器的作者需要跟跟架构师们紧密的合作,构建一个更有竞争力的机器。
实现技术的趋势
集成电路逻辑技术:晶体管集成度每年都会提高约50%(晶体管的摩尔定律),在三年内翻了四倍,尺寸的增加可预测性不高,一般不会超过25%,然而布线技术并没有得到改善,这导致了时钟周期方面的速度提升缓慢。
半导体DRAM集成度每年增长不超过60%,周期改善的相当缓慢,十年里减少了约三分之一,而芯片的带宽随着延迟的减少以及DRAM接口的改善得到提升。
磁盘技术:1990年以前,磁盘的单位密度每年增长只有四分之一,90年以后,磁盘技术有了长足的发展,90年以后每年都有约一半的增加。
以上这些快速变化的技术影响着微处理器的设计,随着速度和技术的增强,有长达5年以上的使用寿命。即使在单个产品周期(两年设计两年生产)范围内,关键的技术也足以影响设计者的决定;实际上,设计者经常会为下一个可能出现的技术设计。
成本和成本趋势
数目,商品化的影响
数量是决定成本的关键因素,增加数量会在几个方面上影响成本。首先,他们减少了降低学习曲线(学习曲线学习曲线表示了经验与效率之间的关系,指的是越是经常地执行一项任务,每次所需的时间就越少。)所需的时间,减少时间的比率主要由使用的数目决定;其次,大批量的使用会降低成本,因为他增加了采购和制造的效率。根据经验,数目增加一倍,成本会降低一成。
商品是由多个供应商大量销售并且基本相同的产品。不同的供应商对产品的高度竞争,降低了成本和售价之间的差距,同时也降低了计算机的成本。
一个系统的成本分析
上图是1990s末期彩色台式机的大致成本细目,虽然DRAM等设备的成本会随着时间的推移降低,但是部分设备的成本将会几乎没有变化。此外可以预期,未来的假期将会有更大的存储设备(内存或硬盘),这意味着价格下降比技术改进更慢。
处理器系统仅占成本的6%,虽然在中高端设计中,比例会增加,但是在主要的系统中是类似的。
性能的测量
当我们说一台电脑比另一台电脑快时,是什么意思? 用户可以说,当程序以较少的时间运行时计算机更快;而计算机中心管理员可以说,在一小时内完成更多作业时计算机更快。计算机用户对减少事件的执行时间感兴趣。 大型数据处理中心的经理可能对增加吞吐量感兴趣。
用户每天运行相同程序将是评估计算机性能的理想选择。为了评估新系统,用户需比较工作负载执行时间(用户在机器上运行的程序和操作系统命令的混合)。 然而,这种方法是机器枯燥的,大多数人依靠其他方法来评估,希望这些方法能够预测机器的使用性能。在这种情况下使用四个级别的程序,下面按照预测精度的降序列出。
真实程序。虽然不知道这些程序花费了多少时间,但知道用户可能会运行他们解决实际问题。例如C的编译器,文本处理软件或者CAD工具。
内核。尝试从真实的程序中提取几个小的、关键的片段评估他们的性能。Livemore Loops和Linpack是最出名的例子。与真实的程序不同,没有用户会运行内核程序,因为他们只用于评估性能。这种方法好在隔离机器各个特性的性能,解释实际程序性能差距。
Toy benchmarks。这种方法通常在数十行代码之间,产生用户在运行当前程序前已经知道的结果。像Sieve of Erastosthenes,Puzzle和Quicksort这样的程序很受欢迎,因为它们体积小,易于输入,几乎可以在任何计算机上运行。一般在程序语言开始分配阶段运行。
Synthetic benchmarks。在原理上与内核很相似,这种方法试图匹配一个大的程序集里面的操作以及操作数平均值。Whetstone和Dhrystone是流行的产品。
计算机设计的定量原理
加快经常事例
也许计算机设计的最重要和最普遍的原则是加快经常使用的cas额,这个原则也适用于确定如何使用资源,因为如果case发生频繁,对一些发生更快的影响想爱你然更大。 改进频繁事件,而不是罕见事件,也有助于性能提升。 此外,频繁的情况通常更简单,并且可以比不常见的情况更快地完成。
Amdahl定律
Amdahl定律:系统中对某一部件采用更快执行方式所能获得的系统性能改进程度,取决于这种执行方式被使用的频率,或所占总执行时间的比例。阿姆达尔定律实际上定义了采取增强(加速)某部分功能处理的措施后可获得的性能改进或执行时间的加速比。简单来说是通过更快的处理器来获得加速是由慢的系统组件所限制。
CPU性能方程
大多数计算机使用恒定速率运行的时钟构建。 这些离散时间事件称为时钟周期。计算机设计者通过其长度(例如,2ns)或其速率(例如,500MHz)来指代时钟周期的时间。一个程序的CPU时间可以表示为:
cpu time=时钟周期数 * 时钟周期时间
内存分层概念
首先,让我们看看硬件设计的一个经验:较小的硬件运行更快。这种简单的经验特别适用于由相同技术构建的存储器,原因有两个。首先,在高速机器中,信号传播是延迟的主要原因; 较大的存储器有更多的信号延迟,并且需要更多的电平来解码地址。第二,大多数技术中,我们可以获得比 较大的存储器 更快的 更小的存储器。这主要是因为设计者可以在更小的设计中使用每个存储器单元更多的功率。
速表明有利于访问这样的数据将提高性能。 因此,我们应该尽量保持最近访问的项目在最快的内存中。 因为较小的存储器将更快,所以我们希望使用较小的存储器来尝试保持最近访问的项目靠近CPU,并且随着我们从CPU远离CPU而逐渐增大(和较慢)的存储器。 此外,对于更靠近CPU的那些存储器,我们还可以采用更昂贵和更高功率的存储器技术,因为它们小得多,并且由于存储器的小尺寸而降低了成本和功率影响。 这种类型的组织称为存储器层次结构。其中有两个重要的层次是缓存和虚拟内存。
(上图是上个世纪的容量与速度,不过到现在依然有参考意义)
缓存是位于靠近CPU的小型快速存储器,其保存最近访问的代码或数据。 当CPU在高速缓存中找到所请求的数据项时,其被称为高速缓存命中。 当CPU在高速缓存中找不到它需要的数据项时,发生高速缓存未命中。 从主存储器检索包含所请求字的固定大小的称为块的数据块,并将其放入高速缓存中。 时间局部性告诉我们,我们可能在不久的将来需要这个词,因此将其放在可以被快速访问的缓存中是有用的。 由于空间局部性,块中的其他数据将很快需要的概率很高。
高速缓存未命中所需的时间取决于存储器的延迟及其带宽,其确定检索整个块的时间。 由硬件处理的高速缓存未命中通常导致CPU暂停或停止,直到数据可用。
同样,并非程序引用的所有对象都需要驻留在主存储器中。 如果计算机具有虚拟内存,则某些对象可能驻留在磁盘上。地址空间通常被分成固定大小的块,称为页面。
在任何时候,每个页面驻留在主内存或磁盘上。 当CPU引用在高速缓存或主存储器中不存在的页面内的项目时,发生apage fault,并且整个页面从磁盘移动到主存储器。由于页面故障需要长时间,因此它们由软件处理,CPU不会停止。发生磁盘访问时,CPU通常切换到其他任务。 高速缓存和主存储器与主存储器和磁盘具有相同的关系。
第二章部分介绍
在本章中,我们专注于指令集架构。程序员或编译器写程序可见的机器部分。
本章介绍了指令集架构师可用的各种各样的设计替代方案。特别的,本章重点讨论四个主题。
提出一个指令集替代品的分类,并给出一些定性评估各种方法的优缺点。
提出和分析一些指令集测量,这些测量在很大程度上独立于特定的指令集。
解决语言和编译器的问题,以及它们对指令集架构的影响。
显示了以上想法如何反映在DLX指令集中。
在本章中,研究了各种各样的结构测量方法。这些测量取决于测量的程序和用于进行测量的编译器。这些结果不是绝对的,因为如果使用不同的编译器或不同的程序集进行测量,可能会看到不同的数据。
作者认为,这些章节中所示的测量值合理地表示了一类典型应用。许多测量使用一小组基准来呈现,使得可以合理地显示数据,并且可以看到之间的差异。
一个新机器的架构师想要分析一个大的程序集合,来做出他的决策,所显示的所有测量都是动态的。也就是说,测量事件的频率是通过在执行测量程序期间事件发生的次数来加权计算得到。
指令集结构的分类
CPU内部存储器类型是指令结构分类考虑的关键指标,因此在本节中,将重点介绍这部分的替代方案。主要的选择有堆栈,累加器或一组寄存器。
一种可以将存储器作为指令的一部分访问,称为寄存器-存储器架构,一种仅能够使用load/stroe指令访问主存的称之为寄存器-寄存器架构。第三类,目前流通的机器中还未出现,是将所有操作数保存在内存中,称为内存-内存架构。
虽然大多数早期机器使用的是堆栈或累加器式架构,但在1980年后设计的机器几乎无一例外都采用load-store寄存器架构。
出现通用目的寄存器机器的主要原因有两个。首先,CPU内部的寄存器的比存储器更快。第二,寄存器对于编译器来讲更容易使用,并且可以比其他形式的内部存储更有效地使用。
寄存器可以用来保存变量。当变量被分配给寄存器时,存储器的使用就会减少,程序运行速度提高(由于寄存器比存储器快),并且代码密度提高(因为寄存器可以用比存储器位置更少的位命名)。
两种主要的指令集特性划分了通用目的寄存器架构。这两个特性涉及典型算术与逻辑指令(ALU指令)的操作数的性质。第一个涉及ALU指令是否具有两个或三个操作数。在三操作数格式中,指令包含目的地址和两个源操作数。在双操作数格式中,操作数中的一个既是操作的源也是操作之后的结果。通用目的寄存器架构中的第二个区别涉及多少个操作数可以是ALU指令中的存储器地址。典型ALU指令支持的存储器操作数的数目从0到3。
内存地址
有两种不同的模式用于排序字内的字节。大端模式与小端模式:
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
在许多机器中,对大于一个字节的对象访问必须对齐。什么是对齐?address of data % sizeof(data type)==0
就是对齐,对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。。不对齐就会造成数据访问花费额外的时钟周期,和额外的指令(编译器或OS附加的),并且数据经过更长的路径,比如pipeline才能到达CPU(从RAM)。这就是对齐问题。这里的重点是数据的起始地址与数据的大小。
寻址模式
- 寄存器寻址模式
- 立即寻址
- 间接寻址
- 索引寻址
- 直接寻址或绝对寻址
- 内存间接寻址
- 变址寻址
小结
本书第二版发型到现在虽然已经过了不短的时间,但是依然有很多值得我们细细品赏的地方。第一部分,作者没有像其他类似主题的书籍一样,上来就讲体系结构,而是拉着我们,从设计的趋势、设计的目的、设计架构需要考虑的诸多方面娓娓道来,读的过程中很有代入感,好像我们真的就是designer,特别是成本管控方面,原来基本上就没有考虑这一点,这里作者甚至从计算机制造业的视角分析了很多。这看起来跟我们没什么关系,但是实际上是为我们开辟了一个很棒的思路,以前很多模模糊糊的东西现在变得容易理解了。之后第二章开始讲了一些设计上的技术层面,第二章开始也说了,主要是关于指令集的知识,包括指令集结构划分的依据以及原因,字节存储模式、字节对齐的优势以及寻址模式等等,后面的内容由于时间关系没有看到,之后会继续品读,更新在这里。