缩短整体.NET汇编的编译时间-增量编译?

时间:2018-10-02 13:13:07

标签: c# .net compilation

我们正在使用.NET Framework 4.6,并且当前具有包含我们大多数应用程序的单片DLL。它包含大约70万行代码。每当我们进行更改时,都会花费一分钟以上的时间进行重新编译。如果可能的话,我们希望加快速度。

一种选择是将整体拆分为多个对等程序集,而这些程序集都不相互依赖(除了用于项目之间共享的代码的单个Common程序集)。因此,如果更改仅限于单个程序集,则编译器只需重新​​编译此较小的DLL。此外,这些对等程序集可以并行编译,从而再次减少了编译时间。

其他人建议.NET Core 2.x的新“增量构建”功能可以从根本上增加整体的构建时间,这意味着编译器仅可以重建整体更改的部分,而忽略了其余的。

是真的吗?或者这不是新的增量构建功能的工作原理吗?

或者有人对改善编译时间的最佳方法有其他建议吗?

3 个答案:

答案 0 :(得分:3)

Incremental builds可能会帮助您使用单个整体DLL。在2.0版中引入了它们,但主要集中于检测依赖项更改-Visual Studio已经针对您的情况进行了此操作。

但是,来自What's New in .NET Core 2.1

  

使用长期运行的SDK构建服务器,这些服务器跨越各个dotnet构建调用。它们消除了每次运行dotnet构建时JIT编译大代码块的需要。

我目前无法精确测试这对于大型项目建设以及小范围的隔离代码更改意味着什么,但我建议您尝试一下。

更新,我试图准确测试这可能意味着什么,但不幸的是,没有70万行项目可用于测试。我使用了this MS example project并运行了2个副本-一个副本合并为一个dll,另一个拆分为左。我所做的每项测试,样本“整体式”组合项目的运行速度都比较快,但是我认为这些都是无效的结果,因为很难将组合式项目视为“整体式”。

在此示例“整体”中发现了一件有趣的事情:无论是对视图进行小的更改还是对项目进行的大范围更改,我都从未发现新的编译时间之间存在差异。这使我确切地质疑用JIT东西缓存这个新的构建服务器的东西。也许又是因为我的“整体”并不大。

最后,我找不到证据证明您真正的整体项目将在.NET Core 2.1进行更改期间提供更快的构建。我可以看到证明,将其拆分为几个功能项目将可以带来好处。最重要的是,未更改的项目将不会重新编译。

最后,对于您的情况,您拥有一个整体的DLL,这是问题的主要部分。您和其他人在注释中建议的所有其他事项将大大增加您的构建时间,因为如果不经常更改的某些关键代码区域可以分解为其他项目,则它们不会在每次编译时都重新构建。如果它们不经常更改,甚至可以被拉进nuget包中并托管在私有nuget服务器上。这样一来,即使在全新的开发环境中,它们也不会被编译。

希望这会有所帮助!

答案 1 :(得分:0)

远离单片设计并考虑将属于一起的零件放入单独的组件中是一个好主意-因此,这是我建议您采用的方式。

实现此目标的方法是(在Visual Studio 2015-2017中):

  1. 打开您的解决方案
  2. 对于要添加的每个程序集,在解决方案中创建一个单独的项目(在解决方案上右键单击以打开上下文菜单,然后选择添加->新项目... ,然后选择 Visual C#->类库
  3. 为创建的每个程序集添加对主项目(这是您的整体应用程序)的引用(在引用上右键单击,然后选择添加引用... 并浏览 Projects->解决方案导航器以将其选中)
  4. 右键单击主项目,然后选择 Build Dependencies-> Project Dependencies ... 。在对话框中,确保在下拉菜单中选择了您的主项目。勾选标记您添加的每个程序集。现在,您的主要项目取决于程序集,并且将以正确的顺序进行编译。
  5. 将类从您的主项目移动到装配项目中(剪切并粘贴它们)。如果这会创建任何其他依赖项,请打开 Build Dependencies-> Project Dependencies ... ,这次是在依赖于不同程序集的项目上打勾,并勾选它所依赖的程序集。
  6. 右键单击解决方案,然后选择重建解决方案。。如果您正确完成所有操作,则编译应该会成功。否则,请修复出现的所有错误(例如,由于缺少参考)。检查是否错过了任何using语句来导入名称空间。

从现在开始,仅在装配已更改时才需要装配(右键单击项目,然后选择“ build”)。如果存在任何依赖项,则编译器会按照正确的构建顺序自动对其进行编译。

因为您已经将整体设计分解为较小的程序集,所以它们每个都可以非常快速地进行编译,因此您只需要重新编译那些已更改的程序集。

那些不依赖其他程序集的程序可以并行编译-因此,如果要运行不同的编译器进程,可以考虑创建一个执行此操作的批处理。项目依赖关系对话框可帮助您确定可以并行编译的程序集。

“构建顺序”选项卡显示了它们应以什么顺序运行,“依赖关系”选项卡为每个程序集显示了它依赖的其他程序集。这样,您可以找出是否有可以与其他“分支”并行编译的“分支”(对于“分支”,我并不是指源代码控制的分支,而是您解决方案的分支:想想一棵树,主项目是根-每个分支都由您的解决方案的一系列组装组成,这些组装可以并行编译。)

答案 2 :(得分:0)

我同意分解。但是有很多方法可以分解解决方案。我认为您应该分阶段进行。

第1阶段-现在分解它的最快方法。

获得最快结果的最常用方法:

  1. 所有模型-创建一个模型库项目并将所有模型文件移动到该项目。让您的整体参考模型项目。完全不要更改文件或名称空间。只需将一个项目剪切并粘贴到另一个项目即可。如果有任何模型类是私有的,则将它们设置为内部模型(在项目中查找并替换),然后将InternalsVisibleTo属性添加到整体的装配中。迁移所有模型课程的估计时间:20分钟。

  2. 所有接口-创建一个接口库项目并将所有接口类文件移至该项目。让您的整体参考模型项目。完全不要更改文件或名称空间。只需将一个项目剪切并粘贴到另一个项目即可。如果有任何接口类是私有的,则将它们设置为内部(在项目中查找并替换),然后将InternalsVisibleTo属性添加到您的整体组件中。迁移所有接口类的估计时间:20分钟。

  3. 生成的代码-创建一个GeneratedCode项目。将所有100%生成的代码的类移至该项目。当然,这是一个假设,因为您有70万行代码,所以很多类都是完全生成的。如果没有生成的代码,请跳过此代码。移动所有生成的代码的估计时间:1小时。

  4. 扩展方法-创建一个扩展项目,然后将所有扩展方法类移至该项目。迁移所有模型课程的估计时间:30分钟。

  5. FixedLogic-创建一个固定的逻辑项目。查找所有您很少碰到的类-在一个像他这样大的项目中,可能有数百个多年来没有被碰过的类文件。您无缘无故地一次又一次地重新编译这些类。您必须找出那些是。希望您可以在源代码管理中轻松看到这一点。预计时间:2小时以找出源代码管理中的查询。将这些文件移至新项目需要10分钟。

  6. 停留在1个dll进行发布。现在,我假设您有一个构建,部署,安装过程。如果不这样做,请忽略此步骤。如果这样做,而且很复杂,您可能想要将dll添加到现有进程中,因为那样会增加这些任务的时间。因此,请确保所有文件的编译方式仍会导致1个dll。请参阅这篇文章:https://docs.microsoft.com/en-us/dotnet/framework/app-domains/how-to-build-a-multifile-assembly。实施本文的预计时间:4小时。

主要目标:首次构建后,后续构建只会构建有更改的项目,从而大大减少了构建时间。

第2阶段-一种更好的长期方法。

请花一些时间使您的代码散乱起来。仅当您仍然触摸代码时,才执行此操作。

  1. 分析您的项目,并列出代码覆盖的区域或域。这只是文本文件中的一个简单列表。您可能有很多域。

  2. 将类文件分配给那些域,但还将类文件标记为模型,接口,逻辑。

  3. 下次您触摸域中的文件时,请创建三个项目:Domain.Models,Domain.Interfaces和Domain.Logic。将代码移到正确的项目中。

主要目标-关注点分离