确保API 性能的经验性方法

程序员根据对 API 性能的期望选择 API、数据结构和整个程序结构。如果预期或性能严重错误,程序员不能仅仅通过调优 API 调用来恢复,而必须重写程序(可能是主要部分)。前面提到的交互式程序的防御结构是另一个例子。

当然,有许多程序的结构和性能很少受到库性能的影响(科学计算和大型模拟通常属于这一类)。然而,今天的许多“常规 IT” ,特别是遍及基于 web 的服务的软件,广泛使用了对整体性能至关重要的库。

即使性能上的微小变化也会导致用户对程序的感知发生重大变化,在处理各种媒体的节目中尤其如此。偶尔放弃视频流的帧可能是可接受的 ,但是用户可以感知到音频中哪怕是轻微的中断,因此音频媒体性能的微小变化可能会对整个节目的可接受性产生重大影响。这种担忧引起了人们对服务质量的极大兴趣,在许多方面,服务质量是为了确保高水平的业绩。

尽管违反性能契约的情况很少,而且很少是灾难性的,但在使用软件库时注意性能可以帮助构建更健壮的软件。以下是一些经验性原则:

1 谨慎地选择API 和程序结构

如果有幸从头开始编写一个程序,那么在开始编写程序时,要考虑一下性能约定的含义。如果这个程序一开始只是一个原型,然后在服务中保持一段时间,那么毫无疑问它至少会被重写一次; 重写是一个重新思考 API 和结构选择的机会。

2 在新版本发布时提供一致的性能约定

一个新的实验性 API 会吸引那些开始衍生 API 性能模型的用户。此后,更改性能约定肯定会激怒开发人员,并可能导致他们重写自己的程序。一旦 API 成熟,性能约定不变就很重要了。事实上,大多数通用 API (例如 libc)之所以变得如此,部分原因在于它们的性能约定在 API 发展过程中是稳定的。同样的道理也适用于 api 端口

人们可能希望 API 提供者能够定期测试新版本,以验证它们没有引入性能怪癖。不幸的是,这样的测试很少进行。但是,这并不意味着不能对依赖的 API 部分进行自己的测试。使用分析器,通常可以发现程序依赖于少量的API。编写一个性能测试工具,将一个API的新版本与早期版本的记录性能进行比较,这样可以给程序员提供一个早期预警警,即随着API新版本的发布,他们自己代码的性能将发生变化。

许多程序员希望计算机及其软件能够一致地随着时间的推移而变得更快。这实际上对于供应商来说是很难保证的,但是它们会让客户相信是这样的。许多程序员希望图形库、驱动程序和硬件的新版本能够提高所有图形应用程序的性能,并热衷于多种功能的改进,这通常会降低旧功能的性能,哪怕只是轻微的降低。

我们还可以希望 API 规范将性能约定明确化,这样使用、修改或移植代码的人就可以遵守约定。注意,函数对动态内存分配的使用,无论是隐式的还是自动的,都应该是API文档的一部分。

3 防御性编程可以提供帮助

在调用性能未知或高度可变的 API 时,程序员可以使用特殊的方式,对于考虑故障性能的情况尤其如此。我们可以将初始化移到性能关键区域之外,并尝试预加载API 可能使用的任何缓存数据(例如字体)。表现出大量性能差异或拥有大量内部缓存数据的 API ,可以通过提供帮助函数将关于如何分配或初始化这些结构的提示从应用程序传递给 API。某个程序偶尔会向服务器发出 ping 信号,这可以建立一个可能不可用的服务器列表,从而避免一些长时间的故障暂停。

4 API 公开的参数调优

有些库提供了影响性能的明确方法(例如,控制分配给文件的缓冲区的大小、表的初始大小或缓存的大小),操作系统还提供了调优选项。调整这些参数可以在性能约定的范围内提高性能,调优不能解决总体问题,但可以减少嵌入在库中的固定选项,这些选项会严重影响性能。

有些库提供了具有相同语义函数的替代实现,通常是通用API的具体实现形式。通过选择最好的具体实现进行调优通常非常容易,Java 集合就是这种结构的一个很好的例子。

越来越多的API被设计成动态地适应应用,使程序员无需选择最佳的参数设置。如果一个散列表太满,它会自动扩展和重新哈希(这是一种优点,但偶尔会降低性能)。如果文件是按顺序读取的,那么可以分配更多的缓冲区,以便在更大的块中读取。

5 测量性能以验证假设

常见方式是检测关键数据结构,以确定每个结构是否正确使用。例如,可以测量哈希表的完整程度或发生哈希冲突的频率。或者,可以验证一个以写性能为代价的快速读取结构实际上被读取的次数多于被写入的次数。

所有这些都不是为了阻止完美主义者开发自动化仪表盘和测量的工具,或者开发详细说明性能约定的方法,以便性能测量能够建立对性能约定的遵守。这些目标并不容易实现,回报可能也不会很大。

通常可以在不事先检测软件的情况下进行性能度量,优点是在出现需要跟踪的问题之前不需要任何工作还可以帮助诊断当修改代码或库影响性能时出现的问题。定期进行概要分析,从可信赖的基础上衡量性能偏差。

6 使用日志检测和记录异常

当分布式服务组成一个复杂的系统时,会出现越来越多的违反性能约定的行为。注意,通过网络接口提供的服务有时具有指定可接受性能的SLA。在许多配置中,度量过程偶尔会发出服务请求,以检查 SLA 是否满足 。由于这些服务使用类似于 API 调用的方法(例如,远程过程调用或其变体,如 XML-RPC、 SOAP 或 REST),因此可能是有性能约定的期望。应用程序会检测这些服务的失败,并且通常会应对得当。

然而,响应缓慢,特别是当有许多这样的服务互相依赖时,可能会非常快地破坏系统性能。如果这些服务的客户能够记录他们所期望的性能,并生成有助于诊断问题的日志(这就是 syslog 的用途之一) ,那将会很有帮助。当文件备份看起来不合理的慢,那是不是比昨天慢呢?比最新的操作系统软件更新之前还要慢?考虑到多台计算机可能共享的备份设备,它是否比预期的要慢?或者是否有一些合理的解释(例如,备份系统发现一个损坏的数据结构并开始一个长的过程来重新构建它) ?

在没有源代码,也没有构成组合的模块和API的细节的情况下,诊断不透明软件组合中的性能问题可以在报告性能和发现问题方面发挥作用。虽然不能在软件内部解决性能问题 ,但是可以对操作系统和网络进行调整或修复。如果备份设备由于磁盘几乎已满而速度较慢,那么肯定可以添加更多的磁盘空间。好的日志和相关的工具会有所帮助; 遗憾的是,日志在计算机系统演进中可能是一个被低估和忽视的领域。

诚然,性能约定没有功能正确性约定那么重要,但是软件系统的重要用户体验几乎都取决于它。

猜你喜欢

转载自blog.csdn.net/Jernnifer_mao/article/details/132556614