正如我前面所说,与第三方程序(如数据库、缓存、Web 服务或 LDAP 服务器)通信的应用程序可能会在这些应用程序运行缓慢时放慢速度。在应用程序端使用常规代码分析
方法可以进行跟踪。但是,如果第三方软件自己工作正常,那么罪魁祸首可能是网络。
问题可能是配置错误的集线器,低带宽网络链路,或甚至是大量的使计算机多次发送
相同的数据包的流量冲突。
这里有几个元素,你应该收集一下。要了解发生了什么,首先要研究以下 3 个领域:
• 使用以下工具观察网络流量。
○ ntop:http://www.ntop.org(仅限 Linux)。
○ wireshark:www.wireshark.org(以前命名为 Ethereal)。
• 使用 net-snmp 跟踪不正常或错误配置的设备(http://www.net-snmp.org)。
• 使用统计工具 Pathrate 来估量两台计算机之间的带宽。参见 http://www.cc.gatech.
edu/~dovrolis/bw-est/pathrate.html。
如果你想进一步了解网络性能问题,你可能还需要阅读 Richard Blum 的 Network
Performance Open Source Toolkit。本书公开了调整大量使用网络的应用程序的策略,并提供
了一个扫描复杂网络问题的教程。
Jeremy Zawodny 所著的 High Preformance MySQL 也是一本很好的书,在编写使用 MySQL
的应用程序时值得一读。
小结
在本章中,我们已经看到。
• 3 个优化规则如下。
○ 首先要能工作。
○ 从用户的角度考虑。
○ 保持代码的可读性。
• 基于编写具有速度目标场景的优化策略。
• 如何分析 CPU 或内存使用情况和一些网络分析提示。
现在,你已经知道如何找到性能问题,下一章将介绍一些常见的解决性能问题的通用策略。
优化——一些强大的技术
优化程序不是一个神奇的过程。遵循一个简单的算法就可以完成,这个算法由 Stefan
Schwarzer 在 Europython 2006 上综合给出,他的原始伪代码示例如下:
def optimize():
"""推荐的优化过程"""
assert got_architecture_right(), "fix architecture"
assert made_code_work(bugs=None), "fix bugs"
while code_is_too_slow():
wbn = find_worst_bottleneck(just_guess=False,
profile=True)
is_faster = try_to_optimize(wbn,
run_unit_tests=True,
new_bugs=None)
if not is_faster:
undo_last_code_change()
# By Stefan Schwarzer, Europython 2006
这个例子可能不是最整洁,最清晰的,但是它几乎抓住了一个有组织的优化过程的所
有的重要方面。从中我们可以学到的主要内容如下。
• 优化是一个迭代过程,在这个过程中,并不是每次迭代都会有更好的结果。
• 主要先决条件是通过测试验证并且正常工作的代码。
• 你应该始终专注于优化当前的应用程序的瓶颈。
让你的代码工作地更快不是一个容易的任务。如果是抽象的数学问题,解决方案当然在
于选择正确的算法和适当的数据结构。但在这种情况下,很难提供一些通用的提示和技巧,
可以在任何代码中用于解决算法问题。当然有一些通用的方法来设计一个新的算法,甚至是
可以应用于各种各样的问题的元启发式算法,但它们是语言无关的,因此不在本书的范围。
总之,一些性能问题只是由某些有质量缺陷的代码或应用程序的使用上下文引起的。
例如,以下问题可能会降低应用程序的运行速度。
• 基本内置类型的使用不当。
• 太复杂。
• 硬件资源使用模式与执行环境不匹配。
• 过于长时间的等待来自第三方 API 或后台服务的响应。
• 在应用程序的时间关键部分做太多。
更常见的是,解决这样的性能问题不需要高级的学术知识,只要有良好的软件技能即
可。技能的关键在于知道何时使用合适的工具进行处理。幸运的是,已经有一些著名的模
式和解决方案来处理性能问题。
在本章中,我们将讨论一些常用的和可重用的解决方案,你可以通过以下非算法方法
优化你的程序。
• 降低复杂度。
• 架构体系的权衡。
• 缓存。
降低复杂度
在我们进一步深入学习优化技术之前,让我们准确地定义我们要处理的问题。从本章
的介绍中,我们知道专注于改善应用程序的瓶颈对于成功优化至关重要。瓶颈是严重限制
程序或计算机系统处理能力的单个组件。每个有性能问题的代码都有一个重要特点,就是
它通常只有一个瓶颈。我们在前一章讨论了一些分析技术,因此你应该已经熟悉定位和隔
离这些地方所需的工具。如果你的分析结果显示有几个地方需要立即改进,那么你应该首
先尝试将每个一个地方作为单独的组件,并且进行独立优化。
当然,如果没有明显的瓶颈,但是你的应用程序的运行情况仍然低于你的预期,那么
这你来说真的很不利。优化过程的收益与优化瓶颈的性能影响成正比。有些小组件对总体
执行时间或资源消耗没有实质性的影响,分析并优化这些小组件,需要花费大量的时间,
而收益却很小。如果你的应用程序似乎没有真正的瓶颈,有可能是你错过了一些东西。尝
试使用不同的分析策略或工具,或从不同的角度(内存、I/O 操作或网络吞吐量)查看它。
如果这仍然没有帮助,那你应该考虑调整你的软件架构。
但是,如果你已经成功地找到一个单一的整体组件,它限制了你的应用程序的性能,
那么你真的很幸运。很有可能只需要改进极少的代码,你将能够真正地改善代码的执行时
间并且(或者)资源使用。并且优化的增益将再次与瓶颈的大小成比例。
当尝试提高应用程序性能时,第一个也是最明显的方面就是复杂度。有很多关于程序
复杂性的定义以及很多的表达方式。一些复杂度指标可以提供代码行为的客观信息,并且sqc 这样的信息有时可以用来推测性能的预期。有经验的程序员甚至可以根据不同实现的
复杂性和实际的执行上下文,可靠地猜测它们在实践中的运行情况。
定义应用程序复杂度的两种常用的方式是:
• 循环复杂度(Cyclomatic complexity),往往与应用程序的性能相关。
• 朗道记法(Landau notation),也称为大 O 记法,在客观判断性能中,这是一种非常
有用的算法分类方法。
从那里开始,优化过程有时可以被理解为降低复杂度的过程。本节通过简化循环提供
了简单的技巧。但首先,让我们学习如何测量复杂度。