CSAPP--存储器及程序的局部性

  作为一名程序员,你需要理解计算机存储系统的层次结构,他对应用程序的性能有着巨大的影响,如果程序所需要的数据存储在cpu的寄存器中,那么指令在执行期间,就可以花费零个周期来进行访问,而在Cache中则需要1~30个周期,主存中需要50~200周期,而在磁盘中则需要几千万个周期。

  我们需要能够了解系统如何将数据在存储器系统层次结构中上下移动的,这样编写应用程序时我们可以将数据项存储在层次结构较高的地方,那样cpu可以快速的访问到。

存储技术

  利用存储器的层次来介绍一下存储器概念。

  最高为CPU中的寄存器,由触发器构成,几乎不需要访问周期。其次是若干高速缓冲存储器Cache,有SRAM构成,用来解决主存访问速度慢的问题,可以是多级缓存。其次是DRAM构成主存,其中主存中也是包括ROM的,用来存储固件(比如BIOS,一个计算机系统通电以后首先 运行ROM中的固件)。再往下是磁盘,一般为传统的旋转磁盘,盘面上的磁性材料记录着数据信息。SSD是一种基于闪(EEPROM)的存储技术,在某些情况下是传统磁盘的极强的替代品。

主存访问

  数据流通过总线的共享电子电路在处理器和DRAM主存/输入输出设备之间来来回回。如图:

系统总线连接着I/O桥(将系统总线中的电子型号翻译成I/O总线的电子信号或者存储器总线的电子信号)。Intel系统中使用北桥和南桥的芯片组分别将cpu连接到存储器和输入输出设备。

局部性

  这种局部性包括时间局部性和空间局部性,良好的局部性也就是说CPU倾向于引用最近的或者临近的的数据项。这种倾向性成为局部性原理。

  现代的计算机中各个层次,从硬件到操作系统,再到应用程序,都利用到了局部性原理。

  • 硬件 高速缓冲存储器
  • 操作系统 用主存来缓存磁盘文件系统中最近被使用的磁盘块
  • web 浏览器也会将最近引用的文档放在本地磁盘上,这属于时间局部性

这里举一个例子,对于变量V,这里有很好的空间局部性,因为我们采用的是步长为1的引用模式,在主存中取块B放入到Cache中,步长为k的引用模式中平均每次循环迭代会有min(1,(wordsize*k)/B)次缓存不命中,k的增加,空间的局部性会降低。

int sumvec(int v[N])
{
int i, sum = 0;

for (i = 0; i < N; i++)
sum += v[i];
return sum;
}

 量化评价一个程序的局部性的简单原则:

  1.重复引用一个变量的程序有良好的时间局部性。

  2.对步长为k的引用模式,k越小,空间局部性越好。

  3.对取指令来说,循环有好的时间和空间局部性,循环体越小,迭代次数越多,局部性越好。

高速缓存存储器

  缓存的Cache和主存的三种映射方式:组相联映射,全相连映射,直接映射。

  实际上计算机组成原理课上已经了解来Cache的工作原理,利用局部性的思想编写高速缓存友好的代码的基本方法:

  • 让最常见的情况运行的更快,所以要把注意力集中在核心函数的循环上。
  • 让循环内部缓存不命中数量最小。

 在上面的代码中,我们假定v是块对齐的,字为四个字节,高速缓存块为四个字。

  1.对局部变量的反复引用是好的,因为编译器能够将他们还存在寄存器文件中(时间局部性)。

  2.步长为1的引用模式是好的。

 而在这个多位数组中我们这样访问:

交换i,j的次序使得每次都不命中。

缓存不命中种类

  • 冷不命中/强制不命中:此时k层是一个空的缓存,所有访问都会不命中
  • 冲突不命中:对象映射到了同一个缓存块,导致缓存无法命中
  • 容量不命中: 数据量太大,无法保存在缓存中导致。

 个人总结

 编写程序可以考虑到高速缓存友好和局部性原理,当然最后的这些优化的前提是你使用的高效的算法。

原文地址:https://www.cnblogs.com/a1225234/p/5741219.html