是什么使大型复杂软件产品变慢?[关闭]


16

由于很大程度上无关紧要的原因,我在这么长时间内再次安装了Delphi 7。我不得不说,我完全被震撼了-在某种程度上我还没来过。这根本不是我记得的东西。安装耗时约30秒。启动它花了2秒钟,并且立即可用。我可以在启动后第二秒钟按“运行”,不到一秒钟后,空白程序就已经可见并正在运行。万岁为计算机变得如此快!

但是我之所以被这样震撼,是因为通常我使用Visual Studio 2010,一点也不觉得这样快。诚然,Delphi 7是一个比Visual Studio 2010小得多的系统,但是它确实具有所有真正必要的东西的外观:控件面板,表单设计器,具有代码完成功能的代码编辑器。我意识到该语言可能更简单,代码完成功能可能不太强大,IDE可能没有那么可扩展且功能丰富,但是仍然:我不了解如何(即通过哪种机制)进行许多额外的功能(我可能还没有触发过)使类似Visual Studio的系统总是比较呆滞。

我想问一下在Visual Studio规模方面具有丰富经验的人:是什么让它们变慢了?是将代码库保持在人类理解能力范围内所需的抽象层吗?是否需要运行大量的代码?在时钟周期/内存使用部门(付出惊人的巨大代价)上,是否倾向于节省程序员时间的方法的现代趋势?


7
简单:随着质量的增加,需要更多的力来克服惯性。
Shog9 2011年

曾经有人告诉我经理,但我一点也不相信。
MIchael Grassman 2011年

1
这是我仍然主要使用D7进行Delphi编程的大部分原因。
GrandmasterB

最快的代码是永远不会执行的代码。
亨利

4
@romkyns:我发现现代时代的许多软件常常是肿的,不必要的庞大和笨拙。现在,许多软件可以用十分少的功能和空间来解决与十年甚至20年前解决的相同的问题。为什么它仍然比以往严重滞后,甚至更多?效率低下和膨胀。
2011年

Answers:


20

建筑宇航

Visual Studio 2010基于Windows Presentation Foundation构建。看一下WPF 的Button类。它是基础班的第9个孩子。它具有约5页的属性,方法和事件。在幕后,它还有另外五页样式定义,描述了其精美的圆角和鼠标指针移到其上时的微妙动画过渡。这一切都是为了从根本上显示一些文本或图片,并在检测到鼠标按钮下降时产生click事件。

在任意随机位置停止像Visual Studio这样的程序。查看堆栈跟踪。很有可能您已进入调用堆栈20个级别,并且已加载五个DLL才能到达那里。

现在,将这两件事与Delphi进行比较。我敢打赌,您会发现Delphi Button仅具有20个属性,方法和事件。我敢打赌,Delphi IDE的堆栈跟踪深度只有5-7级。因为当计算机速度较慢时,如果没有IDE花费40分钟才能启动,您就无法承受Visual Studio 2010的开销:-)

这个比那个好吗?好吧,我通常可以告诉Delphi程序何时加载,因为它看起来很平坦,颜色被静音了(也许是8位?),并且没有细微的阴影或动画。这些天,我只是觉得“便宜”。便宜,但是很快。

我们过得更好吗?这是哲学家而不是编码员的问题。


4
一个delphi程序看起来并不平坦。而是,程序员对程序进行编程以使其看起来平坦。您可以像使用C#或C ++一样使用Delphi制作美观,现代的全彩色界面。
GrandmasterB

2
这是一个有见地的答案;但我不确定它是否完整。Visual Studio 2008(2010的前身)中没有WPF,但仍然比Delphi 7慢很多。对于调用堆栈深度和加载的DLL数量,您是否还会说同样的话?
Timwi'1

3
@Timwi是的,绝对可以。我的意思不是关于WPF的弊端(实际上我很喜欢WPF),而更多地是关于在有选择的情况下如何倾向于在软件抽象层上添加层。也许Visual Studio 2008并没有那么多开销,但是正如您所指出的,它已经足够了:-)
Jay Beavers

@GrandmasterB,我不会抨击Delphi,因为它带有更少的假设和更简单的库。WPF的设计假设GPU硬件加速将允许程序使用更深的颜色,频繁的动画,alpha混合,阴影等。Delphi是在无法做出这些假设的时候设计的。您能在Delphi中重新实现所有这些功能吗?当然可以,但是您必须投入大量代码才能获得WPF按钮的行为。从好的方面来说,Delphi按钮不具备CPU,内存和GPU的要求,而WPF按钮则不是@OP的问题。
杰·海弗斯

10
Windows 10的新“现代” UI完全使您对平面UI和纯UI的论点无效。现在,我们拥有所有这些开销,以便像30年前那样创建扁平,方形,普通的按钮。
gbjbaanb

11

我想问一下在Visual Studio规模方面具有丰富经验的人:是什么让它们变慢了?是将代码库保持在人类理解能力范围内所需的抽象层吗?是否需要运行大量的代码?在时钟周期/内存使用部门(付出惊人的巨大代价)上,是否倾向于节省程序员时间的方法的现代趋势?

我想您猜到了很多,但是我想提供我认为是最大的因素,因为在相当大的代码库上工作(不确定它是否与Visual Studio一样大-包含数百万行代码)类别和大约一千个插件)约10年的时间,并且会出现观察现象。

由于它没有涉及API或语言功能或类似功能,因此它的争议也较小。这些与“成本”有关,它可能引发辩论而不是“支出”,我想集中讨论“支出”。

松散的协调和传统

我观察到的是,松散的协调和长期的遗留往往会导致大量累积的浪费。

例如,我在此代码库中发现了大约一百种加速结构,其中许多都是冗余的。

我们想要一棵用于加速一个物理引擎的KD树,一棵用于经常与旧物理引擎并行运行的新物理引擎的KD树,我们将有数十种用于各种网格算法的八叉树实现,另一棵用于渲染的KD树,采摘等等等。这些都是大而笨重的树形结构,用于加速搜索。对于一个非常平均大小的输入,每个人可能要占用数百兆到千兆字节的内存。它们并不总是被实例化并一直使用,但是在任何给定时间,它们中的4或5可能同时在内存中。

现在,所有这些都存储了完全相同的数据,以加快对它们的搜索。您可以像模拟旧数据库一样想象它,该数据库将其所有字段一次存储到20个不同的冗余映射/字典/ B +树中,并通过相同的键进行相同的组织,并始终对其进行搜索。现在,我们要处理20倍的内存和处理。

此外,由于存在冗余,因此几乎没有时间用随之而来的维护价格标签来优化其中的任何一个,即使我们这样做了,也只能达到理想效果的5%。

是什么原因导致这种现象?松散的协调是我看到的第一大原因。许多团队成员经常在各自孤立的生态系统中工作,开发或使用第三方数据结构,但是却没有使用其他团队成员所使用的相同结构,即使他们完全是完全相同的关注点。

是什么导致这种现象持续存在?传统和兼容性是我看到的第一大原因。由于我们已经支付了实现这些数据结构的成本,并且大量代码依赖于这些解决方案,因此尝试将它们合并为更少的数据结构通常太冒险了。即使这些数据结构中的许多在概念上都是高度冗余的,但在接口设计中它们并不总是接近相同。因此,与仅让它们消耗内存和处理时间相反,替换它们将是一个巨大且冒险的更改。

记忆效率

通常,内存使用和速度往往至少在批量级别上相关。您通常可以通过占用内存的方式来发现速度较慢的软件。并非总是如此,因为更多的内存会导致速度下降,因为重要的是“热”内存(一直在访问什么内存-如果程序使用大量的内存,而所有程序仅使用1 MB的内存),时间,那么在速度上就没什么大不了的)。

因此,您可以经常根据内存使用量发现潜在的猪。如果应用程序在启动时占用数十至数百兆的内存,则可能效率不高。如今,当我们拥有千兆字节的DRAM时,数十兆字节可能看起来很小,但是最大和最慢的CPU高速缓存仍在可怜的兆字节范围内,而最快的仍然在千字节范围内。结果,从硬件CPU缓存的角度来看,仅使用20兆字节启动且不执行任何操作的程序实际上仍在使用“大量”内存,特别是如果该内存的全部20兆字节将被重复访问并程序运行时频繁运行。

对我来说,解决方案是寻找更协调,规模更小的团队来构建产品,他们可以跟踪自己的“支出”并避免一遍又一遍地“购买”相同的商品。

成本

我将深入讨论更具争议性的“成本”方面,只是我观察到了一些带有“支出”现象的现象。如果一种语言最终给对象带来了不可避免的价格标签(例如提供运行时反射并且无法强制为一系列对象进行连续分配的价格标签),则该价格标签仅在非常细粒度的元素(例如单身PixelBoolean

然而,我看到了很多的源代码里面做处理重负载(例如:处理几十万到上百万的程序PixelBoolean实例)在这样的粒度级别支付该费用。

面向对象的编程会加剧这种情况。然而,这并不是“对象”本身或什至是错误的OOP的代价,只是这样的费用是以微小的微小元素支付的,将被数百万例示。

这就是我正在观察的其他“成本”和“支出”现象。成本是几美分,但是如果我们要单独购买一百万罐苏打水,而不是与制造商进行批量购买,则要花费几分钱。

对我来说,解决方案是“批量”购买。即使在每种语言都带有几美分的价格的语言中,对象也可以很好地实现,前提是该成本不会比类似的汽水罐高出一百万倍。

过早优化

我从来不喜欢这里使用的Knuth措词,因为“过早的优化”很少会使现实的生产程序运行得更快。有人将其解释为“尽早优化”,而Knuth的含义更像是“没有适当的知识/经验来进行优化以了解其对软件的真正影响”。如果有的话,真正的过早优化的实际效果通常会使软件变慢,因为可维护性的降低意味着几乎没有时间优化真正重要的关键路径。

这是我观察到的最后一种现象,开发人员在购买一罐苏打水时节省了几分钱,再也买不到,或更糟的是,房子浪费了所有的时间捏着几分钱(或更糟的是,想象中的几分钱从无法理解他们的编译器或硬件体系结构),而在其他地方浪费了数十亿美元。

时间是非常有限的,因此在没有适当的上下文信息的情况下尝试优化绝对值通常会使我们失去优化真正重要位置的机会,因此,从实际效果上来说,我会说“过早的优化会使软件运行缓慢。 ”

问题在于,有一些开发人员类型会采用我在上面写的有关对象的内容,并尝试建立一种编码标准,以禁止面向对象的编程或类似的疯狂方式。有效的优化是有效的优先级划分,如果我们陷入了维护难题的海洋中,那绝对是毫无价值的。


2
换句话说,就是技术债务。永远无法偿还的技术债务。
罗伯特·哈维

1
罗伯特是正确的。一个人犯的一个错误--force,经理人大喊的200个错误,大喊“如果明天不实现,就会被解雇”,这摧毁了多年的良好软件工程实践,TDD,单元测试以及任何人为和理智的编程原则,再加上两次,您很累..那个离开公司发疯的家伙是因为他无缘无故下岗并弄乱了代码库。.这些从未停止更新的库,您从来没有更新过……现在您拥有了:美味的意大利面条代码库和肿的软件。Bon
appetit

2
有趣,尤其是在您看到滥用过多粒度的方式方面。过去,我有时会做类似的事情,结果表现很差。这与几天前关于使用集合和批量算法优先于过度粒度的答案非常相似。我不敢相信,答案因其深刻而倍受赞赏。这使我重新思考了我多年来构建的几种设计。我不知道为什么这些技术没有得到更广泛的推广?
Mike

2
@Mike在尝试推广更多面向数据的思维方式时,我的表现有些差。它在游戏行业中很流行,他们试图利用每一英寸的硬件。也就是说,它确实会降低灵活性。如果您有一个抽象的像素类,则可以做一些疯狂的事情,例如将一张图像混合两种或多种不同的像素格式!但是,当我们处理关键路径时,这种灵活性可能不会使任何图像受益,而对于涉及图像和像素的任何事物,性能开始成为真正的问题。

1
在过去的糟糕时期,我实现了一些代码来绕过图形API,并直接访问内存中的像素以获取关键代码。抽象层和直接访问层之间的差异大约是100倍,这在当时的计算机上很重要。现在,您的计算机速度足够快,您可以根据需要进行任意数量的抽象处理。
Michael Shopsin
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.