在大型软件项目中,如何衡量实践中的复杂性?


11

在大学中,在我们的算法课程中,我们学习如何精确计算实际使用的各种简单算法(例如哈希表或快速排序)的复杂性。

但是现在在一个大型软件项目中,当我们想使其更快时,我们所要做的只是看各个部分-那里有一些嵌套循环,可以用更快的哈希表代替,这里的慢速搜索可以通过更花哨的技术-但我们从未计算过整个流程的复杂性。

有什么办法吗?还是在实践中人们只是使用快速算法依靠“本地”来使整个应用程序更快,而不是全局地考虑整个应用程序?

(因为在我看来,要证明如果您堆积大量已知非常快速的算法,那么最终还是会得到一个整体快速的应用程序。)

我之所以这样问,是因为我的任务是加快别人编写的大型项目的速度,其中许多算法正在交互并处理输入数据,因此我不清楚在单一算法上更快地完成单一算法的影响。整个应用程序。


1)这需要一种测试方法,以找到要改进的地方。基准测试,耐久性测试,动态测试(通过监视每个组件的内存/ CPU指标)。2)找到要改进的要点后,您将找到这些要点的根本原因。3)找到解决根本原因的解决方案,并保持正确性。
外汇兑换

您需要一些用于第1点中提到的测试的工具
过度交换'18

1
大O分析无法告诉您算法的执行方式。它告诉你的表现将如何扩展,如n增加。
John Wu,

Answers:


5

大型软件项目由许多不同的组件组成,并不是所有这些组件通常都是瓶颈。恰恰相反:对于我生命中几乎所有程序中低性能都是一个问题,帕累托原理适用:通过优化不到20%的代码就可以实现80%以上的性能提升(实际上,我认为数字通常在95%到5%之间)。

因此,开始研究各个部分通常是最好的方法。这就是为什么进行概要分析(如David Arno的答案所述)的原因,因为它可以帮助您识别出提到的5%的代码,优化将使您“物有所值”。优化“整个应用程序”具有过度工程化的风险,如果将这95%的优化程度提高了10倍,那么通常没有任何可测量的效果。还要注意,剖析比任何假设的算法复杂度估计都能为您提供更多信息,因为需要O(N ^ 3)个步骤的简单算法仍然比需要O(N log(N))的复杂算法要快,只要N足够小

分析显示热点后,可以对其进行优化。当然,“热点”可能比一两行代码更大,有时必须替换整个组件才能使其更快,但是在较大的程序中,这通常仍然只是代码库的一小部分。

典型的优化技术包括

  • 改善算法和数据结构的使用

  • 前者的微调

  • 在某些实际热点上进行微优化

  • 使用汇编代码或CUDA重新编码关键部分

请注意,这些技术在不同的抽象级别上起作用,其中一些比其他技术更“整体”地看待组件。因此,这取决于您的意思是“我们要做的就是查看各个部分” -如果您仅考虑微优化,那么我不同意“我们”仅在此方面进行工作。但是,如果您要对孤立的零件或组件进行全面的优化,则“我们”可能正在开发正确的零件,因此您应该质疑自己的期望。


13

经过检验的标准方法是分析代码。您可以对正在运行的系统进行动态分析,以测量时序,内存使用情况等。然后分析结果以查找性能瓶颈。

然后,将这些瓶颈进行实验性重写,并再次分析结果以确定是否已实现速度提高,内存使用减少等。然后重复此过程,直到获得可接受的性能提升。


1
这解决了性能问题,这就是我们这样做的原因,但没有回答原始问题。我认为,在最坏的情况下,最好使用静态程序分析工具找出时间或空间的复杂性,而后者可能会丢失。性能测试对于特定情况非常有用,但它们并不能告诉您很多最坏的情况。
弗兰克·希勒曼

3
@FrankHileman我认为这里的要点是性能是一个实际问题,只能实际测量。即使使用数学(算法)解决了瓶颈,您也不会使用数学来找到软件的瓶颈。
通配符'18

与此相关的是,在较早的幻灯片演示中(玻璃幻灯片),有一种完整的伪装技术,该技术如何通过数学上的努力来计算灯笼幻灯片的平均密度,从而确定要使用的光的亮度。完全没用:如果图片显示不佳,您会得到更亮的光线!
通配符

@Wildcard虽然只能在运行时测量性能,但是可以静态地预测性能。在性能测试中,对数据结构的错误选择可能看起来不错,但在静态分析可以预测的边缘情况下却失败了。这也是我们通常对数据结构进行最坏情况复杂度分析的原因。
弗兰克·希勒曼

@Wildcard:您是正确的,但是Frank也非常正确,即该帖子未回答问题。
布朗

3

尽管其他答案是正确的,并提供了一些指导,但我的确认为它们错过了一步。在像您现在正在使用的复杂系统中,了解组成系统的不同组件是了解为什么速度缓慢的关键。

我的第一步是获得详细的体系结构图或自己创建一个。找出软件中的哪些组件采取了哪些步骤以及每个步骤花费了多长时间。

另外,找出组件之间如何交互。这可以使一切变得不同。

例如,我看过C#中的代码,其中两个组件之间的接口正在传递由第一个组件构建的IEnumerable,然后由第二个组件枚举。在C#中,这需要上下文切换,这在某些情况下可能会很昂贵。解决它对算法没有影响。一个简单的.ToList()确保在下一步解决此问题之前已收集结果。

要考虑的另一件事是对运行代码的系统的影响。硬件交互显然是复杂系统中的一个因素。查找磁盘IO,大内存分配和网络IO。有时可以通过调整系统甚至更换硬件来更有效地解决这些问题。

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.