警惕程序优化的七大陷阱
优化,仅从字面上就给人一种美好的感觉。没错,优化是一个好东西。通过优化,我们可以减少程序占用的内存,可以缩短计算耗费的时间,可以处理以前处理不完,处理不了的数据。但是,俗话说得好,every rose has its thorn,围绕着优化,有许多陷阱,需要小心避开。下面结合个人经验,总结一下程序优化的七大陷阱。一是优化够用的程序。经常会见到有人在论坛上抱怨说,前任程序员写的代码虽然凑合能用,但是左看右看怎么看都觉得碍眼,觉得这里不够优化,那里还能改进,想要“刷刷刷”删掉重写。实际上,大量重写可用的代码从来不是一个轻松的工作,决定重写和优化之前,一定要慎之又慎。毋庸置疑,重写代码可以让程序的结构更好,可以提高性能,但是同时也需要耗费大量人月,需要承担破坏程序可用性的风险。回想多年之前维护的那个大型系统,它从代码风格到算法实现都让我不断地摇头叹息,每次改动起来都不得不捏着鼻子。但是我拿它没有办法,因为事实证明,它能够胜任自己的工作。直到有一天,领导叫住我说,别在这坨东西上修修补补了,你需要做的是全新的一套,放手去干吧,我给你充足的时间。这时候,我知道自己的机会来了,是时候翻开新篇章了。
二是优化老化的程序。这是第一种陷阱的反面。做这种优化就像是给一个老化的桑塔纳换上兰博基尼的发动机,得不偿失。软件虽然不像硬件那样,有着固有的物理生命周期,但是也有生老病死。随着时间的推移,软件需要不断地升级换代。如果软件的结构不好,或者主刀手没有高超的技艺,那么过了一段时日之后,程序就会得像魔兽争霸里面的憎恶一样,一眼看上去就是那么恐怖吓人,恶心至极。优化这样的程序,不仅工程量大,耗时长,而且维护极为困难。在另外一些情况下,程序本身内部仍然很健康,但已明显跟不上外部条件的变化,如需求的提高,软硬件环境的换代等。此时,也不要再纠结是不是还要在继续老程序上修修补补了。要知道,想养个恐龙当宠物,得从孵化恐龙蛋开始,科莫多喂得再大也还是蜥蜴。
三是过早地优化程序。.很多事情,即使很有必要做,也需要找到合适的时机,程序优化也是一样。一般来说,相对于过晚优化,过早优化的情况发生地更为普遍。因为随着时间的推移,程序的缺陷会暴露得越来越清晰。只要反馈机制不是过于薄弱,公司结构不是过于尾大不掉,开发者的神经不是过于大条,那么他会及时地收到警报。之所以强调不要过早优化,是因为在程序开发运行的早期,相比于优化,有很多更重要的事情要处理。例如很多时候,功能实现比程序优化更为重要,因为新增功能是一个零到一的阶跃,而程序优化是从一到一百的渐变。如果在系统还没有完成的时候,就搁置其他更重要的事情,心急火燎地开始优化,最后很有可能被证明是在浪费时间,甚至会适得其反。同时应该注意到,对系统特新和应用特点的理解,是一个积累的过程,需要一定时间的理解和分析。只有对系统理解充分了,把测试数据掌握全面了,结果分析透彻了,才能找到影响性能的关键因素,才能直指问题本质。
四是设立过于乐观的优化目标。我不是一个悲观主义者,但是几十载的经历告诉我,从来没有什么好事能不需要努力争取,而莫名其妙从天上掉下来。天上掉下来的不是馅饼,也不是林妹妹,而是一坨一坨的烦心事。因此,对每件事,我都保持谨慎的乐观,对程序优化也是如此。凭着这种观念,我最近就与同事进行了一番争吵。那时我们对一个高速缓存系统的设计进行讨论。他说希望能把系统做得的高效,高性能,并且非常通用。我一听,立即产生了警觉,反诘道,凭什么它的性能比页高速缓存高,同时又能和页高速缓存一样通用?难道是说页高速缓存实现得太烂,不然怎么会那么轻而易举被我们超越?经过一番激烈的争论,我们才达成了共识,认清了修改接口语义的必要性,也就意味着应用需要为性能提升付出代价,它们要忍受一致性语义的放松。这时,我才放下心来,因为它符合我中庸的观念,终于没有设立过于完美的预期目标。理想是丰满的,现实是骨感的。在骨感的现实面前,只有设立可行的优化目标,才不会在谜底揭晓的那一刻给自己的信心以沉重的一击。
五是在错误的层次上优化。这里层次的含义是什么呢?我们都知道软件开发的流程首先是进行需求分析,然后设计架构,确定数据结构,选择算法,最后是编码实现。这是一个阶段一个阶段,步步推进的过程,也对应了可以进行优化的各个层次。其中,可以产生最大优化效果的是对应用需求进行深入挖掘。君不见一些重大优化手段的提出就是因为发现了应用中可以利用的特殊性质。利用这些性能,可以优化系统结构的设计,进而决定数据结构与算法的选择,最后影响到编码细节。然而,很多人有意无意忽视了这一点。相反,一些具体实现上的细节,如编程语言、编译器、硬件结构的特性等,造成的细微性能差异却被众多的书籍长篇累牍地谈论。许多程序员也因知晓这些细节而深感自豪。更有甚者,一些IT企业在面试时经常拿出些不知道那个角落里翻出来的老古董作为考察应聘者的题目。殊不知,过度地抠挖细节,实际上是拣了芝麻丢了西瓜,最后发现这些细节上的优化,不仅没有可移植性,而且其空间会不断地被编译器等底层软硬件的优化蚕食,最后只是沦为一种智力游戏而已。因此,这里需要大声疾呼,过度抠挖细节的习惯是上古时代遗留下来的糟粕,需要及时摒弃。对这种行为,早就有人给予了批评,但一般主要强调的是算法的重要性,也即算法如果太烂,细节处理得再好,性能也上不去。不过,经过这么多年的积累,基础算法已经发展得极为完备了,需要用到的常用算法,基本上都能找到前人的成果,可继续挖掘的空间并不是太大。反倒是算法之上的系统架构与应用需求分析,则越来越彰显出其重要性。可以确信的是,在这方面有足够广阔的空间供各领域的程序员大做文章,施展抱负。
六是优化错误的代码片段。说得老套一点,就是抓住主要矛盾,忽略次要矛盾,也可以表述成哪里都能见到它的“二八原理”,即百分之二十的代码消耗了八分之八十的时间,只有优化这一段代码,才能取得明显效果。这虽然是老生常谈,但是总有人在这上面耗费青春,纠结在一些完全不会造成性能瓶颈的地方,所以这里还是需要强调一下。
七是过度重复前人的工作。牛顿说,他是站在巨人肩膀上摘苹果的。我说,与从前的巨人相比,我们只是一帮孩纸。毛主席说,只要想得到,就能做得到。我说,即使我能想得到,可能也做不到,但是这个世界上肯定有人已经做到了,不然就是根本不可能有人能做到。好了,不瞎掰了。我想说的是,对于一个新问题,只要应用场景不是特殊,不是那么难以描述,不是没有办法敲入到搜索框中,那么一定能找到前人在这方面的相关工作。很多时候,我等芸芸众生所做的事情,不过是把前人总结的经验再经历一遍,把前人发现的知识再学习一遍,把前人的一般性成果在实际工程中再应用一遍。所以,动手优化程序之前还是要多找找论文,多翻翻TAOCP,多搜搜开源软件,看看能不能借鉴前人的工作。当然,也不要期望前人能帮我们完成所有的事情,他们不可能在昨天就煮熟我们今天吃的米饭,但是米饭究竟要怎么煮熟煮香煮好,肯定已经被他们研究得透彻见底了。
最后让我们杜撰几段关于汽车改装的对话,增进一下记忆。
“你知道我为什么要改装汽车吗?你想想吧,开着改装车上下班,在北京四环上招摇过市,多拉轰,多有面子?!”
““拉倒吧!需要改进的是北京的交通,而不是您的车。”
“我想改装我们家老爷车,它的速度忒慢了!”
“三思啊!您确定它受得了折腾吗?如果我踢一脚它的屁股,它会不会直接散架,变成一个零件堆?”
“请一定想想办法,让我看到我的车子突破音障,在天地之间飞驰!”
“您请回吧,您需要的东西,我们地球人称之为灰机。”
“你好,在改装汽车之前,我有几个简单的问题想请教一下。请问:啥是汽车?啥是改装?为啥要改装汽车?”
“小盆友,乖,不要闹了,你妈喊你回家吃饭。”
“改装汽车用得着那么打动干戈吗?是不是只要拧几个螺丝,紧几个发条就大功告成了?”
“您好,我们这里是汽车改装中心,暂不提供闹钟修理服务。”
“是不是要把驾驶座,保险杠,探照灯,还有一些螺丝全给拆掉,它们忒重了,严重防碍加速!”
“个人建议,在搞破坏之前,还是先干正事吧。如果您想购买F1赛车,请出门右转。”
“其实,我在想,为了改好我的车, 首先第一步,就是要配一个好的发动机。一想到发动机,我就在想现在的发动机怎么设计得都那么挫?难道那帮人就没有真正动过脑子吗?!你知道吗,我确信答案就藏在物理学或者化学的某个角落里!找到了那个原理,你就找到了把发动机设计得更好的关键。可是一提到自然科学,我就不由自主地产生怀疑。有时候我压根不敢相信科学家讲的那一套。你知道吗,他们太过自信了。每个科学家都认为自己能找到事情的原因。可是他们真的能做到吗?事件甲先于事件乙发生,他们就认定甲是乙的原因。难道他们没有仔细想想,这里面是有问题的吗?真不敢相信,他们竟然从没有怀疑过。不行,我一定要把这些问问一个个想清楚了,不然的话,我们改装汽车的大业就失去了支撑,就失去了指导,就迷失了方向。我们完全是在瞎搞胡搞!我不能让这种事情发生。我得回去想想了,在想好之前,我们不能轻举妄动!我已经意识到时间完全不够用了,我得抓紧一切时间了。我不能跟你瞎聊了,再见了朋友!”
“再见了,您呐!希望下次见面不必等到沧海桑田。”
作者:fsdev 发表于2012-5-1 20:46:02 原文链接
都是曾经犯过或即将要犯过的错误,深有感触。尤其是第一、二在维护别人的系统中存在;三会发生成设计产品的阶段;程序员都是激进的,都是不能真实衡量自己能力的,所以往往头脑一热就设定很理想的目标。 五、六的错误99%的人都会犯,并且一直犯着,自我感觉良好。七是所有程序员都在犯的问题,
页:
[1]