当前位置: 首页 > news >正文

在开发中你可能没有考虑到的两个性能优化

1:多余的存储引用导致性能降低;

2:利用局部性提高程序性能;

先来说说引用是怎么降低程序性能,个人认为降低程序性能主要有两个原因,一是数据结构选择不合理,二是多层嵌套循环导致部分代码被多余重复执行。在第二种情况下我们一般都是优化循环最里层的代码,能提出来的尽量往外层提,实在不行的就优化它的运行速度。

1:多余的存储引用导致性能降低。先来看一个关于引用导致性能降低的问题。下面两个方法哪个更快。

        static void Test2(ref int sum)
        {
            for (int i = 1; i <= timer; i++)
            {
                sum += i;
            }
        }

        static void Test3(ref int sum)
        {
            int tmpSum = sum;
            for (int i = 1; i <= timer; i++)
            {
                tmpSum += i;
            }
            sum = tmpSum;
        }

大致一看他们的性能应该没有差别,因为这两个方法其实就是利用一个循环求和,而真正能影响方法的性能就是这个循环,且两个方法的循环表面上看可以说是一样的,当,我令timer=10000000时,即求1+2+...+10000000的和,方法Test3的速度比Test2快。是的,Test3比Test2快,在某个区间内timer越大,性能差别越大。运行结构如下:

咱们直接来看反汇编代码,部分反汇编代码如下,我们只用看红线框着的部分。

 

最主要的一句代码:sum+=i;方法Test2比方法Test3多了最后面一行,即将每次循环后求得的和回写到内存中,方法Test3却不用这么麻烦,只用一个寄存器,每次求得的和写到寄存器中,求完和后一次将和写到内存中。在每次循环中,Test2要读两次内存(sum和i都从内存中读),写一次内存(将求得的和写到内存中),而方法Test3只需要读一次内存,即从内存中读i的值,方法Test3的性能比Test2高就不言而喻了。就因为Test2每次都是以引用的方式读sum的值,CPU要得到sum的值,就得通过sum在内存中的地址,所以必读内存,,而Test3不必读内存,用一个寄存器即可。

 

2:利用局部性提高程序性能。还是直接看一个简单的例子

        static int Test4(int[,] arr, int row, int column)
        {
            int sum = 0;
            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < column; j++)
                {
                    sum += arr[i, j];
                }
            }
            return sum;
        }

        static int Test5(int[,] arr, int row, int column)
        {
            int sum = 0;
            for (int j = 0; j < column; j++)
            {
                for (int i = 0; i < row; i++)
                {
                    sum += arr[i, j];
                }
            }
            return sum;
        }

简单一个看,两个方法几乎是完全一样,不同的是Test4是按行求和,而Test5是按列求和。如果多次执行这两个方法进行,对比就会方法Test4的性能高于Test5。运行两个方法100000次,结果如下:

为什么按行求和比按列求和快呢?简单一句话:数组是按行存的。CPU每次从内存读数据,不是要哪个就读哪个就直接读哪个,而是每次读一个高速缓存行,就是每次要多读一些,如,果需要的数据在高速缓存行中,就不用到内存中去读了,而是直接从高速缓存行中取,当然就比再从内存中读取要快一些。而在这里,数组按行存储,也就是说,CPU每次会读多个数组元素导高速缓存行,如果需要的元素在高速缓存行中就不用到内存中去取了,这就是传说中的命中率,按行求和命中率当然就高了。而按列求和,命中率自然低,CPU每次将一行中的多个元素读到高速缓存行中,按列求和每次只需要一个元素,也就是每次只需要一个元素而CPU却读了多个元素,命中率当然低,低到可能出现命中率为0。

程序的局部性包括:空间局部性和时间局部性,这里说的就是空间局部性。

空间局部性就是说一个被使用的到数据其周围的数据很可能会被马上使用。

时间局部性就是说一个被使用的到数据很可能会被再次使用。

更多内容请看《深入理解操作系统》。

作者:陈太汉

博客:http://www.cnblogs.com/hlxs/

相关文章:

  • Kali***(二)之被动信息收集——搜索引擎
  • 编码格式导致批处理文件bat文件不能执行
  • 我的信仰
  • 陶哲轩实分析公理8.1——选择公理
  • C# 小测试(一):类成员初始化与构造函数执行的顺序
  • debian中安装和编译ipvsadm问题
  • kubernetes 1.8 高可用安装(三)
  • Putty中为CentOS 5.5配置SSH证书登录验证(转)
  • 写JS的时候,想强制刷新页面,有些代码却不能很好的兼容
  • 选eMTC还是NB-IoT,不应该再是一个问题
  • HTML教程
  • 总结: 在fc23中, 安装音频mp3 视频flv 的播放插件其实很简单, 只要一步就可以了: dnf install gstreamer1-libav...
  • jxl操作excel(每页200个,每行4个)
  • 为什么ios不支持flash
  • C# Dictionary 的几种遍历方法
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 《Java编程思想》读书笔记-对象导论
  • Akka系列(七):Actor持久化之Akka persistence
  • Android Studio:GIT提交项目到远程仓库
  • ComponentOne 2017 V2版本正式发布
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • ESLint简单操作
  • Git同步原始仓库到Fork仓库中
  • Hibernate最全面试题
  • Iterator 和 for...of 循环
  • yii2中session跨域名的问题
  • 不上全站https的网站你们就等着被恶心死吧
  • 电商搜索引擎的架构设计和性能优化
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 如何学习JavaEE,项目又该如何做?
  • 微服务入门【系列视频课程】
  • 运行时添加log4j2的appender
  • MyCAT水平分库
  • puppet连载22:define用法
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • #、%和$符号在OGNL表达式中经常出现
  • (6)STL算法之转换
  • (C语言)fgets与fputs函数详解
  • (C语言)球球大作战
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Oracle)SQL优化技巧(一):分页查询
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)德国人的记事本
  • .java 9 找不到符号_java找不到符号
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .net core使用ef 6
  • .NET Core中的去虚