You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 04 | 性能工程三定律IT业和性能优化工作的“法律法规”
你好,我是庄振运。
在开篇的几讲里,我谈了性能工程的重要性以及所需要的知识面,接下来我们就正式地进入相关的学习。
不过不要着急,第一个模块我们并不会直接进入性能问题的现场,一上来就去解决问题,而是要先耐下心来,学习一些必备的基础知识。为什么呢?因为学习任何事情,打好坚实的基础是至关重要的。
古人云:“合抱之木,生于毫末;九层之台,起于累土。”
所以接下来的几讲,我们需要先学习一些基础知识,包括和性能工程相关的几个重要定律法则和数理基础。这一讲我先和你探讨三个定律法则:帕累托法则、阿姆达尔定律和利特尔法则。
## 帕累托法则
我想你可能知道帕累托法则,它也被称为 80/20 法则、关键少数法则,或者八二法则。
这个法则是基于我们生活中的认识产生的人们在生活中发现很多变量的分布是不均匀的——在很多场景下大约20%的因素操控着80%的局面。也就是说所有的变量中比较重要的只有20%,是所谓的“关键少数”。剩下的多数,却没有那么重要。
举例来讲在企业销售中根据帕累托法则大约“80的销售额来自20的客户”。认识到这一点对企业管理至关重要比如需要重视大客户的关系。
虽然帕累托法则在生活中很多方面都适用,但我们今天的重点是来看看**帕累托法则是怎么应用到我们IT界**的,尤其是怎么指导我们的代码开发和性能优化相关的领域的。
我总结了一下,这个法则可以有以下几个领域的指导。
![](https://static001.geekbang.org/resource/image/9d/1d/9d99b547cbf2074144203f2ec2806c1d.png)
**1.应用程序的使用**
一个应用程序往往可以提供很多功能。但是如果我们统计用户的使用情况会经常发现大约80%的用户使用集中在20%的程序功能。
所以,对我们开发程序的指导意义是,要花足够心思在最常用的少数功能模块上,对它的设计和实现都要充分地优化。
**2.程序代码开发时间的分配**
一个程序的开发过程中大约80%的代码开发只占用了20%的总体开发时间。一般来讲,一个应用程序开发时,一开始往往花不了多少时间就可以快速地搭建一个大体可以工作的原型。反而是后面的剩余代码和代码改进需要更多的时间。
也就是说80%的开发时间往往用在最精髓的20%代码上。帕累托法则对我们的指导意义是,对代码开发工作量的安排上,需要合理有序地规划好开发时间。
**3.程序代码的维护**
如果观察代码的维护和改进历史你经常会发现少数代码被不断地改动甚至经常被改得面目全非而多数代码几乎从第一次写完后就一成不变了。按照帕累托法则80%的代码演进和改动发生在大约20%的代码上。
对程序维护而言我们需要对这20%的代码尽量熟悉,这样才能对其他程序员的代码改动了如指掌,一目了然。
**4.程序代码的修正和纠错**
统计数字也表明所有的代码错误中差不多有80%的错误发生在大约20%的代码上。
所以根据帕累托法则代码修复和纠错时要重点修复最容易产生Bug的代码这样才能把保证整个应用程序的合理质量。
**5.客户流量的时间分布**
估计80的流量将在总时间段的特定20内发生。比如一个商业软件客户的流量峰值往往是上班时间开始的那几个小时比如上午9点到12点
所以,我们在应用程序设计和部署时,要充分考虑帕累托法则带来的影响,尤其是客户访问的峰值时段和空闲时段。
**6.程序代码的优化**
一个程序完成后必然会去运行如果我们统计代码的运行时间往往会发现程序的80%的时间是在运行大约20%的代码。也就是说,只有少数代码非常频繁地被调用。
所以,如果我们想提高程序的性能,最好找出这些少数代码,并做重点优化,这样就可以用很少的改动大幅度地提升整个程序和系统的性能。
那么现在,我们就从性能优化的角度,来看看如何参照帕累托法则,来规划我们性能优化工作的投入和产出。
假设我们的性能优化投入永远是按照代码的优先级来投入的也就是说总是要先优化最值得优化的代码。那么我们看到只要投入差不多20%的努力就能产出80%的性能优化产出,获得最大的投入产出比。
下图是一个根据帕累托法则来投入努力和产出效果的过程。
![](https://static001.geekbang.org/resource/image/bc/11/bc49ec2e4107878d6069c84252e92311.png)
假如先解决20%的最重要的问题就可以达到总体效果的80%。以后再花费80%的努力也只能解决剩下的20%的问题。
在应用帕累托法则的时候需要注意的是里面的80%或者20%都是大约数字,实际的场景千差万别,不可能是恰好这两个数字。这个法则的精髓是,我们的生活和自然界万物的分布不是均匀的,总有些因素比其他因素更重要。
## 阿姆达尔定律
接下来我们来看第二个定律。这个定律你可能会感觉有点陌生。
**阿姆达尔定律**Amdahls law / Amdahls argument是计算机科学界非常重要的一个定律和法则。它本来用于衡量处理器进行并行处理时总体性能的提升度。但其实阿姆达尔定律可以用在很多方面为了方便你理解我们就从一个简单的生活例子开始。
我们用洗衣服和晾衣服来举例。这里假设我们不用洗衣机而是用传统的方式先洗再晾。再假设洗衣服和晾衣服各需要10分钟那么整个过程进行完需要20分钟。
![](https://static001.geekbang.org/resource/image/21/f2/21936de65dad5d3821ae6312799529f2.png)
如果我们对晾衣服的过程进行优化从10分钟缩短到5分钟相当于进行了两倍优化。现在整个过程需要多长时间呢需要15分钟因为洗衣服的模块还是需要10分钟。
![](https://static001.geekbang.org/resource/image/42/7a/420a4b5cecbf15d13d4c7fc83211947a.png)
在这个基础上我们继续对晾衣服模块进行优化速度提升5倍从10分钟缩短到2分钟。整个过程现在需要12分钟完成。
![](https://static001.geekbang.org/resource/image/73/18/73e4fa8f29685a1dc120cf66d1a95218.png)
在这个基础上继续进行类推我们就会发现无论对晾衣服模块进行多大的优化整个洗衣服、晾衣服的过程所需的时间不会小于10分钟也就是整体加速比不会超过2。
根据阿姆达尔定律描述科学计算中用多处理器进行并行加速时总体程序受限于程序所需的串行时间百分比。譬如说一个程序50%是串行的其他一半可以并行那么最大的加速比就是2。无论用多少处理器并行这个加速比不可能提高到大于2。
所以在这种情况下,改进程序本身的串行算法可能比用多核处理器并行更有效。
用公式来讲假设一个系统的整体运行时间是1其中要进行优化加速的模块运行用时是P。如果对这个模块的加速比是N那么新系统的处理时间可以用下面的公式来表示。
![](https://static001.geekbang.org/resource/image/5e/af/5e654721a7dc896068afe5dc653b94af.png)
这里面1-P是未被加速的其他模块运行时间而N分之P是优化后的模块运行时间。它们的和就是新系统的总体运行时间。
相对于旧系统,运行时间的加速比就是:
![](https://static001.geekbang.org/resource/image/50/ed/50adf0c81433aa34ed91f08319504fed.png)
阿姆达尔定律对我们进行性能优化的指导意义有以下2点。
1. 优先加速占用时间最多的模块,因为这样可以最大限度地提升加速比。
2. 对一个性能优化的计划可以做出准确的效果预估和整个系统的性能预测。
下面这张图描述了不同的并行百分比场景下分别进行并行优化的曲线。不同的曲线对应不同的并行模块百分比。横轴是并行程度,也就是多少个并行处理器。纵轴是速度提升度。
![](https://static001.geekbang.org/resource/image/c4/30/c4fee8386932e98bcf2d20cb67de2130.png)
对每一条曲线我们都可以看到,超过一定的并行度后,就很难进行进一步的速度提升了。
另外说明一点阿姆达尔定律其实是另外一个定律的简化版本。这个更复杂的定律叫通用扩展定律USL, Universal Scalability Law你有兴趣的话可以去学习一下。
## 利特尔法则
接下来我们来看利特尔法则Littles Law。这个法则描述的是在一个稳定的系统中长期的平均客户人数N等于客户抵达速度X乘以客户在这个系统中平均处理时间W也就是说N=XW。
这个法则看起来有点不直观,但从整个系统的宏观角度仔细想想的话就容易理解了。
如下图所示客户按照一定的速度不断地进入我们的系统假设这个速度是每分钟X个客户。每个客户在我们系统里的平均处理时间是W分钟。一旦处理完毕客户就不会滞留在我们的系统里。
![](https://static001.geekbang.org/resource/image/40/89/408c046dc1722db99058ce0bbcf94389.png)
所以如果这个状态稳定也就是说我们的系统处理速度恰恰好赶上客户到达速度的话一方面系统没有空闲另外一方面客户也不需要排队在系统外等待。那么在这个稳定状态下我们的系统的总容量就恰好等于系统里面正在处理的客户数目。也就是说N就等于X和W的乘积。
我举一个服务器性能提升的例子来解释吧。
假定我们所开发的服务器程序可以进行并发处理,同时处理多个客户请求。并发的客户访问速度是**每分钟到来1000个客户**,每个客户在我们的服务器上花费的**平均时间为2分钟**。根据利特尔法则在任何时刻我们服务器系统里面将容纳1000×22000个客户。这2000个客户都在被服务。
过了一段时间由于客户群的增大并发的访问速度从每分钟1000客户增大到**每分钟2000个客户**。在这样的情况下,我们该如何改进我们系统的性能来应对呢?
根据利特尔法则,我们可以有两种方案来解决这一需求。
第一种方案是把客户的处理时间减半从2分钟减到1分钟。这样我们的系统容量可以不变客户滞留在我们系统的时间减半刚刚好可以适应访问速率加倍的要求。系统容量就等于2000客户每分钟乘以1分钟还是2000个客户。
第二种方案是扩大系统容量维持处理时间不变。因为客户访问速度加倍了所以系统容量也需要加倍变成4000。假如原来的系统需要500台服务器那么新系统就需要1000台服务器。
从这里可以引申出利特尔法则在性能优化工作中的两种用处:
1. **帮助我们设计性能测试的环境**。性能测试的内容我们后面会详细讲到,这里简单提一下。比如当我们需要模拟一个固定容量的系统,那么性能测试的客户请求流量速度和每个请求的延时都需要仔细考虑。
2. **帮助我们验证测试结果的正确性**。有时候,如果性能测试的工作没有仔细地规划,得出的测试结果会出奇得好,或者出奇得差,从而让我们抓脑壳。这时如果采用利特尔法则,就可以很快地发现问题所在之处。
## 总结
我们今天讨论的性能工程相关的三大法则,分别是帕累托法则、阿姆达尔定律和利特尔法则。
可以说这些法则就是IT业和性能优化工作的“法律法规”有了它们我们在实际工作中才能做到“有法可依有法必依”。熟悉并熟练应用这几个法则对我们的工作是会有很大的帮助的。
![](https://static001.geekbang.org/resource/image/74/dc/74b119d052157540473f3d75870c64dc.png)
孟子说:“不以规矩,不能成方圆。”熟悉并熟练应用这几个“规律法则”,对我们的工作是会有很大的帮助的。
## 思考题
帕累托法则可以用到很多场景下,除了本讲中讨论的场景,你还能想到什么场景可以使用帕累托法则呢?使用这个法则会帮助你对问题的把握和找寻解决思路吗?
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。