如何进行项目优化,这些是应该考虑的东西
代码优化的几个考虑的点
前言
最近在工作中碰到一个需求,要把之前一个庞大臃肿项目中的某一个业务剥离出来放到一个单独的小项目中,项目主管还特地告诉我要我把这个业务代码进行优化,因为这段代码实在是太慢了。累觉不爱。接下来我主要从sql优化入手,确确实实把这个业务的速度给提上去了。但是感觉项目优化应当是一个比较复杂且重要的东西,我这做的只是从某一个方向去进行代码优化。所以,看了一些资料以后把前辈总结的几个点贴在了下面。
以下内容原作:Ravi Shankar Rajan ,译者:欧剃
不要进行优化
代码优化的第一条原则就是,“不要”优化它。
这个程序是不是已经足够好了?你要去理解这个程序将会被如何使用,知道它是在怎样的环境下运行的,明白如果让它运行的更快到底有没有好处。在真正开始代码优化之前,你必须要问自己这几个问题。
没错,代码优化所耗费的经历和成本,只有在这样的情况下是有意义的:
1. 这个软件很重要
2. 它运行的确实很慢
3. 在保证代码健壮、正确、清楚的情况下,它确实还有改进的余地
一个程序,就算它运行得再快,如果无法得到正确的结果,那就毫无用处。有效的优化,给软件带来的好处应该总要比坏处多。但如果你的优化走错了路,那往往还不如别动它。
所以,你要做的第一件事,应该是设置一个合理的优化目标:
你需要清楚地了解你要达成的目标是什么,以及各种优化手段与这个目标之间的关系。
你需要明确而简单地说明这个目标,简单到就算技术理解能力最差的部门经理也能够理解和复述它。
你需要在整个过程中坚持这些目标。
要开始这项工作,最好的办法是,根据对目标的影响确定每项任务的优先顺序。你要做的每一件事情,都必须是可计量的。不要相信直觉,它基本上总是把你引向非常糟糕的方向。
使用一个分析器
贸然修改的最终结果可能就是做了无用功,所以我们需要用分析出系统的热点代码,先从热点代码入手。
在没有经过分析之前,不要贸然调整任何东西。最常见的错误做法就是,花了一整天去重构优化一段代码,结果在运行的时候发现,这段代码平时根本用不到。
分析器能精确地测量出你的程序把时间都花在什么步骤上了。有些分析器能列出每一个函数,包括它们被调用的次数,以及每次执行的时候耗时的占比等。
还有的分析器能列出每个命令的执行次数,被频繁执行的那些命令,在总占用时间上的权重肯定更高,而完全没被运行的那些命令,往往就是一些无用的代码,或者没有经过合适测试的代码。
一个好的分析工具,最有用的地方就是能让你发现软件中的“热点”,也就是消耗了最多运行时间的那些函数或者命令语句。基本上如果你发现了一个热点,你也就发现了问题所在。
性能分析的最佳使用方法就是识别出“热点”,然后尽可能地优化它们,接着再次测量,以查看是不是有新的热点冒了出来。
启用编译器优化
通常情况下,有种比较靠谱的优化方式,那就是打开编译器提供的那些内置的优化选项。
编译器优化通常会给你的程序带来几个百分点到两倍的运行速度提升。但某些情况下,这也可能反而降低速度,所以你需要在最终交付之前仔细测量性能优化的结果。不过总的来说,现代的编译器在这方面已经做的足够好了,程序员基本上再也不需要像以前那样,不停地对编译参数做各种频繁的小调整。
一些现代的编译器还具备全局优化能力,可以分析你的整个程序,以获得潜在的提升。如果你的系统中有这样的编译器,请一定要试试。它可能会把运行时间减少个几秒钟。
注意:编译器的优化设置越激进,最终编译出来的程序中出现不明 Bug 的可能性也越高。所以,强烈建议你在开启编译器的优化选项后,务必重新进行回归测试,以避免出现一些奇怪的意外。
调整代码
只有到这时,你才真正开始修改调整代码。在此之前,你必须已经通过第二步的性能分析发现了“热点”,并且试过使用编译器进行优化——毕竟绝大多数这些问题能让编译器帮你解决,也避免了你把这些代码弄得过于复杂。
那么,一般来说,有几种比较成熟的方法来处理这些“热点”。再次提醒,你必须非常谨慎,确保在提交每个更改之前,对它产生的影响进行测量。
那么,让我们看看这几个方法吧。
将常用的表达式计算归集在一起
如果同一个非常消耗性能的计算在多个地方重复出现,最好能只在一个地方进行计算,然后记住计算结果。除非必要,否则不要在循环中进行这样的计算。
用简单的计算代替消耗性能的算法
字符串处理对于任何一个程序来说,都算是非常常见的运算了。但如果你用错误的办法去处理字符串,它们也有可能消耗大量的性能。类似的,在某些情况下,你可以用一系列移位操作来代替乘法运算。
但请务必注意,这种方式或许能带来一些性能提升(其实并不一定),也有可能让你写出非常崎岖复杂的代码。所以在重构的时候,你必须非常注意代码可读性,以免写出无法维护的代码。
消灭循环
循环,往往是开销最大的行为,没有之一。在允许的情况下(例如迭代数量不太多的时候),尽量避免使用循环。
缓存常用的值
缓存能有效地利用本地性——也就是程序(以及用户)更倾向于重用最近的数据。你只需要缓存最常用的字符或数据,就能大大提高程序的性能。
使用一种更低层次的语言重写
警告:不到万不得已,不要这样玩。
更低层次的语言在利用硬件设备性能方面往往更具效率(看看 Python 里的内置函数是用 C 写的就知道了),但要写好这些东西,将会消耗更多的编程开发时间。
有时,通过用低层次的编程语言重写关键代码,能获得较大的性能提升,但这是以降低可移植性为代价的,也会让以后的维护变得非常困难。因此,请谨慎做出决定。
请记住:在优化工作中,做出选择这件事占了90%的权重。值得花时间来决定你要做什么,以及怎样才能做的对。当然,这也正是编程的黑科技之处!
在你的管理模型中加入代码审查环节
这条是同时写给开发者和管理者的。对于软件工程的管理者,你必须确保代码审查是项目开发过程的一部分;对于开发者,你应当将代码审查作为最佳编程做法中的必备环节。
结语
最后,任何代码的改进,都是从你自身开始的。在编程的世界里,你不可能从第一遍就非常完美地写出代码。你总需要对代码进行更改、修正错误,甚至有时代码无论如何都无法按照你想要的方式工作。
最后确实想吐槽我改的原代码,有点烧头发,或许这就是年轻程序员的资本吧。