防止代码库变慢的方法


11

我们正在开发中等大小的C ++代码库(10Mloc),通过我们的优化工作,它逐渐变得越来越

该代码库是一组库,我们将它们组合起来以使其起作用。当开发出这些库如何通信的通用框架时,便会着重于性能,后来,当添加更多部分时,通用框架并没有太大变化。在需要时以及随着我们的硬件发展而进行了优化。这使得昂贵的早期决定只有在很久以后才显现出来。现在我们处于进一步优化的代价更高的位置,因为它们需要重写代码库的大部分内容。我们发现自己逼近了不希望有的局部最小值,因为我们知道原则上代码应该能够更快地运行。

是否有任何成功的方法论可以帮助您确定将代码库的发展转向全球最佳执行解决方案的过程,而这些过程又不容易被轻松的优化机会所混淆?

编辑

要回答我们当前如何配置的问题:

实际上,只有两种不同的情况可以使用此代码,这两种情况都令人尴尬地是并行的。使用大量输入的平均值和更详细的运行时间(指令成本,分支错误预测和缓存问题)对挂钟时间进行平均,即可进行性能分析。由于我们仅在极其同类的计算机(由数千个相同的计算机组成的集群)上运行,因此效果很好。由于通常我们大部分时间都在使所有机器繁忙,因此运行速度更快,这意味着我们可以查看其他新内容。问题当然是,当出现新的输入变化时,由于我们删除了其他用例中最明显的微效率问题,因此它们可能会受到后来者的惩罚,从而可能缩小“最佳运行”方案的数量。


10
10Mloc实际上是浩大的工程
BЈовић

1
按算,它是一千万loc(SI前缀)sloc。我称它为“中等大小”,因为我不知道在这里什么被认为是“大”的。
本杰明·班尼尔

5
可以肯定的是,至少有1000万人在每个地方,甚至在大多数地方都是巨大的。
Ryathal 2012年

1
太好了,谢谢@honk对于10M LOC,听起来您正在以非常低的水平(几乎是在硬件级别)进行优化?传统的OOP(AOS“结构数组”)在缓存上的效率极低,您是否尝试过将类重新排列为SOA(数组结构),以便代码所处理的数据点在内存中保持一致?有了这么多机器,您是遇到通信阻塞还是同步吞噬了时间?最后一个问题,您是在处理大量流数据,还是在很大程度上处理数据集的复杂问题?
Patrick Hughes 2012年

1
当您获得了如此多的代码时,您可能会从极好的生活变成了打赌,您可能会发现我提到的非本地类型的潜在巨大提速。如果有成千上万的线程/进程,则没有区别。一些随机的暂停会为您指指点点,或者证明我做错了。
Mike Dunlavey,2012年

Answers:


9

我不知道用于此问题的通用方法,但过去有两种与之相关的方法对我来说效果很好:由于缺乏更好的用语,我称它们为“ 聚束”和“ 水平优化”

捆绑方法是尝试用单个运行缓慢,高度专业化的操作替换大量短而快速的操作,最终产生相同的结果。

:在对视觉规则编辑器的一个特别慢的操作进行性能分析后,我们没有发现“垂头丧气的结果”:没有一个操作占用了超过2%的执行时间,但整个操作却显得迟钝。但是,我们发现编辑器正在向服务器发送大量的小请求。即使编辑者正在快速处理各个答复,请求/响应交互的数量也会产生乘法效果,因此操作花费的总时间为几秒钟。在长时间运行的操作过程中仔细分类了编辑器的交互之后,我们向服务器界面添加了新命令。该附加命令更加专业,因为它接受了执行短操作子集所需的数据,探索了数据依存关系以找出要返回的最终数据集,并提供了一个响应,其中包含在单次访问服务器时完成所有单个小操作所需的信息。这并没有减少我们代码中的处理时间,但是由于消除了多个昂贵的客户端-服务器往返行程,因此减少了大量的延迟。

当使用执行环境的特定功能消除在系统的多个组件中稀疏分布的“慢”时,水平优化是一项相关技术。

:对长时间运行的操作进行了性能分析后,我们发现我们在应用程序域边界进行了许多调用(这是.NET的特有方法)。我们无法消除任何呼叫,也无法将它们聚集在一起:它们在不同时间来自我们系统的各个不同部分,它们所请求的内容取决于先前请求返回的结果。每个调用都需要对相对少量的数据进行序列化和反序列化。同样,单个呼叫的持续时间很短,但数量却很大。我们最终设计了一种方案,该方案几乎完全避免了序列化,而是通过在应用程序域边界上传递指针来代替它。这是一个巨大的胜利,因为通过应用单个应用程序,完全不相关的类的许多请求立即变得更快横向解决方案。


感谢您分享您的经验,这些是记住有用的优化。而且,由于它们将有问题的零件提升到一个独特的位置,因此将来控制起来会更好。从某种意义上说,他们首先将应该发生的事情放到原地,现在只有硬数据才能支持。
本杰明·班尼尔

3

这使得昂贵的早期决定只有在很久以后才显现出来。现在我们处于进一步优化的代价更高的位置,因为它们需要重写代码库的大部分内容。

开始重写时,您必须做一些不同的事情。

第一。而且最重要。停止“优化”。“优化”一点都不重要。如您所见,只有批量重写才有意义。

因此。

第二。了解每种数据结构和算法选择的含义。

第三。使数据结构和算法的实际选择成为“后期绑定”问题。设计接口,该接口可以具有在接口后面使用的多种实现中的任何一种。

如果已经定义了一组接口,使您可以对数据结构或算法进行全面更改,那么您现在正在做的事情(重写)应该要痛苦得多。


1
感谢您的回答。尽管我们仍然需要进行优化(对我来说,这属于1.和2.),但我非常喜欢3.背后的有组织的思想。通过使数据结构,算法和访问定义得较晚和明确,一个人应该能够处理很多问题。我们面临的问题。感谢您使用一致的语言。
本杰明·班尼尔

您实际上不需要优化。一旦拥有正确的数据结构,优化将显示为浪费精力。分析将显示您有错误的数据结构和错误的算法。++和之间的性能差异混为一谈+=1是无关紧要的,几乎是无法衡量的。这是你要的东西持续
S.Lott 2012年

1
并非所有纯粹的推理都能找到错误的算法。偶尔需要坐下并进行个人介绍。这是找出最初猜测是否正确的唯一方法。这是估计实际成本(BigO + const)的唯一方法。
Benjamin Bannier 2012年

分析将揭示错误的算法。完全正确。那仍然不是“优化”。这仍然是我进行设计更改时对基本设计缺陷的纠正。优化(调整,微调等)对于配置文件很少可见。
S.Lott 2012年

3

一个不错的实用技巧是将单元测试套件用作性能测试套件

以下方法在我的代码库中效果很好:

  1. 确保您的单元测试覆盖面良好(您已经这样做了,对吧?)
  2. 确保您的测试运行框架报告每个测试的运行时间。这很重要,因为您要查找性能下降的地方
  3. 如果测试运行缓慢,请以此为手段进行深入研究,并在该区域进行优化。在确定性能问题之前进行优化可能被认为还为时过早,因此此方法的优点在于,您首先会获得性能不佳的具体证据。如果需要,可以将测试分为一些较小的测试,这些测试可以对不同方面进行基准测试,从而可以确定根本问题所在。
  4. 如果测试运行得非常快,那通常是好的,尽管您随后可能会考虑使用不同的参数在循环中运行测试。这使其成为更好的性能测试,并且还增加了对参数空间的测试范围。
  5. 编写一些针对性能的额外测试,例如端到端事务处理时间或完成1,000个规则应用程序的时间。如果您有特定的非功能性性能要求(例如,响应时间小于300毫秒),则如果测试时间过长,则使测试失败。

如果您继续执行所有这些操作,那么随着时间的流逝,您的代码库的平均性能应该会有机地提高。

还可以跟踪历史测试时间并绘制性能图表,并发现平均性能随时间的变化。我从来没有打扰过它,主要是因为要确保在进行更改和添加新测试时进行比较有点棘手,但是如果性能对您来说足够重要,这可能是一个有趣的练习。


我喜欢技术-聪明- howevre,这是微优化,将提高到当地最低-它不会解决,让你打全球最低的建筑问题
jasonk

@jasonk-你绝对正确。尽管我想补充一点,有时它可以为您提供证据,以证明您为什么需要对特定的体系结构更改进行合理说明……..
mikera 2012年

1

@dasblinkenlight的答案指出了一个非常普遍的问题,特别是对于大型代码库(以我的经验)。可能存在严重的性能问题,但它们不是本地化的。如果运行探查器,则没有例程会花费足够的时间来吸引自己的注意力。(假设您查看的是包含被叫方的全包时间百分比。甚至不用理会“自己的时间”。)

实际上,在那种情况下,实际问题不是通过剖析发现的,而是通过幸运的洞察力发现的。

我有一个案例研究,其中有一个简短的PDF幻灯片演示,详细说明了此问题以及如何解决此问题。基本要点是,由于代码要慢得多,因此(根据定义)这意味着多余的时间花费在了可以删除的事情上。

如果要查看程序状态的一些随机时间样本,由于所花费的时间百分比,您只会看到它正在执行可移动活动。可移动活动很可能不仅限于一个功能,甚至不限于多个功能。它不会以这种方式本地化。

这不是一个“热点”。

这是您对所见所闻的描述,大部分时间都是真实的。这使得发现起来很容易,但是是否容易修复取决于它需要多少重写。

(这种方法经常引起批评,即样本数量太少而无法用于统计有效性。这在PDF的幻灯片13上得到了回答。简要地说-是的,潜在节省的“衡量”存在很大的不确定性,但是1)潜在节省的期望值基本上不受影响,并且2)当潜在节省$ x $转换为加速比乘以$ 1 /(1-x)$时,它会严重偏向高(有利)因子。)


感谢您的回答。我们不相信统计抽样,而是将其与valgrind配合使用。这使我们对我们所做的大多数事情的“自我”和“包容性”成本都有很好的估计。
本杰明·班尼尔

@honk:对。但是可悲的是,仪器仍然认为性能问题是局部的,因此可以通过测量在例程等中花费的时间比例来发现。您当然可以运行valgrind等,但是如果您想真正了解性能,请查看该幻灯片。 。
Mike Dunlavey,2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.