我已经写程序35年了,我在优化程序执行速度方面做了很多工作(一个例子),我也见过其他人做优化。我发现有两种基本的优化技术经常被忽视。请注意,这两种技术并非旨在避免过早优化。不是把冒泡排序变成快速排序(算法优化)。它也不是一种语言或编译器优化。将i*4写为i<<2也不是优化。这两种技术是:使用分析器。查看程序执行时的汇编代码。使用这两种技术的人会成功地编写出快速的代码,而那些不使用的人则不会。让我详细解释给你听。使用Profiler我们知道程序运行时90%的时间花在了10%的代码上。我觉得这不准确。一次又一次,我发现几乎所有程序都将99%的运行时间花在了1%的代码上。但它是哪1%?一个好的Profiler可以告诉你答案。即使我们需要用100个小时来优化这1%的代码,也比用100个小时来优化其他99%的代码要有益得多。问题是什么?人们不使用分析器?不。我工作过的一个地方使用了一个华丽而奢侈的Profiler,但自从我买了它之后,包装仍然和3年前一样新。为什么人们不使用它?我真的不知道。有一次,我和同事去一个超负荷的交易所,同事坚持说他知道瓶颈在哪里,毕竟他是一个非常有经验的专家。最后,我在他的项目上运行了我的Profiler,我们发现瓶颈在一个完全意想不到的地方。这就像赛车。该团队在提供一切的传感器和日志上取胜。你可以调整赛车手的裤子,让它们在比赛中更舒适,但这不会让你赢得比赛,也不会让你更有竞争力。如果你不知道你的速度是否因为引擎、排气、空气动力学、轮胎压力或驾驶员而下降,那么你就无法获胜。为什么编程应该有所不同?只要不衡量,就永远无法改进。世界上有很多Profiler。找一个就可以看到你的函数的调用层次,调用次数,以及前面每段代码的时间分解表(甚至到汇编层次)。我见过太多的程序员回避使用Profiler,而是将时间花在错误方向上无用的“优化”上,结果却被其竞争对手羞辱。(译者陈浩注:使用Profiler时,重点关注的点是:1)需要花费大量时间来优化其算法的函数,2)被调用很多的函数——如果一个函数每秒被调用300K次,你只需要0.001毫秒,这是一个相当大的优化。这就是作者所说的1%的代码占用了99%的CPU时间)看汇编代码几年前,我有一个同事MaryBailey,她在华盛顿大学教授代数,有一次,她在黑板上写:x+3=5并要求他的学生“求解x”,学生们不知道答案。于是她写道:__+3=5然后,让学生“填空”,所有学生都能回答。未知的x就像一个神奇的字母,让每个人都认为“x是代数,而我没有学过代数,所以我不知道该怎么做”。汇编程序是编程世界的代数。如果有人问我“内联函数是编译器展开的吗?”或者问我“如果我写i*4,编译器会将它优化为左移操作吗?”。这个时候我会建议他们看看编译器的汇编代码。这样的回答岂不是粗鲁无用?通常我这样回答提问者之后,提问者通常会说,sorry,我不知道assembly是什么!即使是C++专家也会这样回答。汇编语言是最简单的编程语言(即使与C++相比),如:ADDESI,xis(C-stylecode)ESI+=x;并且:CALLfoo是:foo();细节因CPU类型而异,但这就是它的工作原理。有时候,我们甚至不需要细节,只看汇编代码长什么样,再和源代码对比一下,就可以知道汇编代码很多。那么,这对代码优化有何帮助?例如,几年前我认识的一位程序员认为他应该发现一种新的、更快的算法。他有一个基准来证明这个算法,他写了一篇关于他的算法的非常漂亮的文章。然而,有人看了他的原始算法和新算法的编译,发现他的算法改进版本允许他的编译器将两个除法运算变成一个。这真的与算法无关。我们知道除法运算是一个开销很大的运算,而在它的算法中,这两个除法运算还是在一个内嵌循环中,所以他的算法改进版当然更快。不过,只需要对原来的算法做一个小改动——使用除法运算,那么原来的算法就会和新算法一样快。而他的新发现算不了什么。在下一个示例中,一位D用户发布了一个基准测试,显示dmd(DigitalMarsD编译器)在整数运算方面很糟糕,而ldc(LLVMD编译器)要好得多。对于这样的结果,还是挺自以为是的。我快速查看了程序集,发现两个编译器编译的结果完全相同,没有明显的原因导致如此大的2:1差异。但是我们看到有一个long型整型的划分,这个划分调用了runtime库。而这个库就成了一个耗时杀手,其他所有的加减法都没有速度影响。没想到benchmark跟算法代码生成一点关系都没有,完全是长整数除法的问题。这暴露了dmd的运行时库中长除法的糟糕实现。修正后可以提高速度。所以,这与编译器无关,但如果不查看程序集,您将无法发现其中的任何内容。查看汇编代码通常会给您一些意想不到的东西,让您了解程序为何如此运行。一些意想不到的函数调用,意想不到的嚣张,不该存在的东西,一切都是事实。但是您也不需要成为汇编代码黑客才能做到这一点。结语如果你觉得你需要一个执行速度更好的程序,那么最基本的方法就是使用一个profiler,并且乐于查看它的汇编代码来找到程序的瓶颈。只有找到了程序的瓶颈,才是真正考虑如何改进的时候,比如思考更好的算法,使用更快的语言优化等等。传统观点认为制胜法宝是选择最佳算法而不是微观优化。虽然这种方法是毋庸置疑的,但有两件事是学校没有教给你的,需要你注意。首先也是最重要的,如果你优化的算法没有参与到你程序性能的算法中,那么你优化它只是在浪费时间和精力,而且还会转移你的注意力,让你错过应该做的事情。优化部分。第二点,算法的性能与处理的数据密切相关。即使冒泡排序有那么多笑话,如果它处理的数据基本都是有序的,只有少数是未排序的,那么冒泡排序也是所有排序算法中性能最好的。因此,担心没有使用好的算法和没有进行测量只是在浪费时间,对你和计算机都是如此。就像为赛车零件订购速度端不会让您更接近冠军(即使您正确安装了零件),如果没有Profiler,您将不知道问题出在哪里,如果不查看汇编您可能会知道是什么问题是,但你常常不知道为什么。
