问题诊断
在几乎所有邀请我修复的性能问题中,所述问题都与响应时间有关:“过去完成X不到1秒;现在有时需要20+”。当然,类似具体的表述通常掩盖在其他问题的表象下,例如:“我们整个系统太慢了,以至无法使用它”[8]。
虽然我遇到很多类似的很多事情,并不意味着它会发生在你身上。你首先要做的最重要的事情,就是清楚地说明问题,以便可以清楚地思考它。
提问是开始的好方法,你想要实现的目标状态是什么?找到一些可以测量的指标来表达它:例如,“在许多情况下,X的响应时间超过20秒。我们希望至少95%的执行响应时间为小于等于1秒。“听起来不错,但如果您的用户没有这样一个指标量化的目标呢?该目标有两个数值(1和95);如果您的用户不知道其中任何一个,该怎么办?更糟糕的是,如果您的用户有不可能实现的具体的想法,该怎么办?你怎么确定什么是“可能”或“不可能”呢?
我们一起寻找方法解决以上问题。
时序图
时序图是UML(统一建模语言)中指定的一种图形,用于按交互发生的顺序表示对象之间的交互。时序图是可视化响应时间的一个非常有用的工具。Figure 1 是一个标准的UML时序图,展示了由浏览器、应用服务器和数据库组成的简单应用系统。
假设现在按比例绘制时序图,使得每个进入的“请求”箭头与其相应的“响应”箭头之间的距离与处理请求所花费的持续时间成正比。我在 Figure 2 展示了这样一个图。很好的以图形化的形式表示了图中组件如何花费用户的时间。您可以通过看图来“感受”响应时间的相对比例。
时序图正合适帮助人们产生概念,在一层将任务的控制权交给下一层的过程中,响应如何消耗在给定系统上。时序图也很好地展示了同步处理线程如何并行工作,它是超出信息技术之外分析性能的好工具[10]。
时序图是讨论性能的一个很好的概念性工具,但要把性能思考清楚,还需要其他工具。有一个问题。假设你需要处理的任务响应时间为2468秒(41分钟,8秒)。在这段时间内,运行该任务会使应用服务器执行322968次数据库调用。 Figure 3 显示了该任务的时序图。
应用和数据库层之间有很多请求和响应箭头,你看不到任何细节。在非常长的长卷上打印序列图并非有用的解决方案,因为在你需要数周的目视检查,才能从看到的细节中获取有用的信息。
序列图是一个很好的工具,将控制流和相应的时间流形成概念。然而,要把响应时间思考清楚,你还需要其他一些工具。
性能剖析
时序图不能很好地扩展。为了处理具有大量调用次数的任务,你需要方便地汇总时序图,以便了解花费时间的最重要模式。Table 2 展示了一个性能剖析的例子,它做到了这一点。性能剖析是响应时间的表格分解,通常按响应时间占比降序列出。
例四:Table 2 中的性能剖析是基本的,但它确切地显示了你缓慢任务花费用户2468秒的地方。例如,使用此处显示的数据,可以得到性能剖析标识的每个函数调用的响应时间的百分比。你还可以得到任务中每种类型函数调用的平均响应时间。
性能剖析显示你的代码花费时间的地方,甚至更重要的是显示了没有花费时间的地方。不必猜测本身就有很大的价值。
从 Table 2 展示的数据中,你可以知道 DB:fetch() 调用消耗了用户响应时间的70.8%。此外,调用的持续时间被聚合以创建此性能剖析,如果您可以深入单个调用,那么你可以知道有多少 DB:fetch() 调用,相应的 App:await_db_netIO() 调用有多少,并且你可以知道每个调用消耗了多少响应时间。“这个任务应该运行多长时间?”,通过性能剖析,您可以开始形成问题的答案。到目前为止,您已经知道在任何好的问题诊断的第一步,这都是的一个重要问题。
阿姆达尔定律
性能剖析有助于您清楚地思考性能。即使 Gene Amdahl 在1967年没有提出阿姆达尔定律,你也可能在看到最初几个性能剖析之后提出它。
阿姆达尔定律指出:性能提升与程序使用你的改进点的程度成正比。如果你尝试改进的点仅占任务总响应时间的5%,那么您能够最大影响总响应时间的5%。意味着你的工作越接近您的性能剖析的顶部(假设性能剖析按响应时间降序),总体响应时间的收益潜力就越大。
这并不意味着您总是使用性能剖析自上而下的顺序工作,因为您还需要考虑您要执行的改进措施的成本[9]。
例五:考虑 Table 3 的性能剖析。它与 Table 2 的性能剖析基本一样,除此之外,您可以看到你认为通过为性能剖析的每一行实施最佳的改进措施可以节省多少时间,以及每种改进措施实施的成本是多少。
您将首先实施哪种改进措施?阿姆达尔定律说,对第1行实施改进具有最大的潜在收益,可以节省约851秒(2468秒的34.5%)。但是,如果它确实“成本高昂”,那么第2行的改进措施可能会产生更好的净收益,即使可能节省的响应时间仅为305秒,这也是您真正需要优化的制约点。
性能剖析的巨大价值在于,您可以准确预计投入带来的改进效果。它打开了一扇大门,让人们更好地决定首先实施哪些改进措施。作为分析师,您的预测为您提供了衡量自己表现的标准。最后,它让您有机会展示自己的聪明才智和您对技术的亲和力,因为您可以找到更有效的改进方法,以低于预期的成本缩短响应时间。
首先采取什么改进措施,归根结底取决于你对成本估算的信任程度。“成本极其廉价”的提议改进是否真的考虑到了可能对系统造成的风险?例如,改动该参数或删除该索引似乎成本极低,当前您根本没能顾及的,改动是否会潜在地破坏某些对象的良好性能的行为?可靠的成本估算是另一个技术有所回报的领域。
另一个值得考虑的因素,你可以通过创造小胜利获得政治资本。也许成本低廉、低风险的改进不会带来太多整体响应时间的改善,但建立小改进的跟踪记录是有价值的,它可以准确地兑现你对慢任务响应时间降低的预测。预测和实现的跟踪记录最终会让你获得影响同事(同事、经理、客户等)所需的可信度(尤其是在软件性能领域,几十年来神话和迷信在许多地方盛行)。使得你可以执行成本越来越高的改进措施,从而为企业带来更大的回报。
不过,有一句话需要提醒:当你获得成功并提出更大、更昂贵、风险更大的改进措施时,千万不要大意。信誉是脆弱的。建立信誉需要大量的工作,但摧毁只需要一个疏忽大意的错误。
倾斜
当使用性能剖析时,会反复遇到如下的子问题:
例六:Table 2 性能剖析表明 322,968 次 DB:fetch() 调用花费了1748.229秒的响应时间。如果你能去除一半的调用,你会减少多少不必要的响应时间?答案几乎永远不会是 “一半的响应时间”。考虑一下这个简单得多的例子:
例七:四个子程序调用花费四秒钟。如果你能去除一半的调用,您会减少多少不必要的响应时间?答案取决于可以去除的调用的响应时间。你可能已经假设每个调用持续时间的平均值为4/4=1秒,但描述中没有任何地方告诉你调用持续时间是一样的。
例八:设想以下两种可能,每个列表表示四个子程序调用的响应时间:
A = {1, 1, 1, 1} B = {3.7, .1, .1, .1}
列表 A,响应时间是一样的,因此无论你去除哪一半(两个)调用,你都可以将总响应时间减少到两秒。然而,列表 B,哪两个调用被去除会有很大不同。如果去除前两个调用,则总响应时间将降至0.2秒(减少95%)。如果去除最后两个调用,则总响应时间将降至3.8秒(仅减少5%)。
倾斜是一系列数值的不均匀性。各种可能的倾斜使你无法给我在本节开头提出的问题提供准确的答案。让我们再看看:
例九:Table 2 性能剖析表明 322,968次 DB:fetch() 调用花费了1748.229秒的响应时间。去除一半的调用,将去除多少不必要的响应时间?在不了解任何关于倾斜的情况下,您可以提供的最准确的答案是,“介于0到1748.229秒之间。”
但是,假设你有 Table 4 中附加的信息。你就可以得出更精确的最佳情况和最坏情况的估计。具体来说,如果有这样的信息,您会聪明些想出如何确切地减少47444个响应时间在0.01到0.1秒范围内的调用。
最小化风险
前几节我提到了一种风险,改进一个任务的性能可能会损害另一个任务的性能。这让我想起了一个发生在丹麦的故事。故事很简单:
场景:丹麦的巴勒鲁普自治市(Måløv)的橡木餐桌(事实上,橡树桌网络名声在外,这是一个由 Oracle 从业者组成的网络,他们相信使用科学方法来改进基于甲骨文的系统的开发和管理)[12]。大约有10个人围坐在桌子旁,在笔记本电脑上工作,进行各种对话。
Cary:伙计们,热死我了。你们介意我把窗户打开一点,让冷空气进来吗?
Carel-Jan:你为什么不脱下你的厚毛衣?
完。
有一个工作中普遍的原则,从事优化的人都知道:当除了你每个人都很高兴,在你乱搞那些影响到其他人的全局事务之前,确保你的本地事务井然有序。
这就是为什么每当有人建议更改系统的 Oracle SQL * Net 数据包大小时,我都会退缩。因为问题实际上是几个写得不好的Java程序,进行了许多不必要的数据库调用(因此,也会产生不必要的网络I/O调用)。如果除了一两个程序的用户外,每个人都相处得颇为融洽,那么解决这个问题最安全的办法就是将范围局限于这一两个程序。
续下篇:Thinking Clearly about Performance (Part 2)
原文链接:Thinking Clearly about Performance
本文作者 : cyningsun
本文地址 : https://www.cyningsun.com/10-21-2020/thinking-clearly-about-performance-cn-part-one.html
版权声明 :本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!