在尝试任何优化之前,请了解当前的情况。
十有八九,时间并不会消耗在你猜测的地方。
通常不成功的策略是微观优化,实际需要的是适当的算法。
必须引用唐纳德·库斯(Donald Knuth)的话:
"我们应该忘记小优化,大约有97%的时间: 过早的优化是万恶之源"
步骤:
首先,获得一个性能分析器来测量代码。不要陷入认为你知道瓶颈在哪里的陷阱中。即使之后你的假设被证明是正确的,也不要认为下一次有类似任务时可以跳过测量步骤。
然后,分析您的发现。查看代码,识别出您可以对其进行改进以产生效果的瓶颈。尝试估计这将带来多少改进。
根据您的分析决定是否要走这条路。收益是否值得?是否需要重写代码?也许您会发现,虽然运行速度很慢,但已经达到了极限,或者您接近性能曲线的顶部,在那里需要付出巨大努力才能获得微小的改进。
实施您的更改,必要时进行重写,或者如果您已经选择了重构路径,则重构代码。逐步进行,以便于测量您的更改是否达到预期,或者您是否需要回退一步并尝试另一种方法。
然后回到开始,重新测量、分析、决策、实施等。
另外,在重构代码的时候,首先应该更改的是大型算法级别的方法。例如,用性能更好的排序算法替换原来的算法等。不要从行级别的优化开始,比如如何让递增值的行更快。这些通常是最后级别的优化,除非你正在极端性能条件下运行,否则通常不值得。
首先不要忘记这些:
其次;
不要假设,试一试
我认为这是优化的基本规则,测试它,除非你测试并证明它有效,否则你不会知道。
在您的情况下,我会首先重构代码,了解它。
如果您有单元测试,那么很幸运,只需逐个函数进行检查,并具体查看最常调用的代码(使用分析工具观察调用和瓶颈所在)。 如果您没有测试,请编写一些基本测试以确认某些条件下的整体输出,以确保您不会破坏任何内容并且可以自由尝试。
除了性能分析,正如每个人都提到的那样,我总是首先考虑记忆化和延迟加载这两个解决方案(在性能分析之后),它们都很容易实现并且通常会产生很大的差异。
所有的答案都很好。
我想要完善一下建议中的“测量”部分。我进行测量是为了量化我可能做出的任何改进。然而,为了找到需要修复的问题(这是不同的目的),我会手动获取多个调用堆栈样本。
假设,为简单起见,程序运行需要20吉赫周期,但实际上需要10个周期。如果我随机暂停它10次,那么在其中5次左右,它将处于其中一个不必要的周期中。我可以通过查看每个调用堆栈层来确定该周期是否必要。如果在堆栈的任何级别上有任何调用指令可以被消除,则该周期是不必要的。如果这样的指令出现在多个堆栈上,则消除它将加速程序,加速的百分比大约等于它所在的堆栈样本的百分比。
任何出现在多个堆栈上的指令都是可疑的——堆栈越多,可疑性越高。现在,call _main
可能是我无法删除的一个指令,但是
foo.cpp:96 call std::vector::iterator:++
如果它在多个堆栈上出现,那么它绝对是一个需要关注的焦点。
出于风格原因,有些人可能不想替换它,但是他们会大致知道为这个选择付出了多少性能代价。
因此,优化的过程就是识别嫌疑对象并找到一种替换或消除它们的方法。
困难的部分(我已经做过很多次)是理解什么是必要的,什么是不必要的。为此,您需要了解在该程序中如何以及为什么执行某些操作,这样,如果某个活动是一个循环占用资源的问题,您就可以知道如何安全地替换它。
有些循环占用资源的问题可能很容易解决,但您很快就会遇到一些您不知道如何安全替换的问题。为此,您需要更熟悉代码。
如果您可以向已经在该程序上工作过的人请教,那将会很有帮助。
否则(假设代码约为10^6行,就像我曾经处理过的那样),您可以相对容易地加速一些操作,但要超越这个范围,可能需要几个月的时间才能达到您舒适地进行重大更改的水平。
好的策略
除了提到的基本优化法则(测量,如果不必要就不要进行优化),尽管问题明确要求效率,但我总是在检查期间对这样的代码进行重构。
通常,性能差的代码也很难理解。因此,在重构时,我会确保代码本身更易于理解和更好地记录文档。这是确保我知道我正在优化什么的基础(因为在大多数情况下,该代码片段的要求也不会完全可用)。
何时停止
对于运行非常缓慢的应用程序,您通常会在分析器中显示单个方法(或一组相关方法)的运行时间出现峰值,从而显示编程错误或设计缺陷。因此,如果分析方法的运行时间分布大致相等(或者显示的瓶颈方法的大部分是平台代码,例如Sun Java方法),我通常会停止。如果客户需要进一步优化,则必须重新设计应用程序的较大部分,而不是优化现有代码。
一定要有足够的单元测试,以确保您进行的任何优化都不会破坏任何东西。
确保限定您的执行环境。有时,执行选项的简单更改可以大有作为。
然后,才开始分析代码。
重写决策(针对已经工作的代码)只有在当前架构可能无法支持足够的未来发展时才应该考虑。
如果简单的修复可以加速一个不太可能发展的代码,那么就不需要完全重写。
通常与最终用户(客户端)合作确定停止标准,但我建议使用正式文件来确定此优化过程的目标。
首先确定您的优化目标 - 为给定硬件平台上特定操作的时间设置目标。准确地测量性能(确保您的结果可重复)并在类似生产环境的环境中进行测试(除非这是您在生产中使用的VM等)。
然后,如果您认为速度已经足够快,那么可以停止优化。
如果仍然不够好,则需要进行一些额外的工作 - 这就是分析的作用。您可能无法很好地使用分析器(例如,如果它会对行为产生影响),在这种情况下,应改用仪表化。