3.2《深入理解计算机系统》笔记(二)内存和高速缓存的原理【插图】
《深入计算机系统》笔记(一)主要是讲解程序的构成、执行和控制。接下来就是运行了。我跳过了“处理器体系结构”和“优化程序性能”,这两章的笔记继续往后延迟!
《深入计算机系统》的一个很大的用处是:给了我们很多定义式的解释,或者称之为科学的解释,这将成为我的理论依据;不再是网上一些自称老手的闲聊了。不愧是计算机最牛逼的大学卡内基-梅隆大学的教材。
该blog跳过第四章CPU的结构,第五章优化程序性能,也没有详细讨论高速缓存的机制。
六、存储器层次结构
有必要将存储器层次结构再次列出来:
高速缓存的“行”、“块”和“组”是什么意思?参考本bolg最下面“高速缓存的物理逻辑图”
字:一个word的意思,IA32中指的是16位,
块:是一个固定大小的信息包,在高速缓存和主存之间来回传送。块包含32~64个字节。所以内存中只是信息,成为块
行:高速缓存中存储块已经其他信息的容器。所以,行总是一个块的大小,通常“行”和“块”可以互换使用。结合上图高速缓存既有块和其他信息,称之为“行”
组:是一个或者多个行
---》之前知识停留在RAM和ROM,现在再往前推进一步RAM也分为静态SRAM和动态的DRAM。SRAM主要用于高速缓存DRAM用于内存
SRAM将每个位存储在一个双稳态的存储器单元里。每个单元是用一个六个晶体管电路来实现。它可以无限期地保持在两个不同的电压配置或状态之一。就是说只要有电,他就会永远地保持它的值。即使有干扰,例如电子噪音、扰乱电压,当干扰消除时,电路就会恢复到稳定值。下图说明了“双稳态的”效果,采用倒摆的时钟。
难道这个原理可以解释:电磁干扰造成电视画面错乱,手机信号干扰么?
DRAM将没位存储为一个电容,这个电容非常小,通常只有30毫微微法拉。与SRAM不同的是DRAM的存储单元对干扰非常敏感。当电容电压没扰乱之后,他就永远无法恢复了。所以DRAM需要不断刷新电容。暴露在光线下会导致电容电压改变。实际上,数码照相机和摄像机中的传感器本质上就是DRAM单元的阵列。【惊讶】
无论如何SRAM和DRAM都是易失的(volatile)
下面讨论DRAM的读取机制:
这种二维阵列的缺点是:必须分两步发送地址,增加存储时间。
---》DDR2和DDR3的内存的区别。Double data-rate synchronous DRAM带宽分别是4位和8位。DDR3也分为1333MHz和1600MHz
---》磁盘,略过。
---》DRAM和磁盘的性能滞后于CPU的新能,虽然他们的性能都在增长。
这也就是为什么CPU和内存之间的高速缓存一直增多。
---》人们发现无法像以前那样增大CPU的时钟频率了,计算机制造商撞上了“能量墙”因为如果那样芯片的功耗会太大。所以多核就出现了,多核出现后CPU的时钟有所减少,并区域平缓。但是有效CPU周期时间还是像之前的速率增长。
---》高速缓存运行机制。p(406)
---》缓冲区命中。
为什么第二次启动程序要比第一次快的多,就是因为缓冲区命中的原理。第一次启动后很多数据还停留在多级缓冲区中,这个时候如果再次启动就会减少数据移动次数,也就减少程序的启动时间。从上图中我们发现,有部分数据存在与高速缓存中,就不用从内存中取了。
举例子:灌溉小麦,使用100米的水渠(普通水渠,不是水泥那种),假如水泵出水是固定的。
第一次使用时,水渠中的水前进很慢,走完100米的水渠大约需要10分钟,这是因为:有一些水会损失掉,即被渗到水渠下面的泥土中了。这部分水可以看作是“高级缓存”中的水。
第二次(时间较短)再灌溉小麦时,水渠的水走的很快,花费了2分钟,因为相对于第一次来说,水渠不需要王下面渗很多水了。如果时间长了,隔了2天,水渠中的渗的水再往下渗。
第三次灌溉(时间较长),同样需要一些水渗到水渠下泥土里面,所以,还是需要10分钟。
用计算机中的实例:当一条加载指令指示CPU从内存地址A读出一个字时,他将地址A发送给高速缓存。如果高速缓存证保存着A处的地址的按个拷贝,它就立即将那个字发给CPU。这要比从内存中读取快很多。
---》core i7的高速缓存层次结构
注意:只保存指令的高速缓存i-cache,只保存数据的高速缓存叫d-cache。既保存指令又保存数据的成为统一的高速缓存。
就像第一章说的那样高速缓存至关重要,特别是“缓冲区命中”这个技术如果优化好了,速度将会很大改进。
高速缓存的物理逻辑图:
---》直接映射高速缓存p(410)
根据E(高速缓存行)高速缓存被分为不同的类。每个组只有一行(E=1)的高速缓存呗称为直接映射高速缓存 虽然一个整形可能在寄存器中,而整形数组,则可能存在与高速缓存中。
---》冲突不命中。请看下面的例子:
- float dotprod(float x[8], float y[8])
- {
- float sum = 0.0;
- int i;
- for(i = 0;i < 8;i++)
- sum += x[i] * y[i];
- return sum;
- }
当第一次迭代x[0]时,必定不命中,那么会导致x[0]~x[3]的块被加载到高速缓冲组0,下一次是y[0]的调用,又一次不命中,导致y[0]~y[3]的块被拷贝到组0,从而覆盖前一次x的值。如此迭代,下次x[1]的值继续不命中,继而将x[0]~x[3],覆盖调y[0]~y[3]的值。这种就叫做冲突不命中,也叫”抖动“。本质原因是:x和y数组被映射到同一个组。程序员可以避开这种抖动,但是我觉得编译器应该解决这个问题。
原书中可能错误的地方不算多,但也影响到阅读的心情。
p406图6-24少了一个10的单元格,12单元格重复
p403图6-21 a)函数名字应该为:int sumarraycol(int a[m][n]).
p199第一行最后应该为“偏移量”