博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
程序局部性原理的一些思考
阅读量:5992 次
发布时间:2019-06-20

本文共 2223 字,大约阅读时间需要 7 分钟。

今天OS课上老师提到影响缺页次数的因素中有一个是 程序的局部性越好,越不容易缺页,并举了个关于双重for循环顺序的选择问题作为例子。

我回去也查询资料研究了一下这个问题。

何为程序的局部性(locality)

程序的局部性原理是指程序在执行时呈现出局部性规律,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域。也就是说,程序倾向于引用邻近于其他最近引用过得数据项,或者最近引用过的数据项本身。我的理解就是:通过利用“缓存”来提高程序运行效率

程序的局部性又通常有两种不同的形式:时间局部性(temporal locality)空间局部性(spatial locality).

时间局部性:被引用过一次的存储器位置在未来会被多次引用

空间局部性:如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。也就是说靠近当前正在被访问内存的内存内容很快也会被访问。

理论分析

先以一维数组为例,考虑对程序数据引用的局部性。

借用《深入理解计算机系统》书中的例子进行分析

6807cf1bjw1ersp6fm9qij20nt072gm8.jpg

sumvec函数中数组v的元素是被顺序读取的,一个接一个,按照它们存储在存储器中的顺序。(假设地址从0开始).因此对于变量V,函数有很好的空间的局部性。因此这个函数有良好的局部性。

向上面例子中按顺序、连续的对v的引用,称为步长为1的引用模式(相对于元素大小)。同理,在一个连续的向量中,每隔k个元素对向量进行访问,称为步长为k的引用。一般来说,随着步长的增加,空间局部性会下降。

再考虑二维数组

6807cf1bjw1erspfo31xsj20mw06waal.jpg

图中函数是对一个二维数组求和(M=2,N=3)。双重嵌套循环按照行优先的顺序读取数组的元素。因此函数具有良好的空间局部性,因为它按照数组被存储的行优先顺序来访问这个数组,因此得到的是一个步长为1的引用模式和良好的空间局部性。从而使得程序运行效率得到提高。

但我们更换读取顺序的时候,交换i和j的循环。如下图所示

6807cf1bjw1erspkwdl9oj20o0073aal.jpg
这时候发生了巨大的变化!函数的空间局部性变得很差,因为他按照列顺序来扫描数组,而不是按照行顺序。因为C数组在存储器中是按照行顺序的,结果这里就得到的是步长为N的引用模式。从而使得程序效率降低。

代码实例测试

实例

#include 
#include
#include
int main(){ int a[500][500]; int i,j; clock_t start, finish; double duration; start = clock(); for (int k = 0; k < 1000; k++)//循环放大时间 { for(i=0; i<500; i++) { for(j=0; j<500; j++) { a[i][j]=i; } } } finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; printf( "%f seconds\n", duration ); start = clock(); for (int k = 0; k < 1000; k++)//循环放大时间 { for(j=0; j<500; j++) { for(i=0; i<500; i++) { a[i][j]=i; } } } finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; printf( "%f seconds\n", duration) ; return 0;}

运行结果1

6807cf1bjw1erspvm7ymvj20it0cbmy7.jpg

可以发现当行列数相同的时候,按照行顺序扫面的效率要高一些。也符合之前的理论分析.

运行结果2

当我把数组定义改为a[10][10000]的时候,测试结果如下,依旧为行顺序扫描效率较高。

6807cf1bjw1ersq25rrs2j20it0ajmy5.jpg

运行结果3

数组改为a[10000][10]后,测试结果如下,依旧为行顺序扫描效率较高。

6807cf1bjw1ersq1i1dotj20it0cbgmn.jpg

小结

通过对双重循环不同循环顺序的效率分析,初步理解了局部性原理。也就是说现代的计算机体系的存储技术至少都用了局部存储思想,即CPU提取内存的一个位置的数据放到cache中的同时,也会把其附近的数据也提取到cache中,如果内存以行优先存储方式(注意这个前提!),则提取Array[0][0]位置的数据的同时,则也会顺便把"Array[0][1], Array[0][2],tArray[0][3], Array[0][4]..."等数据提取出来存放在缓存中。这样在后边连续的几次循环中均可以命中缓存,从而减少缓存失效,提高程序的运行效率。

参考资料

《深入理解计算机系统》

转载于:https://www.cnblogs.com/glczero/p/4478274.html

你可能感兴趣的文章
centos关闭ctrl+alt+del重启
查看>>
国外PHP学习网站书籍资料汇总
查看>>
我的友情链接
查看>>
IOS UIApplication详解
查看>>
即将改变软件开发的5个Java 9新特性
查看>>
我的友情链接
查看>>
浏览器兼容汇总
查看>>
mumu轻游戏平台
查看>>
火车运煤问题
查看>>
第二讲,我们来谈谈:“什么是二进制”
查看>>
HTML中的input type="reset"标签失效(不起作用)的可能原因
查看>>
Java程序员从笨鸟到菜鸟之(八十一)细谈Spring(十)深入源码分析Spring之HibernateTemplate 和HibernateDaoSupport...
查看>>
java程序员菜鸟进阶(五)oracle基础详解(五)oracle数据库体系架构详解
查看>>
centos5.6 (64bit)编译安装vsftpd-2.3.4的配置(两种用户登录)[连载之电子商务系统架构]...
查看>>
php经典加密解密函数
查看>>
smb://#### 报错 this location could not be displayed
查看>>
在GNU/Linux操作系统中使用命令行xdg-open打开一个任意格式的文件
查看>>
tmp
查看>>
CentOS6.5下Redis安装与配置
查看>>
如何部署运行Seam 中的Example
查看>>