代码优化概要

标签: 技术读物 流程方法 编程语言 Coding Optimization | 发表时间:2010-09-20 08:22 | 作者:陈皓 raphael
出处:http://coolshell.cn

本文译自Dr. Dobb’s Blogger的Walter Bright写的《Overlooked Essentials For Optimizing Code


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

使用这两个技术的人将会成功地写出运行快的代码,不会使用这两个技术的人则不行。下面让我为你细细道来。

使用一个 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,你不会知道问题在哪里,不去看汇编,你可能知道问题所在,但你往往不知道为什么。 (全文完)

相关文章

相关 [代码 优化] 推荐:

Java代码优化

- - ImportNew
2016年3月修改,结合自己的工作和平时学习的体验重新谈一下为什么要进行代码优化. 在修改之前,我的说法是这样的:. 就像鲸鱼吃虾米一样,也许吃一个两个虾米对于鲸鱼来说作用不大,但是吃的虾米多了,鲸鱼自然饱了. 代码优化一样,也许一个两个的优化,对于提升代码的运行效率意义不大,但是只要处处都能注意代码优化,总体来说对于提升代码的运行效率就很有用了.

代码优化概要

- raphael - 酷壳 - CoolShell.cn
Dobb’s Blogger的Walter Bright写的《Overlooked Essentials For Optimizing Code. 我编写程序至今有35年了,我做了很多关于程序执行速度方面优化的工(一个示例),我也看过其它人做的优化. 我发现有两个最基本的优化技术总是被人所忽略. 注意,这两个技术并不是避免时机不成熟的优化.

Java 代码性能优化

- - IT瘾-geek
代码 优化,一个很重要的课题. 可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢. 这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗. 没用,但是,吃的小虾米一多之后,鲸鱼就被喂饱了. 代码优化也是一样,如果项目着眼于尽快无BUG上线,那么此时可以抓大放小,代码的细节可以不精打细磨;但是如果有足够的时间开发、维护代码,这时候就必须考虑每个可以优化的细节了,一个一个细小的优化点累积起来,对于代码的运行效率绝对是有提升的.

也谈JavaScript代码性能优化

- 可乐加糖 - Fdream&#39;s Blog
差不多两年前写了个选择器whiz,除在DOM查找方面做了许多优化工作之外,还在代码优化上做了很多工作,一直没有分享. 抽空总结一下,基本上在jQuery、Mootools和YUI的源码里面都可以看到这些写法. 有些是已经在网上分享很多遍了,众所周知的,也有一些可能写了多年的JavaScript的开发人员也不一定想得到的.

jQuery代码优化:基本事件篇

- - 可咔酷 | 网络杂货铺
jQuery对事件系统的抽象与优化也是它的一大特色. 本文仅从事件系统入手,简要分析一下jQuery为什么提供mouseenter和mouseleave事件,它们与标准的mouseover、mouseout事件有什么区别. 说到事件,就要追溯到网景与微软的“浏览器大战”了. 当时,事件模型还没有标准,两家公司的实现就是事实标准.

代码优化的另一面

- - InfoQ cn
优化软件是一件好事,但如果使用不当,就会好事变坏事. 如果你在优化代码时走向了错误的道路,那么这种优化会提高开发成本、降低生产力. 在软件开发过程中,成本需要时刻谨记在心. 一般来说,优化的软件需要花费更长的时间来交付,因为你需要花费精力使它质量更高. 有时候,你并不是为了运行速度而做优化. 对于嵌入式系统来说,可能是减少内存使用,对于手持设备,可能是硬件资源限制.

Android代码优化小技巧总结

- - 移动开发 - ITeye博客
关注微信号:javalearns   随时随地学Java. 这篇文章主要是介绍了一些小细节的优化技巧,当这些小技巧综合使用起来的时候,对于整个Android App的性能提升还是有作用的,只是不能较大幅度的提升性能而已. 选择合适的算法与数据结构才应该是你首要考虑的因素,在这篇文章中不会涉及这方面.

PHP代码优化24条真经

- - 外刊IT评论
使用echo的多重参数代替字符串连接. 在执行for循环之前确定最大循环数,不要每循环一次都计算最大值,最好运用foreach代替. 对global变量,应该用完就unset()掉. 用单引号代替双引号来包含字符串,这样做会更快一些. 因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会. 函数代替正则表达式完成相同功能.

JAVA优化代码策略(一)

- - Web前端 - ITeye博客
在Java程序中,性能问题的大部分原因并不在于Java语言,而是在于程序本身. 所以养成好的代码编写习惯非常重要. 比如:String 对象的使用中,出现字符串连接情况时应用StringBuffer 代替. 由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理. 因此,生成过多的对象将会给程序的性能带来很大的影响.

Android性能优化篇:从代码角度进行优化

- - 移动开发 - ITeye博客
关注微信号:javalearns   随时随地学Java. 通常我们写程序,都是在项目计划的压力下完成的,此时完成的代码可以完成具体业务逻辑,但是性能不一定是最优化的. 一般来说,优秀的 程序员在写完代码之后都会不断的对代码进行重构. 重构的好处有很多,其中一点,就是对代码进行优化,提高软件的性能.