【问题标题】:How does the number of classes in an assembly impact performance?程序集中的类数量如何影响性能?
【发布时间】:2012-11-05 06:25:59
【问题描述】:

我正在进行的项目将为大量类生成代码 - 预计会有数百到数千个。 在生成时不知道这些类中有多少会被实际访问。

生成的类可以 (1) 全部存在于单个程序集(或可能是少数程序集)中,该程序集将在脚趾消耗过程开始时加载。

...或 (2) 我可以为 each 类生成单个程序集,就像 Java 将每个类编译为单个 *.class 二进制文件一样,然后提出一种机制来按需加载程序集。

问题:哪种情况会产生更好的(内存和时间)性能?

我的直觉是,对于情况 (1),加载时间和使用的内存与构成单个整体程序集的类的数量成正比。 OTOH,案例 (2) 有其并发症。

如果您知道与加载程序集的内部相关的任何资源,尤其是调用了哪些代码(如果有的话!?)以及分配了哪些内存(为新加载的程序集记账),请分享。

【问题讨论】:

  • 您是否尝试检查性能计数器和分析?乍一看,我会说第一个更好,因为每个程序集的开销在加载和内存消耗方面都不小。

标签: .net performance assemblies .net-assembly


【解决方案1】:

哪种情况会产生更好的(内存和时间)性能

考虑到编译器会为你做很多优化,选项 1 绝对是要走的路。 每个类有一个单独的程序集似乎完全是矫枉过正。不仅如此,您可能会发现加载 1 个大型程序集比加载许多小型程序集更快。

另外,这确实感觉像是过早的优化,我的建议是坚持第一个(理智的)选项,如果您认为需要,稍后将类拆分为单独的程序集。

【讨论】:

    【解决方案2】:

    您正在尝试解决一个不存在的问题,程序集加载在 .NET 中非常进行了高度优化。

    毫无疑问,将一个大型装配体分解为许多较小的装配体是您能做的最糟糕的事情。到目前为止,加载程序集的最大开销是查找文件。这是一个冷启动问题,CLR 加载程序被缓慢的磁盘所困,需要检索和搜索目录条目以定位包含文件内容的磁盘扇区。当可以从文件系统缓存中检索程序集数据时,此问题会在 热启动 时消失。请注意,Java 也不是这样做的,它会将 .class 文件打包到 .jar 中。 .jar 大致相当于一个程序集。

    找到文件后,.NET 使用操作系统工具来降低实际加载程序集数据的成本。它使用内存映射文件。这仅涉及为文件保留虚拟内存,但从文件中读取。

    读取直到稍后才会发生,并且由页面错误完成。任何按需分页虚拟内存操作系统的功能。访问虚拟内存会产生页面错误,操作系统从文件中加载数据并将虚拟内存页面映射到 RAM。之后程序继续,永远不会意识到它被操作系统打断了。产生这些页面错误的将是抖动,它访问程序集中的元数据表以定位方法的 IL。然后它从中生成可执行的机器代码。

    此方案的一个自动好处是,您无需为程序集中但未被使用的代码付费。抖动根本没有理由查看包含 IL 的文件部分,因此它实际上从未被读取。

    请注意这种方案的缺点,第一次使用一个类确实会因为磁盘读取而导致性能命中。这需要以一种或另一种方式支付,在 .NET 中,债务应在最后一刻到期。这就是为什么属性以缓慢着称的原因。

    较大的组件总是比许多较小的组件好。

    【讨论】:

    • 好的,这处理基于时间的性能。内存性能怎么样?特别是。对于在运行时实际上只触及一小部分类的情况。你能估计在定义程序集中每个新的(未使用的)类定义会产生多少内存开销吗?
    • 正如我所指出的,VM 大小与使用的类数无关。一个巨大的程序集将是 10 兆字节。这需要 0.5% 的地址空间。花生。
    • 最好的确定方法是测试 :) 所以这就是我所做的。我创建了一个包含 10.000 个类的巨大程序集(一个生成的巨大源文件,时钟大约 200 MB)。使用 Assembly.Load() 加载 release-build .dll (~45 MB) 所需的时间:0.05 秒。所以这里有确凿的证据证明你是对的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-21
    • 2012-11-16
    • 1970-01-01
    • 1970-01-01
    • 2016-09-26
    • 2022-08-04
    相关资源
    最近更新 更多