由于性能分析比预期的要长一些,我把它放到了它自己的单独答案中。我将接受彼得的官方回答,尽管它缺乏测量,因为它最有助于激励我自己进行测量,并且因为它给了我最大的灵感来了解可能值得测量的东西。
分析:
目前提到的具体缺点似乎都集中在一种另一种的性能上,但缺少实际的定量数据,我对以下方面进行了一些测量:
- 是时候在 IDE 中加载解决方案了
- 是时候在 IDE 中编译了
- 程序集加载时间(应用程序加载所需的时间)
- 丢失的代码优化(算法运行所需的时间)
这种分析完全忽略了一些人在回答中提到的“设计质量”,因为我不认为质量是这种权衡中的一个变量。我假设开发人员将首先让他们的实现以获得最佳设计的愿望为指导。这里的权衡是,为了(某种程度的)性能,是否值得将功能聚合到比设计严格要求的更大的组件中。
应用程序结构:
我构建的应用程序有点抽象,因为我需要大量的解决方案和项目来测试,所以我写了一些代码来为我生成它们。
该应用程序包含 1000 个类,分为 200 组,每组 5 个类相互继承。类被命名为 Axxx、Bxxx、Cxxx、Dxxx 和 Exxx。类 A 是完全抽象的,BD 是部分抽象的,分别覆盖 A 的方法之一,而 E 是具体的。这些方法的实现使得对 E 实例的一个方法的调用将在层次结构链上执行多个调用。所有方法体都足够简单,理论上它们都应该是内联的。
这些类以 8 种不同配置沿 2 个维度分布在组件中:
- 组件数量:10、20、50、100
- 切割方向:跨越继承层次(AE 从来都不是在同一个程序集中),沿着继承层次
测量结果并非都是精确测量的;有些是通过秒表完成的,误差较大。进行的测量是:
- 在VS2008(秒表)中打开解决方案
- 编译解决方案(秒表)
- 在 IDE 中:开始和第一行代码执行之间的时间(秒表)
- 在 IDE 中:是时候为 IDE 中的 200 个组中的每一个实例化一个 Exxx(在代码中)
- 在 IDE 中:在 IDE 中对每个 Exxx 执行 100,000 次调用的时间(在代码中)
- 最后三个“在 IDE 中”测量,但来自使用“发布”构建的提示
结果:
在VS2008中打开解决方案
----- in the IDE ------ ----- from prompt -----
Cut Asm# Open Compile Start new() Execute Start new() Execute
Across 10 ~1s ~2-3s - 0.150 17.022 - 0.139 13.909
20 ~1s ~6s - 0.152 17.753 - 0.132 13.997
50 ~3s 15s ~0.3s 0.153 17.119 0.2s 0.131 14.481
100 ~6s 37s ~0.5s 0.150 18.041 0.3s 0.132 14.478
Along 10 ~1s ~2-3s - 0.155 17.967 - 0.067 13.297
20 ~1s ~4s - 0.145 17.318 - 0.065 13.268
50 ~3s 12s ~0.2s 0.146 17.888 0.2s 0.067 13.391
100 ~6s 29s ~0.5s 0.149 17.990 0.3s 0.067 13.415
观察:
- 装配的数量(但不是切割方向)似乎对打开解决方案所需的时间具有大致线性的影响。这并不让我感到惊讶。
- 在大约 6 秒时,打开解决方案所需的时间在我看来并不是限制程序集数量的论据。(我没有衡量关联源代码控制是否对这个时间有重大影响)。
- 在这个测量中,编译时间比线性增加一点。我想这大部分是由于编译的每个程序集开销,而不是程序集间符号解析。我希望不那么琐碎的组件沿着这个轴更好地扩展。即便如此,我个人并不认为 30 秒的编译时间是反对拆分的理由,尤其是当注意到大多数时候只有一些程序集需要重新编译时。
- 启动时间似乎几乎无法测量,但明显增加。应用程序所做的第一件事是向控制台输出一行,“开始”时间是该行从执行开始到出现的时间(注意这些是估计值,因为即使在最坏的情况下也无法准确测量) .
- 有趣的是,IDE 程序集的外部加载似乎(非常轻微)比 IDE 内部更有效。这可能与附加调试器或类似的工作有关。
- 另请注意,在最坏的情况下,在 IDE 之外重新启动应用程序会进一步缩短启动时间。可能存在启动 0.3s 无法接受的情况,但我无法想象这在很多地方会很重要。
- 无论程序集拆分如何,IDE 内的初始化和执行时间都是可靠的;这可能是因为它需要调试,使其更容易跨程序集解析符号。
- 在 IDE 之外,这种稳定性继续存在,但需要注意的是……程序集的数量与执行无关,但是当跨越继承层次结构时,执行时间比沿着. 请注意,差异对我来说似乎太小而无法系统化;可能需要额外的时间来弄清楚如何进行相同的优化......坦率地说,虽然我可以进一步调查,但差异是如此之小,以至于我不想太担心。
因此,从这一切看来,更多程序集的负担主要由开发人员承担,然后主要以编译时间的形式出现。正如我已经说过的,这些项目非常简单,以至于每个项目的编译时间都远少于一秒,导致每个程序集的编译开销占主导地位。我想,跨大量程序集的亚秒级程序集编译强烈表明这些程序集已被进一步拆分,超出了合理范围。此外,当使用预编译程序集时,开发人员反对拆分(编译时间)的主要论点也将消失。
在这些测量中,我几乎看不到任何反对为了运行时性能而拆分成更小的程序集的证据。(在某种程度上)唯一需要注意的是尽可能避免跨越继承;我想大多数理智的设计无论如何都会限制这一点,因为继承通常只会发生在功能区域内,而这通常会在单个程序集中结束。