优化代码速度/简介
优化是指代码调整过程,旨在提升某些方面:例如速度、内存消耗、输入/输出(磁盘读写或网络读写)等。在数学中,优化意味着找到性能最佳的值。在计算中,由于程序非常复杂,通常无法在数学意义上优化速度。因此,这个术语通常被用来指代朝着一个或多个方面提升性能的方向发展。
本文将重点介绍优化代码以使其更快地运行。然而,正如您稍后将看到的那样,这可能需要在其他方面优化代码。此外,程序员在尝试优化程序的某个方面时,通常是为了提高速度。
本文的目标受众是软件开发人员,主要是至少掌握一门编程语言,能够编写中等复杂程度程序的程序员。如果您有兴趣参与软件开发的世界,请参考开源教学网站,特别是文章"如何开始贡献或使用开源软件"。请注意,并非所有软件开发人员都是程序员,无论是那些将积极编程作为其工作定义的一部分的开发人员,还是那些有资格编写源代码的开发人员。该文章和该网站上维护的书籍提到了关于学习如何编程的资源,如果您有兴趣,可以参考这些资源。
然后,只有这样,您才能回到这本维基教科书。
那么,为什么要优化呢?如果您已经使用了一段时间计算机系统,并尝试过不太“完善”的软件应用程序,那么您很可能遇到过应用程序让您感到困扰的地方:启动时间过长;行为与预期不符;存在错误;执行看似微不足道的操作需要很长时间;无法取消操作;错误消息含糊不清,无法解释如何克服问题;程序无法退出;导致数据丢失;等等。所有这些问题都可能让您感到困扰和厌烦,使您感到不愉快。尽管现代计算机的计算能力和存储容量以及多功能性得到了应有的尊重,但计算机仍然是其主人(有感知能力的人类)手中的工具。因此,它们应该尽可能快、正确和彻底地执行其主人指示它们执行的功能,让我们能够无缝地继续使用它们来完成自己的工作或娱乐,或者换句话说,从计算中休息一下,做些其他事情,而不是与计算机作斗争。
一个能很好地执行其功能的软件组件或应用程序,具有很高的质量,并让用户感到快乐和掌控。另一方面,一个速度太慢、错误太多、不稳定性太高等的软件组件缺乏必要的质量,会让用户感到沮丧、痛苦、愤怒或失去掌控。没有人喜欢这种感觉。
如何创建和维护在各个方面都具有高品质的软件超出了本文的范围,有兴趣的读者可以参考之前由本文作者创作的关于此主题的一篇文章,标题为"什么是高质量软件?"。本文将重点介绍如何使我们的软件快速响应,这是质量的一个重要方面,有时会被忽略。
如果我们的程序速度太慢或响应迟缓(后者也被称为“迟钝”),那么我们的用户会感到沮丧,或者在程序运行时感到无聊,并去做其他事情。在这种情况下,我们通常应该优化程序,以确保其运行速度足够快,或响应足够快,从而让用户感到更快乐,因为他们不必等待程序完成或响应。我们在下面将更详细地介绍这方面的更多内容。
有一些类型的程序需要优化。其中一类是实时程序。尽管存在普遍的误解,但这些程序并不一定需要非常快。相反,实时程序是指必须在专门用于该事件的特定时间限制内对某些事件做出响应的程序。例如,如果用户在文字处理程序窗口内按下键盘键,它应该相对快速地显示该字符。5 秒的延迟是不可接受的,因此文字处理程序是一种具有某些实时约束的应用程序。
实时系统中的某些部分可能需要优化。然而,一旦时间低于所需的延迟,就无需进行其他优化。(至少在测试证明它再次超出限制之前。)
其他需要优化的程序是那些速度不够快的程序。此类程序的例子包括人工智能程序、游戏、多媒体程序、虚拟机和编程语言解释器,或任何类型的公开可用库或模块,其“野外”使用可能需要它非常快。
当然,另一类此类程序是速度太慢或被认为速度太慢的程序。通常没有内在原因导致它们速度慢,而且往往没有任何东西可以阻止此类程序变得更快。此类被认为速度太慢的程序应该被优化以使其运行得更快。
然而,其他程序本质上是缓慢的。例如,密钥强化算法专门设计为速度缓慢,并且没有已知的捷径。另一个例子是,通过 12 Mbit/s“USB 全速”端口复制 8 GB 数据本质上需要超过 1 小时,这是由于硬件的限制。
经验法则是,如果程序的速度与预期速度相比不够快,那么就应该优化程序。一个常见的误解是,如果程序现在速度不够快,那么随着硬件变得更快,程序的运行速度也会更快。然而,情况并非总是如此。
如果您的程序速度慢,通常是因为代码没有优化(例如,它使用了次优算法)。虽然更好的硬件通常会提高性能,但并不能保证速度会有明显提高。此外,虽然硬件速度的进步使程序员能够编写更浪费的代码(参见Paul Graham 的“百年语言”文章),但有些程序仍然过于缓慢。
在现代环境中编写程序时,应该使其速度足够快。有些人声称,我们在单核心处理器方面已经达到了摩尔定律的终点,我们不能指望单处理器的单线程程序运行速度更快。无论如何,即使程序在高端平台上运行良好,它可能仍然需要在旧的硬件上运行。
我们都见过这样的情况:计算机速度越来越快,但软件运行速度却经常变得更慢,除非硬件升级。 所谓的“盖茨定律”声称,由于各种原因,商业程序的速度每 18 个月下降一半。众所周知,各种版本的 DOS 操作系统在 PC XT 和 286 上运行良好,正如一位记者当时所说,英特尔 386 是一款“精干高效的 DOS 机器”。另一方面,Microsoft Windows 3.0 和 Microsoft Windows 3.1 已经需要一台快速的 486 计算机才能流畅运行,而 Windows 95 在那里几乎无法使用,需要一台奔腾计算机。Windows XP 在奔腾机器上已经运行缓慢,需要高端的奔腾 III 或奔腾 4 计算机。 Windows Vista 需要比 Windows XP 更多的硬件资源,以至于目前许多计算机都无法舒适地运行它。
现在,虽然直接针对 CPU 和内存(可能还有硬盘)运行的软件模拟速度比以前快得多,但系统本身的响应速度似乎并没有明显提高。
如果所讨论的程序运行速度足够快,则不应进行优化。但是,如果它还必须在较慢的硬件上运行良好,处理更大的负载或在资源较少的情况下正常工作,则可能需要根据这些要求进一步优化程序。
令人惊讶的是,分析通常会发现,对特定程序应用特定优化技术会使其变慢。在这种情况下,应恢复到程序的先前“未优化”版本。
优化可能会对模块化和可读性等代码质量因素产生负面影响。例如,考虑 GMP(GNU 多精度) 的开发人员在 他们的手册 中写的内容
GMP 的速度是通过使用全字作为基本算术类型、使用复杂的算法、为许多不同 CPU 的最常见内部循环包含经过精心优化的汇编代码以及普遍强调速度(而不是简单性或优雅)实现的。
还要考虑,短 函数 和 方法 被认为优于较长的方法,以允许更大的代码重用、测试和自文档化。(请参阅 “提取方法”重构模式)。但是,函数或方法调用被认为是相对昂贵的操作,因此这种额外的代码质量可能会使程序变慢。
因此,不难想象,一个希望节省一些额外周期的开发人员会将一个仅使用一次或几次的函数合并到其调用者代码中(或使用 宏 或预处理阶段(如果可能)合并其代码),从而使代码模块化程度降低。
类似地,高度优化的代码也可能变得更加难以阅读。例如,C 预处理器的宏以使使用它们的代码难以阅读而闻名。我听说 一些抱怨 说 OpenSSL 的代码(基于预处理器宏、相同名称的静态函数和大量代码生成)由于这些事实而非常难以理解和遵循。
类似的抱怨不断针对 perl5 的 XS 表达,它是用于在 ANSI C 和其他更低级语言中创建子例程的接口。该 API 的设计尽可能高效和快速,因此难以学习、理解和遵循。