目录

前言

程序局部性

时间局部性

空间局部性

循环展开


前言

程序局部性分为两个概念:时间局部性与空间局部性,它们俩的特性与编译器优化有关,编译器会将经常访问的内存地址或访问的内存地址相邻不大的区域放入到CPU的Cache缓存中,这样每次访问CPU直接从Cache中取数据,这样能大幅度提升程序效率。

循环展开就是非常简单的,就是增加代码长度以提升循环速度。

程序局部性

时间局部性

时间局部性的意思是当一块内存区域,每隔一段时间就会被访问,局部性的意思是程序中的某一小段,这句话可以从计算机角度理解是一个变量每隔几十ns里就会被访问,也就是编译器会推断,推断你代码中某个变量是否会被频繁访问,也可以理解为在一个函数里频繁对一个变量进行访问,如:

void test(){int sum = 0;for(int i = 0;i < 100;++i){sum = i;}
}

test就可以理解为程序的一部分,同时从代码里你可以看到有两个内存会被频繁访问,分别是sum与i,至于是两个都放入CPU的Cache里还是只放一个取决于编译器的优化程度以及编译器的考量算法,至于放一级缓存还是二级缓存这里需要理解一个更底层的概念,RAM,一级和二级都是使用RAM存储器制作的,并且它是紧贴CPU内部的,总线地址最短,是属于内部总线,CPU无需通过任何外接控制芯片就能访问内部的内存,速度会大幅度提升。

它们都有独立的地址,编译器会将符合时间局部性的变量值放入这里,然后对它们的访问不在是内存的地址,而是缓存中的地址,将缓存中的值读出来,然后做运算,然后在写入缓存,最后编译器还会判断,你是否会对这个变量做第二次操作,比如此次循环结束以后在别的地方是否还对这个变量有引用?如果有的情况下则会将缓存中的数据写回到内存,如果不会则会丢掉。

现代的编译器将80%的工作都放在了优化上。

空间局部性

空间局部性是指每次访问的内存地址与上一次的内存地址是相邻的或是内存对齐的。

如:

void test(){int sum[10][10] = {0};for(int i = 0;i < 10;++i){for(int j = 0;j<10;++j)}sum[i][j] = i;}}
}

上述代码符合空间局部性,因为在访问sum时是以顺序访问的,通过行来访问列,这样编译器能更容易推到出下一行的地址,那么编译器会将sum[i],i,j的地址放入缓存,那么CPU每次访问时,只需要sum+(i*j),就可以求出sum[i]的下一个行地址,然后在加上j的偏移,比如sum是int,那么加上j就是加上几个int大小,int为4,当j为2时就是加上2*4,找到列里的其中一个内存值,那么编译器通过以上猜测,猜测出了未来要访问的几个地址,如果缓存中能存储这个数组的全部值,那么就全部放入缓存,若不能,则放入一部分,那么这一部分的地址操作会访问cache里的值,剩余部分则是内存操作,现代化编译器可能会将其中一部分先放入cache,当CPU访问完了之后在写回内存,在将剩余部分在放入cache,然后CPU在继续对其进行操作,写完以后在返回内存。

其实在优化期间CPU也会去你看,你其它地方有没有引用这个数组了,如果没有引用,那么上述代码基本上就会被忽略掉了,甚至不会被执行。

在缓存里最终是否写回内存是取决于你的代码中是否引用了这个变量,所以说目前编译器是非常强大的。

通俗易懂的来说:编译器会推断出你是否会经常性的访问这个数组,并且这个数组是否是按照顺序访问的,如果是那么编译器可以根据数组访问规则以及数组类型来推断未来要访问的数组地址是多少,从而将这里面的值取出放入cache,然后对它们的访问都从cache里访问,最后通过是否引用来决定是否写入内存,如果数组过大,cache放不下,则将能放下的部分写入cache,或者先访问完cache里的数组元素后,根据其它地方是否引用来决定是否写入内存,然后在将剩下的值写入cache在继续访问。

在单片机方面我们一般会加volatile来表明编译器不要将这个变量放入cache,同时要求不能忽略,要按照实际方式进行访问与读写。

说完了符合内存局部性的,接下来给大家看一下不符合内存局部性的:

void test(){int sum[10][10] = {0};for(int i = 0;i < 10;++i){for(int j = 0;j<10;++j)}sum[j][i] = i;}}
}

这段代码仅仅是将sum[i][j]改成了sum[j][i],这里讲一下它的区别:

sum[i][j]:每次访问时偏移4个字节

sum[j][i]:每次访问时偏移(i*j)个字节

要符合空间局部性的唯一标准就是每次访问的地址与未来要访问的地址要是相邻的,即int数组每隔元素相隔为一个int字节大小,那么每次访问时的地址偏移要符合int字节大小,这样编译器能根据数组类型预测出未来要访问的地址是多少,如果不按照这样的偏移的话编译器会认为你有别的想法,同时编译器认为地址相差过大,不符合内存对齐规则所以编译器不会去优化,同时编译器也不会取优化任何可能产生风险的代码。

因为在每次循环访问时sum[i][j],其实每次只递增4字节,而sum[j][i]每次递增了(i*j)个字节,编译器在做推断时推断sum[i][j]只需要通过每次递增4个字节就可以知道未来要访问的地址,而sum[j][i]则要去计算这个数组的列是多大,然后做(i*j)大小偏移,虽然这样能推断出但是还是存在一定风险,如内存不对齐,其次是偏移过大不可把控,你无法知道下一次偏移是否正确,但如果偏移过小的情况下风险会越低,因为一次跨越过大的地址空间编译会认为这不是一个可靠的优化方案。

循环展开

循环展开会更容易理解,如:

void test(){int sum[10][10] = {0};for(int i = 0;i < 10;++i){for(int j = 0;j<10;++j)}sum[i][j] = i;}}
}

展开后:

void test(){int sum[10][10] = {0};for(int i = 0;i < 10;++i){for(int j = 0;j<10;j=j+5)}sum[i][j+1] = i+1;sum[i][j+2] = i+2;sum[i][j+3] = i+3;sum[i][j+4] = i+4;sum[i][j+5] = i+5;}}
}

就是去牺牲代码长度来换取时间上的效率。

这样做的好处是在一次循环时做更多的事情,因为循环结束时会进行逻辑判断然后在进行代码跳转这样一个周期性的工作,如果有局部变量还要做堆栈维护工作。

程序局部性(时间局部性与空间局部性)与循环展开原理详解相关推荐

  1. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

  2. LVS原理详解(3种工作方式8种调度算法)--老男孩

    一.LVS原理详解(4种工作方式8种调度算法) 集群简介 集群就是一组独立的计算机,协同工作,对外提供服务.对客户端来说像是一台服务器提供服务. LVS在企业架构中的位置: 以上的架构只是众多企业里面 ...

  3. jQuery中getJSON跨域原理详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp28 jQuery中getJSON跨域原理详解 前几天我再开发一个叫 河蟹工 ...

  4. nginx配置文件及工作原理详解

    nginx配置文件及工作原理详解 1 nginx配置文件的结构 2 nginx工作原理 1 nginx配置文件的结构 1)以下是nginx配置文件默认的主要内容: #user nobody; #配置用 ...

  5. EMD算法之Hilbert-Huang Transform原理详解和案例分析

    目录 Hilbert-Huang Transform 希尔伯特-黄变换 Section I 人物简介 Section II Hilbert-Huang的应用领域 Section III Hilbert ...

  6. 图像质量损失函数SSIM Loss的原理详解和代码具体实现

    本文转自微信公众号SIGAI 文章PDF见: http://www.tensorinfinity.com/paper_164.html http://www.360doc.com/content/19 ...

  7. 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解

    前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  8. 【Android架构师java原理详解】二;反射原理及动态代理模式

    前言: 本篇为Android架构师java原理专题二:反射原理及动态代理模式 大公司面试都要求我们有扎实的Java语言基础.而很多Android开发朋友这一块并不是很熟练,甚至半路初级底子很薄,这给我 ...

  9. SVM分类器原理详解

    SVM分类器原理详解 标签: svm文本分类java 2015-08-21 11:51 2399人阅读 评论(0) 收藏 举报  分类: 数据挖掘 文本处理(16)  机器学习 分类算法(10)  目 ...

最新文章

  1. PLSQL程序编写杂烦数据表信息编写批量排版
  2. python的celery的面试_python 面试
  3. [Windows Server] 在 Windows Server 2012 上安裝 .NET Framework 3.5 - 摘自网络
  4. 特征层次分析、视觉特征语义探索(微调+预训练)
  5. why SAP Fiori action sheet is created as disabled by default
  6. yaf 重写index.php,php框架Yaf路由重写实例代码
  7. 1、vue 笔记之 组件
  8. Linux学习之如何在物理机上安装Linux发行版
  9. Android学习小Demo(9)一个To Do List的实现
  10. Zdal分库分表、超详细一步一步实现使用zdal搭建框架
  11. Py之pandas:dataframe学习【转载】
  12. 大疆DJI Thermal SDK Linux编译
  13. tensorflow的类、变量和函数讲解
  14. StringUtil 字符串处理工具
  15. Java Web开发Session超时设置
  16. 微信小游戏3d入门视频教程
  17. java map是有序的吗_java中map、set、list是否有序
  18. smartPrinter 安装时1722错误
  19. 在html中制作多彩照片墙,60个照片墙布置方案 记录浪漫时刻
  20. PIL (Python Imaging Library) 教程

热门文章

  1. 手把手教你云服务器如何搭建typecho博客网站(包括配置免费SSL证书)
  2. php5d,php挖洞基础知识篇以及防范方法
  3. cli php 增强包_Linux 上安装 PHP 扩展
  4. 模块化加载_webpack模块化原理-异步加载模块
  5. fastdfs上传文件_Java 实现 FastDFS 实现文件的上传、下载、删除
  6. 网易云api如何调用_谈谈网易云如何快速升级
  7. 无名接口.php,李无名
  8. mdb新版本打不开_救命!!! 我打不开她给我的Pr工程……
  9. serviceFabric oracle,Oracle ASM和多路径软件的兼容性【转】
  10. 机器学习理论与实战:逻辑回归