性能,10点系统性思考

开发 开发工具
曾经企图创建一种公理化的方法来优化计算机软件性能,然而能力所限,惭愧之至。退而求其次,希望能够清楚地系统思考如何优化计算机软件的性能。

 作为一个半吊子全栈工匠,在20多年的职业生涯里遇到过太多关于软件性能的问题。论证或者证明性能的问题往往很关键,能否通过一次一个小而有逻辑的可证明可审核的步骤来解决性能问题呢?

 

曾经企图创建一种公理化的方法来优化计算机软件性能,然而能力所限,惭愧之至。退而求其次,希望能够清楚地系统思考如何优化计算机软件的性能。

[[316994]]

1. 什么是性能?明确概念

性能——performance,有着太多概念外延,在生活中几乎随时可见,例如,职场人的performance就是中文里的绩效,performance review 就是每人都会面对的绩效考核。但是,如果在互联网上百度一下,大多数有关性能的热门文章是关于: 计算机软件执行任何您指定任务所需的时间。

如果把面向对象作为开始,那什么是任务呢?任务,基本上是一个面向业务的工作单元,任务可以嵌套。对于计算机用户来说,性能通常意味着系统执行某项任务所需的时间。响应时间是任务的执行持续时间,以每个任务的时间为单位,例如,在百度上搜索“性能” 的响应时间为0.2秒左右,在浏览器中可以有办法看到这个测量结果,这就是网页搜索的一个性能证据。

 

由于感受软件性能的主体是人,不同的人对于同样的软件能有不同的主观感受,而且不同的人对于软件性能关心的视角也不同。有些人眼中的性能是吞吐量,即在指定时间间隔内完成的任务执行数量,例如“每秒点击次数” 。一般来说,负责团队性能的人更担心吞吐量,因为他们要关心该系统是否能够处理所有用户需要要处理的所有数据。

那什么是性能呢?时空可能是连续的,从时空的视角看,性能是完成某项任务时所展示出来的时间及时性和空间资源有效性。对用户而言,更关注及时性,对服务或者产品提供者而言,既关注时间又关注空间,是多种因素的权衡。

2 性能指标——时空纠缠

性能的指标,是指衡量性能的尺度。从时间的维度看,包括响应时间、延迟时间等,从空间的维度看,包括吞吐量,并发用户数和资源利用率等。

由于时空的内在联系,以两个重要的指标为例,吞吐量和响应时间通常相互关联,但并不完全相同,真正的关系是微妙而复杂的。 

 

通信中的吞吐量与响应时间

假设为某个基准测试以每秒1000个任务的速度度量了吞吐量。那么,用户的平均响应时间是多少呢?人们很容易认为每个任务的平均响应时间是0.001秒,但事实并非如此。如果处理这个吞吐量的系统是有1000个并行的、独立的、同质的服务通道,在这种情况下,每个请求可能正好消耗1秒。

现在,可以知道每个任务的平均响应时间在0到1秒之间。然而,不能仅仅从吞吐量测量中推导出响应时间,必须单独测量它。当然,有数学模型可以计算给定吞吐量的响应时间,但是模型需要更多的输入,而不仅仅是吞吐量。 

 

计算中的吞吐量与响应时间

在另一个方向上,展露了微妙之处。如果需要在单CPU计算机上编程以提供每秒100个新任务的吞吐量,假设编写的新任务在计算机系统上执行仅用0.001秒,那么是否能产生所需的吞吐量?如果能在千分之一秒内运行一次任务,那么肯定能在一整秒内至少运行100次。例如,任务请求被很好地序列化,就可以在一个循环中处理所有100个任务,一个接着一个地循序执行。

但是,如果每秒100个任务随机地出现在系统上,从100个不同的用户登录到单 CPU 计算机上,又会怎样呢?CPU 调度器和序列化资源可能会将吞吐量限制在远低于每秒100个的任务数量,从而不能完全从响应时间度量推导出吞吐量,需要单独测量。

响应时间和吞吐量不一定是相反的。要了解这两者,需要同时测量它们。哪一个更重要呢?对于给定的情况,可以从两个方向上合理地寻找答案。在许多情况下,答案是两者都是需要管理的重要指标。例如,系统可能有一个业务需求,不仅要求在99%以上的系统响应中,对给定任务的响应时间必须小于1秒,而且系统必须支持在1秒间隔内持续执行1,000个任务的吞吐量。

 

 

3. 描述性能:一切结果,都是概率

“在99%以上的系统响应”,是一种响应时间的期望限定,一些人更习惯于用“平均响应时间必须是 x 秒”来描述。不过,说明目标的百分比方法更好地体现在人们经验中。

想象一下,对于每天在电脑上执行的某项任务,响应时间容忍度可能是1秒。假设,a系统90% 的平均响应时间是1秒,b系统60% 的平均响应时间是1秒,那么a系统会有10% 的用户不满意而b系统有40% 用户不满意吗?如果 a 系统中,90% 的响应时间是0.91秒; 在 b系统 中,90%的响应时间是1.07秒,那么, 这样的描述比仅仅说1.00秒的平均响应时间更有信息量。

 

 

我们尝试用可能的两个数来描述世界,一个是均值,一个是方差。客户感受到的可能是方差,而不是均值。将响应时间表示为百分数,可以产生与最终用户期望相符的性能描述,而且令人信服, 例如,”动态库加载”的任务必须在至少99.99% 的执行中在小于0.5秒的时间内完成。

我们同样用概率来描述性能,或许,一切的抽象,可能都归于数学,一切的结果,可能都归于概率。

4 问题诊断——以终为始

在曾经遇到的性能问题中,大多数是关于响应时间的: “过去做某事只需要不到一秒的时间,现在有时候需要10多秒。” 当然,一个更朴实的说法是,“整个系统太慢了,简直不能使用。”

关于性能问题的诊断,最重要的事情是清楚地陈述问题,明确了问题的描述,才能清楚地思考问题。

 

 

以终为始,系统想要达到的目标状态是什么呢?找出一些可以用来表达目标状态的细节数据: 例如,“在许多情况下,系统的响应时间不超过2秒。如果至少有95% 的关键任务响应应时间在一秒以内,这才是我们所要的。”

这样的描述看起来不错,但是——

如果用户没有这样一个定量目标呢?

这个特定的目标有两个量(1s和95%) , 如果不知道其中的某一个该怎么办呢?

更糟糕的是,如果用户确实有特定的想法,但是这些期望是不可能实现的,又该怎么办呢?

如何怎么知道什么是“可能的”或“不可能的” ?......

性能的问题诊断从问题的描述, 以终为始,循序逆推,接下来才是使用工具来应对这些问题。

 

 

时间利器——时序图

时序图是 UML中指定的一种图形,用于按照交互发生的顺序显示对象之间的交互。在可视化响应时间方面,时序图是一个非常有用的工具。

考虑一下绘制时序图的比例,每个进入的“请求”箭头和相应的“响应”箭头之间的距离与服务请求所花费的时间成正比,可以说明图中表示的组件是如何花费时间的,可以“感觉”到响应时间的相对贡献。

时序图可以帮助人们概念化响应时间在给定的系统中是如何被消耗的,还可以很好地显示同步处理线程是如何并行工作的,除了分析业务,也是性能分析的好工具。但要系统性思考性能,还需要一些其他的东西。假设,要修复任务的响应时间为2048秒,在这段时间内,运行该任务将导致应用程序服务器执行了320,000个数据库调用。图3显示了这个任务的时序图。

在应用程序和数据库层之间有太多的请求和响应箭头,以至于看不到任何细节。也就是说, 在一个很长的滚动条上打印时序图并不是一个有用的解决方案。

时序图是一个很好的工具来概念化控制流和相应的时间流,可以作为时间上的利刃,那么有空间利刃么?

 

 

空间分析——组件描述直方图

为了处理那些需要大量调用的任务,需要一个方便的时序集合,这样就能理解时间如何花费的重要模式。概要描述是响应时间的表格分解,通常按组件响应时间贡献降序列出。

直方图一般可以确切地显示慢速任务在哪里消耗了时间。例如,可以推导出概要描述中标识的每个函数,以及函数调用响应时间所占的百分比,还可以推导出任务期间每种类型的函数调用的平均响应时间。

如果可以深入到聚合为单个调用中持续时间,就可以知道有多少这些调用对应于某个函数的其他调用,并且可以知道每个调用消耗了多少响应时间。“这个任务应该运行多长时间? ” 使用组件描述直方图,可以构造问题的答案。

老码农认为,这是问题诊断的第一个重要问题,这是解决性能问题的开端。

5 优化原则——要事优先?

性能改进与程序使用所改进东西的程度成正比。如果正在尝试改进的事情只占任务总响应时间的5% ,那么能够产生的最大影响也紧紧是总响应时间的5% 。这意味着,我们越将焦点集中在直方图的顶部(假设组件直方图按响应时间降序排列) ,整体响应时间的潜在好处就越大。

但是,这并不意味着总是按照自上而下的顺序处理组件的响应,还需要考虑执行补救措施的成本。考虑组件的响应时间直方图,添加最佳补救方法可以节省多少时间,可以看到每个补救方法的实现成本。

 

 

确立优化起点

那么,先采取什么补救措施?成本核算,寻找更好的净收益,这才是真正需要的优化点。

带有改进成本的组件响应时间直方图打开了一扇大门,让我们可以就首先实施哪些补救措施做出更好的决定,为预测改进后的性能指标提供了一个尺度。进一步,可以找到比预期更有效的方法,以低于预期的成本缩短响应时间。

首先采取什么补救措施取决于对成本估算的信任程度。“非常便宜”是否真的考虑到了所提议的改进可能对系统造成的风险呢?例如,改变这个参数或者删除那个索引看起来非常经济,但是这个改变是否有潜在的破坏性?改变了一些现在甚至没有想到的组件的良好性能呢?可靠的成本估算是技术能力得到体现的另一个领域。

 

 

循序渐进中的信誉

另一个值得考虑的因素是可以通过创造小的胜利来获得的信誉。也许低成本、低风险的改进不会带来总体响应时间的改进,但是它建立一个小改进的跟踪记录,完全符合对于为缓慢的任务节省多少响应时间的预测,也是有价值的。在软件性能领域,预测和最终实现的跟踪记录能够带来必要的可信度,以影响我们的同事甚至经理、客户等等,他们会支持你采取越来越昂贵的补救措施,为企业带来更大的回报。

需要注意的是,当提出更大而昂贵、高风险的补救方案且获得支持时,要小心谨慎。信誉是脆弱的,建立很难,但推倒只需要一瞬。

 

 

减少相干风险

在实践中,常常会出现修复一个任务的性能后,结果损害了另一个任务的性能。那么,在性能优化的时候,应该注意些什么呢?

这里,可以类比一个这样的问题:“为了感觉凉快,是该打开窗子还是脱掉厚衣服呢?”

这就是性能优化的最小化风险原则,确保自己本地的东西是有秩序的,尽量缩小故障域的范围。如果除了使用一两个程序之外,所有程序都处理得很好,那么最安全的解决方案就是将范围本地化在这一两个程序的修改上。

6 性能中的时空因素

在具体的性能优化过程中,会遇到各种各样的情况,常见要素包括数据倾斜、执行效率、负载和延迟。

 

 

数据倾斜

当处理处理组件响应时间直方图的时候,可能反复遇到这样的问题: x个数据库调用占用了y秒的响应时间。如果能消除一半的调用,能消除多少不必要的响应时间呢?答案往往出人意料,几乎从来不是“一半的响应时间”, 取决于我们可以消除的单个调用的响应时间。不能假设每个调用的持续时间是平均y/x秒,语句没有告诉我们调用持续时间是一致的。

数据倾斜是具体调用中的不一致性,出现倾斜的可能性使得无法对组件响应时间提供准确的答案。在不了解任何有关数据倾斜信息的条件下,可以提供的答案是,“在0到y秒之间的某个位置。但是,假设有具体的附加信息。就可以制定出更精确的最佳情况和最差情况估计。在数据库应用中,读写分离也只是大粒度分隔数据倾斜的一种方式。

 

 

运行效率

即使整个系统中的每个人都很痛苦,仍然应该首先关注业务需要修复的程序。起点是确保程序尽可能高效地工作。在不增加容量和不牺牲业务功能的情况下, 效率与可消除多少任务执行的总服务时间成反比。换句话说,效率与浪费成反比。

以下是数据库应用程序中经常出现的2个有关浪费例子:

中间层程序为每一行数据库插入创建了一个独立的 SQL 语句。它执行了1000个数据库prepare调用也就是1000个网络IO调用 ,而本可以通过一个调用从而减少999个网络IO调用来完成这项工作。

一条 SQL 语句涉及了数据库缓冲上万次,以返回一个几百行的结果集。而一个额外的过滤语句可以返回终端用户真正想要看到的6行,只对数据库缓冲区访问进行几十次次触摸。

当然,如果一个系统存在某些全局性问题,例如,考虑不周的索引、设置糟糕的参数、配置糟糕的硬件等等,会导致整个系统的大量任务效率低下,那么应该修复它。但是,不要为了适应效率低下的程序而调整系统,不要用权宜之计作为永久的解决方案。

解决效率低下的问题往往在解决程序本身效率低下的问题上。即使某些程序是商业化的现成应用程序,从长远来看,要与软件供应商合作使程序更有效,而不是试图优化系统,使其尽可能高效地处理固有的低效率程序。

使程序更高效可以为系统中的每个人带来巨大的好处,很容易看出减少浪费是如何帮助修复任务的响应时间的。

 

 

工作负载

许多人也不明白的是,让一个程序变得更有效率,会给系统中其他程序带来性能改进,而这些程序与正在修复的程序没有明显的关系。这是由于负载对系统的影响。

负载是由并发任务执行引起的资源竞争。这就是为什么我们的性能测试不能捕捉到生产后期出现的所有性能问题的原因。

负载的一个度量是利用率,即资源使用除以指定时间间隔内的资源容量。随着资源利用率的提高,用户从该资源请求服务时的响应时间也会增加。任何一个在高峰时间在北京开过车的人都经历过这种现象,当交通非常拥挤时,必须在红绿灯等候更长的时间。

软件慢下来和汽车是不一样的,汽车在繁忙的交通中时速30英里而在开阔的道路上时速60英里。由于CPU的每个时钟周期有固定的指令数量,计算机软件总是以同样的速度运行,但是响应时间肯定会随着系统资源的使用增加而减少。

还是时空的纠缠,随着负载的增加,系统变慢的原因有两个: 排队延迟和一致性延迟。

 

 

排队延迟

负载和响应时间之间的数学关系是众所周知的。一个称为 M/M/m 的排队模型将响应时间与满足一组特定需求的系统负载联系了起来。M/M/m 有一个假设,即系统具有“理论上完美的可伸缩性” ,尽管有一些过分,但 M/M/m 模型在性能方面还是有很多值得我们学习的地方。下图显示了m=8时该模型的响应时间和负载之间的关系。

【本文来自51CTO专栏作者“老曹”的原创文章,作者微信公众号:喔家ArchiSelf,id:wrieless-com】

 

戳这里,看该作者更多好文

 

责任编辑:武晓燕 来源: 51CTO专栏
相关推荐

2009-09-29 10:39:04

Linuxlinux系统性能检测

2010-04-23 11:44:34

Aix系统

2015-10-22 10:26:21

更新Build 10565Windows 10

2013-03-20 17:18:07

Linux系统性能调优

2011-03-18 11:13:07

LAMP度量性能

2011-01-05 13:48:55

Linux提高性能

2010-05-24 13:29:30

Swap空间

2013-03-06 10:24:12

ksar工具系统性能

2013-02-28 13:37:59

系统性能调优技术实战

2018-01-22 09:08:14

存储系统性能带宽

2010-04-09 13:26:44

2022-07-26 10:28:00

Linux监控命令

2017-08-11 19:13:01

LinuxNmon系统监控工具

2011-03-10 14:40:52

2009-07-14 16:28:34

光纤测试性能布线

2010-03-03 10:38:59

2013-03-12 17:33:17

Linux系统性能调优

2021-07-15 08:00:47

系统性能调优cpunuma架构

2015-12-17 14:32:46

NmonLinux性能

2022-03-23 08:45:20

系统性能CPU
点赞
收藏

51CTO技术栈公众号