API的性能约定

当今,任何软件系统都依赖于其他人的工作,可以参考《没有被了解的API老码农眼中的API世界》。当然,我们写了一些代码,通过API调用操作系统和各种软件包中的函数,从而减少了代码量。随着分布式系统的日益普遍,我们的软件系统通过 络与服务器通信,依赖于 络相关的API函数和服务来实现正确的操作,也依赖于它们的执行性能以使整个系统拥有良好的性能。在涉及分页、 络延迟、资源共享等的复杂系统中,性能必然会有变化。然而,即使是在简单的环境设置中,当一个 API 或操作系统达不到性能预期时,我们的软件也会性能低下。

成本低廉恒定

这类API函数的性能表现是恒定的,例如,isdigit 和toupper, 这两个函数是性能恒定的。Java.util.HashMap.get在正常大小哈希表中的查找应该很快,但是哈希冲突可能会偶尔减慢的访问速度,类似的函数还有很多。

成本通常低廉

许多API函数被设计成大多数时候都很快,但是偶尔需要调用复杂的代码,例如,java.util.HashMap.put 在哈希表中存储一个新条目可能会超出当前表的大小,以至于会整表放大并重新哈希所有条目。

java.util.HashMap 在公开API的性能约定方面是一个很好的例子: “这个实现为基本操作(get 和 put)提供了常量时间性能,假设哈希函数将元素正确地分散存储桶中。对集合视图的迭代需要与 HashMap 的‘容量’成比例的时间… ”

fgetc 的性能取决于底层流的属性。如果是一个磁盘文件,那么该函数通常从用户的内存缓冲区读取,而不需要操作系统调用,但它必须偶尔调用操作系统来读取新的缓冲区。如果是从键盘读取输入,那么实现可能会调用操作系统来读取每个字符。

成本可预测

一些函数的性能随其参数的属性而变化,例如,要排序的数组的大小或要搜索的字符串长度。这些函数通常是数据结构或算法的实用程序,使用众所周知的算法,不需要系统调用。我们通常可以根据对底层算法的期望来判断性能,例如,qsort排序的平均计算复杂度是 nlog n。当使用复杂的数据结构例如 b 树的变体等,在这些地方可能很难确定底层的具体实现,可能更难估计性能。重要的是,可预测性可能只是可能的,例如 regexec 通常是可预测的,但是有一些变态的表达会导致指数时间的爆发。

成本未知

像open、 fseek、 pthread_create、许多“初始化”函数以及任何遍历 络的调用,大多是成本未知的。这些函数的执行成本较高,而且它们的性能常常有很大的差异。它们从池(线程、内存、磁盘、操作系统对象)中分配资源,通常需要对操作系统或I/O 资源的独占访问,常需要大量的初始化工作。通过 络的调用相对于本地访问总是昂贵的,但是成本的差异可能更大,这使得性能模型的形成变得更加困难。

线程库是性能问题的明显标志。Posix 标准花了很多年才稳定下来,并且在实现仍然被各种问题所困扰,基于线程的应用程序可移植性仍然存在风险。线程难以使用的一些原因有: 

(1)与操作系统紧密集成,几乎所有操作系统(包括 Unix 和 Linux)最初设计时都没有考虑到线程; 

(2)与其他库的交互,特别是保证线程安全而导致的性能问题; 

(3)线程的实现不同,表现为轻量级和重量级。

API的性能约定

为什么 API 必须遵守性能约定呢应用程序的主要结构可能取决于 API 是否遵守了这样的性能约定。程序员根据性能期望选择 API、数据结构和整个程序结构。如果预期或性能严重错误,程序员不能仅仅通过调优 API 调用来恢复,而是必须重写程序的主要部分。

实际上, 明确性能约定的程序较难与不遵守性能约定的APi相配合。当然,有许多程序的结构和性能很少受到库性能的影响。然而,如今许多的“常规 业务程序”,特别是基于 web 服务的软件,广泛使用了对整体性能至关重要的库。

即使性能上的微小变化也会导致用户对程序的感知发生重大变化,在处理各种媒体的程序中尤其如此。偶事实上,比起允许帧速率滞后而言,而放弃视频流的帧可能是可以接受的,但是人们可以检测到音频中的轻微中断,因此音频媒体性能的微小变化可能会产生重大影响。这种担忧引起了人们对服务质量概念的极大兴趣,在许多方面,服务质量是为了确保高性能。

尽管违反性能约定的情况较少,而且较少出现灾难性的事故,但在使用软件库时注意性能可以帮助我么生成更健壮的软件。以下是一些关注点和使用策略。

谨慎地选择API和程序结构

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

API 要在新版本和移植发布时提供一致的性能约定

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

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

许多程序员希望计算机及其软件能够一致地随着时间的推移而变得更快。也就是说,希望一个库或一个计算机系统的每个新版本都能平等地提高所有 API 函数的性能,这实际上对于供应商来说是很难保证的。许多用户希望图形库、驱动程序和硬件的新版本能够提高所有图形应用程序的性能,但他们同样热衷于多种功能的改进,这通常会降低旧功能的性能,可能只是轻微地降低。

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

防御式编程

在调用性能未知或高可变的 API 函数时,程序员可以使用特殊的注意事项,异常处理优先。我们可以将初始化移到性能关键区域之外,并尝试预热 API 可能使用的任何缓存数据(例如字体)。对于表现出大量性能差异或拥有大量内部缓存数据的 API 而言, 可以通过提供助手函数将关于如何分配或初始化这些结构的提示从应用程序传递给 API。健康检测可以建立一个可能不可用的服务器列表,从而避免一些长时间的故障暂停。

小结

软件系统依赖于各种独立组件的组合来工作,这意味着它们以可接受的速度执行所需的计算。静态检查是难保证系统的性能的,软件工程实践已经开发出了测试组件和组合的方法,这些方法可以工作得非常好。每次应用程序绑定到动态库或在操作系统接口上时,都需要验证组合的正确性和API的性能约定。

诚然,API的性能约定没有功能正确性约定那么重要,但是软件系统的核心体验往往取决于它。

特别福利

你关注软件系统的架构和性能么一名软件工程师如何拥抱AI 呢让各种各样的数据、算法和模型更高效的服务于目标产品呢高兴地告诉大家一个和老码农一起工作和学习的机会,笔者所在的小度策略架构团队招人了:

策略架构工程师

地点:百度科技园,北京

干啥h4>
  • 负责小度技能开发平台的前后端研发工作

  • 提升算法落地效率, 与产品、算法团队紧密配合, 研究业界先进技术方案,依托平台能力推动算法规模化应用、快速落地

  • 提升平台工程质量, 在高性能、可扩展、高可用、易运维等方向进行平台技术优化

  • 研究业界新技术方案及应用领域,进行产品与技术探索,并推动落地

要求并不高

  • 计算机或相关专业本科以上学历,5年以上开发经验,素质较好可以适度放开

  • 数据结构和算法的基本功扎实,能应用常见的设计模式和架构方法

  • 熟练掌握PHP和Python, 熟悉Linux开发环境, 了解C/C++开发,有MySQL、Redis等数据库开发经验,熟悉 络编程,有完整的后端项目开发经验

  • 掌握HTML/CSS/JavaScript,至少熟悉React/Vue中的一种框架,有前端项目开发经验

  • DevOps意识,了解系统全链路跟踪,具备一定的数据分析能力 

  • 良好的团队合作态度,具有工匠精神,具有较强的沟通能力

如果对自然语言处理或深度学习技术有了解,能快速入职者优先,另,有对话系统或者开放平台研发经验者优先。

工作地点:百度科技园,北京

简历直发我的邮箱:caohongwei01@baidu.com

【关联阅读】

  • 区块链性能提升:链上设计之道

  • 服务器性能监控的温故知新

  • 基于P2P的互联 内容加速

  • 浅谈面向客户端的性能优化

  • 智能音箱场景下的性能优化

  • 面向互联 应用的 络优化

  • 软件系统的多维性能模型

  • IoT产品的10个最佳实践

  • 老曹眼中的MySQL调优

  • 性能,10点系统性思考

  • 从构造函数看线程安全

  • 服务可用性的一知半解

  • 日常生活中的企业监控

  • 冲浪中游泳

文章知识点与官方知识档案匹配,可进一步学习相关知识算法技能树首页概览34723 人正在系统学习中

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年8月13日
下一篇 2021年8月13日

相关推荐