我希望在我的一个OpenGL项目中大力转向模板,主要是为了娱乐和学习体验。我计划仔细观察可执行文件的大小,看看有多少臭名昭着的膨胀发生。目前,当我赞成速度时,我的Release版本大小约为580 KB,当我喜欢尺寸时,我的版本大小为440 KB。
是的,这是一个很小的项目,实际上即使我的可执行文件大小只有10倍,它仍然会达到5 MB左右,按照今天的标准来看似乎不大......或者是它?这让我想到了我的问题。速度是否与大小成正比,或者是否存在特定阈值的跳跃和平稳期,我应该保持低于阈值? (如果是这样,具体的阈值是什么?)
答案 0 :(得分:14)
在大多数现代处理器上, locality 比尺寸更重要。如果您可以将所有当前正在执行的代码和大部分数据保存在L1缓存中,那么您将看到大赢。如果你四处跳跃,你可能会强制将代码或数据从缓存中删除,然后很快再次需要它。
根据我的经验,“面向数据的设计”有助于代码和数据的位置。您可能对Pitfalls of Object Oriented Programming slides (pdf)(mirrored here (pdf))感兴趣,它可以很好地展示如何以这样的方式处理事情,从而获得良好的数据和代码位置。(顺便说一下,整个缓存大小和位置的事情是“优化大小”在某些情况下可以胜过“优化速度”的原因之一。)
答案 1 :(得分:9)
速度取决于很多因素。这就是为什么有一个遵循良好架构原则的合理程序设计是好的。
但是,可执行文件的实际大小与其性能无关,如果应用程序设计正确。通过分析应用程序和修复慢速部分所获得的性能改进只会影响代码的一小部分(10%或更少)。
在任何给定的时刻(除非程序令人尴尬地并行,或者代码的性能关键部分碰巧非常大),无论如何只有一小部分代码在处理器内执行。
L1缓存尤其如此。原则上,大型程序的执行速度比较小的程序要慢,但实际上,如果保持性能关键部分足够小,性能关键代码应保留在L1缓存中。
请记住,紧凑的高性能循环只需将自身加载到L1缓存中,这是第一次循环。
答案 2 :(得分:6)
可执行“膨胀”对性能的影响小于以下两个因素(相关但不相同)
程序运行以执行工作单元所需的代码量(例如渲染帧)将影响指令缓存命中率和页表命中率。但是,如果90%的工作是在一个完全适合i-cache的小函数中完成的,那么整个程序的代码大小只会占其他10%。
与数据类似,如果您的程序每帧必须触摸100MB数据,这将比具有适合L1,L2或L3缓存的工作集的程序执行得更糟。
因此,可执行文件的大小并不多,但在任何时候都使用了多少“东西”。
答案 3 :(得分:4)
我的拙见认为,“模板膨胀”基本上是C ++的笨蛋;也就是说,这是一个让孩子感到害怕的故事,但我从来没有见过证据表明它确实存在于任何明显的规模上(当然,除了编译器特定的膨胀之外)。由于为任何模板参数集生成了唯一代码,人们会声称它存在,但是他们经常没有提到没有模板,无论如何都要进行代码复制(无论是手工还是宏)。
也就是说,模板可以通过其他方式失控;例如,元编程技术可以推算编译时间。但我认为其好处实际上超过了成本。
答案 4 :(得分:3)
如果您使用的是Unix,则可以在valgrind的cachegrind工具下运行程序,直接测量可执行文件大小和位置对程序运行时的影响,而不必仅尝试从运行时编号向后工作。 cachegrind也为您提供了大量有关数据位置的信息。
调用看起来像valgrind --tool=cachegrind ./your_program arguments
。
对于名为KCacheGrind的valgrind套件,还有一个很好的Qt GUI。
答案 5 :(得分:2)
您的可执行文件的大小并不重要。它是“活动”代码的大小,应用程序实际执行的代码很重要。遗憾的是,这很难量化。对于简单的近似,您可以对应用程序进行概要分析,采用占执行时间90%的过程,并添加其代码大小。
大多数现代处理器具有64KB或128KB指令缓存,因此有助于将活动代码保持在此大小以下。下一个阈值是L2大小,可以是几兆字节。
答案 6 :(得分:2)
根据我的经验,代码膨胀和执行时间膨胀是相辅相成的,而这一切都与软件的设计有关,尤其是数据结构的设计方式。
如果遵循这种方法,即每个心理概念都成为一个类,如果遵循通知风格模式,只需设置一个属性,或者将一个项目添加到一个集合,就会导致整个过程中传播的行为产生隐藏的连锁反应。大型非规范化数据结构网络为了尽量保持一致,那么结果将是大量的源代码和糟糕的性能。
另一方面,如果试图最小化数据结构并使其保持标准化(尽可能合理),如果在合理的范围内,数据结构的不一致性可以容忍并在松散耦合的基础上修复,如果可以使用代码生成,以便程序在运行时不处理几乎没有变化的信息,并且在编译之前就可以处理,那么源代码将更小,易于开发和高效。
这里是a small example,通过一系列步骤,合理设计的程序通过消除数据结构和使用代码生成,缩小了四倍,并且速度提高了40倍。
答案 7 :(得分:1)
我没有注意到我的OpenGL项目的大小和性能之间存在很大的相关性,但是我从未真正开展过一个庞大的项目。编写高效的代码更重要。你想做什么?为您的应用程序提供额外的性能提升有多重要?专注于编写好的代码,你应该没事。尝试将模板作为学习体验,确保定期提交,以便随时返回。
答案 8 :(得分:1)
实际上,整体执行通常取决于您使用的算法。
在可执行文件大小的情况下,您会发现速度与CPU指令高速缓存大小和给定高速缓存行的长度有关。当您将缓存级别分解到下一个较低缓存时,您会注意到更长的延迟。但是,编译器的工作是优化和排列代码,因此通常会线性执行(如果可能)。
除非您希望软件仅在一台计算机上运行,否则不应调整到特定的CPU。为了在一般情况下获得最大速度,良好的设计,良好的算法选择和良好的编译器调整将比可执行的大小更多。
答案 9 :(得分:-1)
Bloat与您的程序同时执行的程度与可执行文件的大小有关。
一个简单而直接的例子就是:假设我有一个程序可以返回前1000万个素数。我可以使用函数计算,或者我可以将该列表硬编码为字符串并打印该字符串。后一个程序要大得多,但产生该列表所需的资源要少得多。
'Bloated'只是意味着它从其他程序中夺走了大量资源,因为它会将过多的进程和线程强加到内存中。通常你的操作系统只是分配这些进程,它也可能意味着它“一直在计算”,因此,一个非常小的程序,需要花费数天来计算一个非常大的素数列表实际上是一个'膨胀',因为它计算所有时间。