分类: C/C++
2009-08-26 15:03:42
80-20准则说的是大约20%的代码使用了80%的程序资源;大约20%的代码耗用了大约80%的运行时间;大约20%的代码使用了80%的内存;大约20%的代码执行80%的磁盘访问;80%的维护投入于大约20%的代码上;通过无数台机器、操作系统和应用程序上的实验这条准则已经被再三地验证过。80-20准则不只是一条好记的惯用语,它更是一条有关系统性能的指导方针,它有着广泛的适用性和坚实的实验基础。
当想到80-20准则时,不要在具体数字上纠缠不清,一些人喜欢更严格的90-10准则,而且也有一些试验证据支持它。不管准确地数字是多少,基本的观点是一样的:软件整体的性能取决于代码组成中的一小部分。
当程序员力争最大化提升软件的性能时,80-20准则既简化了你的工作又使你的工作变得复杂。一方面80-20准则表示大多数时间你能够编写性能一般的代码,因为80%的时间里这些代码的效率不会影响到整个系统的性能,这会减少一些你的工作压力。而另一方面这条准则也表示如果你的软件出现了性能问题,你将面临一个困难的工作,因为你不仅必须找到导致问题的那一小块代码的位置,还必须寻找方法提高它们的性能。这些任务中最困难的一般是找到系统瓶颈。基本上有两个不同的方法用来寻找:大多数人用的方法和正确的方法。
大多数人寻找瓶颈的方法就是猜。通过经验、直觉、算命纸牌、显灵板、传闻或者其它更荒唐的东西,一个又一个程序员一本正经地宣称程序的性能问题已被找到,因为网络的延迟,不正确的内存分配,编译器没有进行足够的优化或者一些笨蛋主管拒绝在关键的循环里使用汇编语句。这些评估总是以一种带有嘲笑的盛气凌人的架式发布出来,通常这些嘲笑者和他们的预言都是错误的。
大多数程序员在他们程序性能特征上的直觉都是错误的,因为程序性能特征往往不能靠直觉来确定。结果为提高程序各部分的效率而倾注了大量的精力,但是对程序的整体行为没有显著的影响。例如在程序里使用能够最小化计算量的奇特算法和数据结构,但是如果程序的性能限制主要在I/O上(I/O-bound)那么就丝毫起不到作用。采用I/O性能强劲的程序库代替编译器本身附加的程序库(参见条款M23),如果程序的性能瓶颈主要在CPU上(CPU-bound),这种方法也不会起什么作用。
在这种情况下,面对运行速度缓慢或占用过多内存的程序,你该如何做呢?80-20准则的含义是:胡乱地提高一部分程序的效率不可能有很大帮助。程序性能特征往往不能靠直觉确定,这个事实意味着试图猜出性能瓶颈不可能比胡乱地提高一部分程序的效率这种方法好到哪里去。那么会后什么结果呢?
结果是用经验猜测程序那20%的部分只会导致你心痛。正确的方法是用profiler程序识别出令人讨厌的程序的20%部分。不是所有的工作都让profiler去做。你想让它去直接地测量你感兴趣的资源。例如如果程序太缓慢,你想让profiler告诉你程序的各个部分都耗费了多少时间。然后你关注那些局部效率能够被极大提高的地方,这也将会很大地提高整体的效率。
profiler告诉你每条语句执行了多少次或各函数被调用了多少次,这是一个作用有限的工具。从提高性能的观点来看,你不用关心一条语句或一个函数被调用了多少次。毕竟很少遇到用户或程序库的调用者抱怨执行了太多的语句或调用了太多的函数。如果软件足够快,没有人关心有多少语句被执行,如果程序运行过慢,不会有人关心语句有多么的少。他们所关心的是他们厌恶等待,如果你的程序让他们等待,他们也会厌恶你。
不过,知道语句执行或函数调用的频繁程度,有时能帮助你洞察软件内部的行为。例如如果你建立了100个某种类型的对象,会发现你调用该类的构造函数有上千次,这个信息无疑是有价值的。而且语句和函数的调用次数能间接地帮助你理解不能直接测量的软件行为。例如,如果你不能直接测量动态内存的使用,那么知道内存分配函数和内存释函数的调用频率也是有帮助的。(也就是,operators new, new[], delete, and delete[]—参见条款M8)
当然即使最好的profiler也是受其处理的数据所影响。如果用缺乏代表性的数据profile你的程序,你就不能抱怨profiler导致你优化程序的那80%的部分,从而不曾对程序通常的性能有什么影响。记住profiler仅能够告诉你在某一次运行(或某几次运行)时一个程序运行情况,所以如果你用不具有代表性的输入数据profile一个程序,那你所进行的profile也没有代表型。相反这样做很可能导致你去优化不常用的软件行为,而在软件的常用领域,则对软件整体的效率起相反作用(即效率下降)。