代码优化概要

我编写程序至今有35年了,我作了不少关于程序执行速度方面优化的工(一个示例),我也看过其它人作的优化。我发现有两个最基本的优化技术老是被人所忽略。 注意,这两个技术并非避免时机不成熟的优化。并非把冒泡排序变成快速排序(算法优化)。也不是语言或是编译器的优化。也不是把 i*4写成i<<2 的优化。 这两个技术是:git

使用 一个profiler。
查看程序执行时的汇编码。
使用这两个技术的人将会成功地写出运行快的代码,不会使用这两个技术的人则不行。下面让我为你细细道来。程序员

使用一个 Profiler
咱们知道,程序运行时的90%的时间是用在了10%的代码上。我发现这并不许确。一次又一次地,我发现,几乎全部的程序会在1%的代码上花了99%的运行时间。可是,是哪一个1%?一个好的Profiler能够告诉你这个答案。就算咱们须要使用100个小时在这1%的代码上进行优化,也比使用100个小时在其它99%的代码上优化产生的效益要高得多得多。 问题是什么?人们不用profiler?不是。我工做过的一个地方使用了一个华丽而奢侈的Profiler,可是自从购买这个Profiler后,它的包装3年来仍是那么的暂新。为何人们不用?我真的不知道。有一次,我和个人同事去了一个负载过大的交易所,我同事坚持说他知道哪里是瓶颈,毕竟,他是一个颇有经验的专家。最终,我把个人Profiler在他的项目上运行了一下,咱们发现那个瓶颈彻底在一个意想不到的地方。 就像是赛车同样。团队是赢在传感器和日志上,这些东西提供了全部的一切。你能够调整一下赛车手的裤子以让其在比胜过程中更舒服,可是这不会让你赢得比赛,也不会让你更有竞争力。若是你不知道你的速度上不去是由于引擎、排气装置、空体动力学、轮胎气压,或是赛车手,那么你将没法获胜。编程为何会不一样呢?只要没有测量,你就永远没法进步。 这个世界上有太多可使用的Profiler了。随便找一个你就能够看到你的函数的调用层次,调用的次数,之前每条代码的时间分解表(甚至能够到汇编级)。我看过太多的程序员回避使用Profiler,而是把时间花在那些无用的,错误的方向上的“优化”,而被其竞争对手所羞辱。(译者陈皓注:使用Profiler时,重点须要关注:1)花时间多的函数以优化其算法,2)调用次数巨多的函数——若是一个函数每秒被调用300K次,你只须要优化出0.001毫秒,那也是至关大的优化。这就是做者所谓的1%的代码占用了99%的CPU时间)算法

查看汇编代码
几年前,我有一个同事,Mary Bailey,她在华盛顿大学教矫正代数(remedial algebra),有一次,她在黑板上写下: x + 3 = 5 而后问他的学生“求解x”,而后学生们不知道答案。因而她写下: __ + 3 = 5 而后,再问学生“填空”,全部的学生均可以回答了。未知数x就像是一个有魔法的字母让你们都在想“x意味着代数,而我没有学过代数,因此我就不知道这个怎么作”。 汇编程序就是编程世界的代数。若是某人问我“inline函数是否被编译器展开了?”或是问我“若是我写下i*4,编译器会把其优化为左移位操做吗?”。这个时候,我都会建议他们看看编译器的汇编码。这样的回答是否是很粗暴和无用?一般,在我这样回答了提问者后,提问都一般都会说,对不起,我不知道什么是汇编!甚至C++的专家都会这么回答。 汇编语言是最简单的编程语言了(就算是和C++相比也是这样的),如:编程

ADD ESI,x编程语言

就是(C风格的代码)函数

ESI += x;性能

而:优化

CALL foo编码

则是:日志

foo();

细节由于CPU的种类而不一样,但这就是其如何工做的。有时候,咱们甚至都不须要细节,只须要看看汇编码的长啥样,而后和源代码比一比,你就能够知道汇编代码不少不少了。 那么,这又如何帮助代码优化?举个例子,我几年前认识一个程序员认为他应该去发现一个新的更快的算法。他有一个benchmark来证实这个算法,而且其写了一篇很是漂亮的文章关于他的这个算法。可是,有人看了一下其原来算法以及新算法的汇编,发现了他的改进版本的算法容许其编译器把两个除法操做变成了一个。这和算法真的没有什么关系。咱们知道除法操做是一个很昂贵的操做,而且在其算法中,这俩个除法操做还在一个内嵌循环中,因此,他的改进版的算法固然要快一些。但,只须要在原来的算法上作一点点小的改动——使用一个除法操做,那么其原来的算法将会和新的同样快。而他的新发现什么也不是。 下一个例子,一个D用户张贴了一个 benchmark 来显示 dmd (Digital Mars D 编译器)在整型算法上的很糟糕,而ldc (LLVM D 编译器) 就好不少了。对于这样的结果,其至关的有意见。我迅速地看了一下汇编,发现两个编译器编译出来至关的一致,并无什么明显的东西要对2:1这么大的不一样而负责。可是咱们看到有一个对long型整数的除法,这个除法调用了运行库。而这个库成为消耗时间的杀手,其它全部的加减法都没有速度上的影响。出乎意料地,benchmark 和算法代码生成一点关系也没有,彻底就是long型整数的除法的问题。这暴露了在dmd的运行库中的long型除法的实现不好。修正后就能够提升速度。因此,这和编译器没有什么关系,可是若是不看汇编,你将没法发现这一切。 查看汇编代码常常会给你一些意想不到的东西让你知道为何程序的性能是那样。一些意想不到的函数调用,预料不到的自傲,以及不该该存在的东西,等等其实全部的一切。但也不须要成为一个汇编代码的黑客才能干的事。

结论若是你以为须要程序有更好的执行速度,那么,最基本的方法就是使用一个profiler和愿意去查看一下其汇编代码以找到程序的瓶颈。只有找到了程序的瓶颈,此时才是真正在思考如何去改进的时候,好比思考一个更好的算法,使用更快的语言优化,等等。 常规的作法是制胜法宝是挑选一个最佳的算法而不是进行微优化。虽然这种作法是无可异议的,可是有两件事情是学校没有教给你而须要你重点注意的。第一个也是最重要的,若是你优化的算法没没有参与到你程序性能中的算法,那么你优化他只是在浪费时间和精力,而且还转移了你的注意力让你错过了应该要去优化的部分。第二点,算法的性能总和处理的数据密切相关的,就算是冒泡排序有那么多的笑柄,可是若是其处理的数据基本是排好序的,只有其中几个数据是未排序的,那么冒泡排序也是全部排序算法里性能最好的。因此,担忧没有使用好的算法而不去测量,只会浪费时间,不管是你的仍是计算机的。 就好像赛车零件的订购速底是不会让你更靠进冠军(就算是你正确安装零件也不会),没有Profiler,你不会知道问题在哪里,不去看汇编,你可能知道问题所在,但你每每不知道为何。

相关文章
相关标签/搜索