
2.3 软件性能的时空视角
从时空的视角来看,性能是指在完成某项任务时所展示出来的及时性和空间资源的有效性。
性能指标是衡量性能的尺度。从时间的维度来看,性能指标包括响应时间、延时时间等;从空间的维度来看,性能指标包括吞吐量、并发用户数和资源利用率等,此外,还有故障响应时间指标。故障响应时间是指软件从出现故障到确认修复前的时间,该指标通常用于反映服务水平。显然,平均故障响应时间越短,故障对用户系统的影响越小。
2.3.1 系统性能的时间指标
响应时间指系统对用户请求做出响应的时间,与人对软件性能的主观感受一致。它完整地记录了软件处理请求的时间。由于一款软件通常会提供许多功能,不同功能的处理逻辑也千差万别,因此不同功能的响应时间也不尽相同,甚至同一功能在不同环境(如输入的数据不同)下的响应时间也不相同。因此,响应时间通常是指该软件所有功能的平均响应时间或者所有功能中的最大响应时间。当然,有时候也需要针对每个或每组功能讨论其平均响应时间和最大响应时间。
软件的响应时间是我们所关注的关键性能指标,而影响响应时间的主要因素涉及各种各样的延时,下面介绍两种延时的抽象模型。
1.排队延时
负载和响应时间之间的数学关系是众所周知的。一个称为M/M/m的排队模型将响应时间与满足一组特定需求的系统负载联系了起来。M/M/m有一个假设,即系统具有“理论上的完美可伸缩性”。尽管这个假设有些过分,但M/M/m在性能方面还是有很多值得我们学习的地方。在低负载时,响应时间基本上与空负载时的响应时间相同。随着负载的增加,响应时间会逐渐缩短。这种逐渐的退化并没有造成太大的危害,但是随着负载持续上升,响应时间开始以一种不再轻微的渐变方式退化。这种退化令人不爽,而且是呈现双曲线趋势的。
在M/M/m中,响应时间(r)由两个部分组成:服务时间(s)和排队延时(q)。服务时间是任务消耗给定资源的时间,以每个任务执行的时间为单位。排队延时是指任务排队等待使用给定资源的时间。排队延时也可以用每个任务执行的时间来度量,此时指给定任务的响应时间与卸载系统上同一任务的响应时间之间的差异(不要忘记“理论上完美可伸缩性”的假设)。
2.相干延时
相干延时是由于任务的有序性执行造成的时间延迟,无法使用M/M/m排队模型。这是因为M/M/m假设所有m个服务通道都是并行、同构且独立的,这意味着M/M/m假设当某用户在先进先出队列中等待足够长的时间,并且前面排队的所有请求都已经退出服务队列后,才会轮到该用户接收服务。然而,相干延时并不遵循这种工作方式。
假设有一个HTML数据输入表单,其中,Update(更新)按钮用于执行SQL Update语句,Save(保存)按钮用于执行SQL commit(提交)语句,用这种方式构建的软件几乎可以直接看到性能的糟糕程度。这对于希望更新同一行数据的其他任务来说,带来的影响可能是毁灭性的。每个任务都必须等待该行的锁定(在某些系统上,可能是更糟糕的页锁定),直到锁定用户决定继续下面的工作并单击Save按钮,或者直到数据库管理员终止用户的会话。
在这种情况下,任务等待释放锁的时间与系统的繁忙程度无关,而是取决于系统各种资源利用以外的随机因素。这就是为什么永远不能假设在单元测试环境中执行的性能测试能够决定是否将新代码插入生产系统。
2.3.2 软件性能的空间指标
软件性能的空间指标包括吞吐量、系统所支持的用户总数、并发用户数和资源利用率等。吞吐量是指系统在单位时间内处理请求的数量。对于无并发功能的软件而言,吞吐量与响应时间呈严格的反比关系,实际上,此时的吞吐量就是响应时间的倒数。无并发的软件都是单机应用的。对于互联网或者移动互联网上的产品而言,并发用户数是指系统可以同时承载的正常使用软件功能的用户数量。与吞吐量相比,并发用户数是一个更直观但也更笼统的性能指标。资源利用率反映的是在一段时间内资源平均被占用的情况,一个主要的度量指标就是负载,另一个是数据倾斜。
1.负载
许多人不明白,为什么让一个程序变得更有效率会给系统中的其他程序带来性能改进,而这些程序与正在修复的程序没有明显的关系。其实,这是由于负载对系统的影响。
负载是指由并发任务执行引起的资源竞争,这就是性能测试难以捕捉到生产后期出现的所有性能问题的原因。负载的一个度量指标是利用率。
利用率=使用的资源/指定时间间隔内的资源容量
随着资源利用率的提高,用户从该资源请求服务时的响应时间也会增加。这就如同交通非常拥挤时,必然会导致等红绿灯的时间变长。
软件运行环境中的CPU在每个时钟周期内都有固定的指令数量,这就会让软件总是以同样的速度运行,但是响应时间肯定会随着系统资源使用的增加而受到影响。在分布式系统中,数据在不同空间的存储位置同样会对软件的性能产生影响。
2.数据倾斜
假设有x个数据库调用,调用操作占用了y秒的响应时间。如果能消除一半的调用量,那么能消除多少不必要的响应时间呢?答案往往出人意料,几乎从来不是“一半的响应时间”,能消除的响应时间取决于我们可以消除的单个调用的响应时间。不能假设每个调用平均的持续时间为y/x秒,因为语句没有告诉我们调用的持续时间是一致的。
数据倾斜用于表征具体调用中的不一致性。因为存在数据出现倾斜的可能性,所以无法对软件的响应时间提供准确预估。在不了解任何有关数据倾斜信息的情况下,可以提供的答案可能是“响应时间在0~y秒之间”。但是,假设有具体的附加信息,就可以对最佳情况和最差情况进行估算。在数据库应用中,读写分离也只是大粒度分隔数据倾斜的一种方式。
2.3.3 系统性能指标的时空关联
由于时空的内在联系,系统性能的时空指标往往具有较强的相关性。以吞吐量和响应时间这两个重要的指标为例,吞吐量和响应时间通常相互关联,但并不完全相同,真正的关系是微妙而复杂的。
1.并发通信中的吞吐量与响应时间
假设某个基准测试以每秒1000个任务的速度测量了吞吐量,那么用户的平均响应时间是多少呢?人们很容易认为每个任务的平均响应时间是0.001s,但事实并非如此。如果处理这个吞吐量的系统有1000个并行、独立且同质的服务通道,那么每个请求可能正好消耗1s。
现在我们知道每个任务的平均响应时间在0~1s之间。然而,不能仅从吞吐量测量中推导出响应时间,必须单独测量它。当然,有些数学模型可以计算给定吞吐量下的响应时间,但是模型需要更多的输入,而不仅是吞吐量。
2.计算环境中的吞吐量与响应时间
计算环境展示了吞吐量和响应时间之间的微妙关系。如果需要在单个CPU的计算机上编程以实现每秒100个新任务的吞吐量,假设编写的新任务在计算机系统上的执行仅需0.001s,那么是否能够达到所需的吞吐量?如果能够在0.001s内运行一次任务,那么肯定可以在1s内至少运行100次。例如,如果任务请求被很好地序列化,就可以在一个循环中依次处理这100个任务。
然而,如果每秒100个任务随机出现在系统上,即100个不同的用户登录到单个CPU计算机上,那么会发生什么呢?CPU调度器和序列化资源可能会将吞吐量限制在远低于每秒100个任务的范围内,因此不能仅凭对响应时间的度量来推导出吞吐量,还需要进行单独的测量。
响应时间和吞吐量并不一定相反。要了解这两者之间的关系,需要同时测量它们。哪一个更重要呢?对于给定的情况,可以从两个方向上合理地寻找答案。在许多情况下,两者都需要管理的核心性能指标。例如,系统可能有一个业务需求,要求在99%以上的系统响应,给定任务的响应时间必须小于1s,同时系统必须支持在1s的间隔内持续执行1000个任务。
2.3.4 常见的软件性能指标
软件的时空架构决定了软件性能的时空约束。而软件时空属性自身的复杂性与多样性也导致了软件性能指标的丰富性。不同的业务系统、子系统乃至功能模型之间都存在着共同的性能指标,如延时、资源利用率等。同时也存在着各自的差异性指标,如前端系统的最大绘制内容(LCP)、累积布局偏移(CLS)等。常见的部分性能指标及其介绍如表2-1所示。
表2-1 常见的部分性能指标及其介绍

表2-1中涉及的性能指标是一些关键指标,寻找各种软件性能指标的完备集是非常困难的。根据软件业务需求的多样性,会产生许多复合指标。有些性能指标可以直接度量,而有些则只能通过度量指标以间接计算的方式得到。很多时候,大家也把度量指标当作性能指标进行讨论。