|
[原创]程序的效率
对于一个程序员来说,程序的效率是一个不能忽视的问题,各种论坛上也充满了各种各样的关于效率的争论,这就不得不引起一种思考,一个程序的效率究竟取决于哪些因素呢?我们应该如何均衡效率问题和其他软件设计问题之间的利弊关系呢?这就是这篇文字想要讨论的问题。
首先,我们来看看,哪些因素左右了我们程序的效率,大致可以分为以下几个因素:
第一,语言和平台:每一种语言都有自己赖以生存的平台环境,这关系到一个语言本身最根本的内部机制,比如java必须运行于JVM,.NET必须有CLR的支持,c\c++必须有支持该语言的编译器和各种库,这些属性就决定了他们对于CPU指令的操作距离,这样的距离越远,当然他们的编译出来的程序的运行效率就越低,拿x86系列来说吧,JVM是面向操作系统的。因此只要安装了对应的操作系统版本,java就能做到编译一次,任何地方都可以运行的目标,但是这里的运行自然是JVM面向操作系统的解释运行,如此一来速度自然快不了;CLR呢?他是面向windows系统的,他等于是“做入”了windows,在第二次JIT编译之后,他便可常驻系统,如此一来,他的速度在windows上自然会快一些,当然也因此失去了跨操作系统运行的能力(虽然理论上可行,但实际上非常不理想,有兴趣的人可以关注一下mono项目);c\c++则只要编译之后,基本上可以和CPU直接通话,效率自然是很高了,但是他们对硬件的依赖程度就大大增加,从而对程序员的要求也达到了新的高度。
第二, 数据结构和算法:语言通常只是一种表达工具,平台是你的工作环境,一般情况下,程序员并没有选择,那么自然他们只能 在表达的内容上下一些功夫了,选择正确的数据结构,可以是程序的编写变得相当容易,而又不损失效率,ADT的思想可以使得我们摆脱数据具体存储的细节问题,我们可以知道二叉树、哈希表利于搜索,链表利于存储,向量利于随机等等,使得我们可以有效的组织内存空间来达到换取CPU效率的目的;算法的设计可以尽可能的减少CPU指令的数量,从而使问题的复杂度呈几个数量级的降低,比如数组的最大子序列问题,一般的设计,这样的问题会做成平方(O( ))甚至立方(O( ))的复杂度,然后通过动态规划和分治法等的重新设计,该问题可以在线性(O(N))复杂度里解决。好的算法可以使得处理海量数据的时间变得可以接受,同时又让内存的使用量控制在合理的范围内,这自然是很大的学问,这里不可能累述,我推荐去看各种专业书籍,比如《算法导论》。
第三, 库的使用:我们都知道技术问题的解决都是站在巨人的肩膀上完成的,很少有人什么都是自己做的,而且你设计的同样的算法和数据结构,往往并没专业库提供的效率高,原因自然是多方面的,比如一些针对编译器的优化,细节上的运算技巧等等,在因此库的选择成了左右程序的效率的另一个主要因素,当然这根本上还是对数据结构和算法的认识,无非就是无需去亲自实现罢了。
然后,我们必须明白,效率问题不是程序设计的唯一问题,甚至有时不是主要问题,尤其在CPU继续遵行摩尔定律在飞速更新的情况下,在一些非海量数据处理的程序里,效率问题并不是那么突出的,这里不是说效率问题不重要了,而是说效率问题和其他软件设计问题之间应该达到一种均衡,这里有如下几个问题:
第一、80-20法则,这是说,一个程序的20%的代码决定了这个程序80%的效率,举个很简单的例子,位运算比算术运算要快,因此位运算放在程序里的效率要高,但是这样的运算如果不是程序的核心处理,每次运行个一两次,这样的效率甚至可以忽略不计的,如果他处在程序的核心部份(也就是那20%的代码),每次运算个上百万次。优势当然就有所体现了,所以对效率的关注要有重点,不能面面俱到。
第二,移植性和通用性,这两个特点是软件界一直在追求的目标,对于移植来说,有些高效的设计往往是不可移植的(比如位运算在32位的CPU下和64位的CPU下的结果就未必相同),语言平台也同样如此,java的程序和c程序的移植性显然也是不可同日而语的;通用意味着代码的可维护性,高效的算法有时候是面对某些特殊的问题而设计,逻辑结构极其复杂,自然给后来的维护以及通用带来许多麻烦,这些问题要根据具体的情况加以均衡,其中使用各种通用程序库也是不错的解决方法之一。
无论如何,程序的效率依然是程序设计过程中一个非常重要的因素,因为CPU的发展速度还是远远落后于数据的处理量,但是凡事都不能过度,效率不是程序的唯一瓶颈,懂得均衡的设计才有可能是好程序。 |
|