提高CI构建时间 (.NET)

24

我们正在使用TeamCity作为CI服务器,开发一个应用框架+插件。

项目细节

  1. 4个Visual Studio解决方案
  2. 约70个项目(并且还在增加)
  3. 当前使用TeamCity运行2个构建:CI和FULL build。

CI - 每次提交时触发。

FULL - 每晚运行。

我想提高这两个构建的性能(尤其是CI构建,因为它需要尽可能快地输出结果)。

通常来说,有哪些可以有效简单地提高构建时间的指南吗?

构建过程仅构建一个.sln文件并运行一些单元测试。

考虑的方向:

  • MSBuild并行化
  • 覆盖CopyFilesToLocal

不确定这些是否适用/会产生性能增益。

我正在寻找更多的方式来提高构建时间(目前需要大约3-4分钟)。

4个回答

14

最小化你的CI构建所需的工作量。

  • 设置工作空间,将不需要的文件夹隐藏起来,以最小化从源代码控制获取文件的数量。如果需要,在源代码中重新组织文件夹结构,使得使用隐藏来排除整个文件夹数据变得容易。

  • 确保CI构建使用增量获取和增量构建。

  • 仅编译需要的解决方案/项目。如果你的库很少更改,你能否预先编译它们并将二进制文件检入源代码控制?如果一个项目目前没有在积极开发,就不要在CI构建中编译它。

  • 每次检入都需要运行单元测试吗?我们运行一个简单的只包含代码的CI构建,而单独的测试构建也是以CI方式运行,但不会超过一小时频率。这可以大大缩短CI构建时间,同时仍然可以在一小时内知道是否已经破坏了任何单元测试。

  • 同样地,不要构建文档、混淆代码、构建安装程序、使用证书签署程序集等,并禁用任何将输出复制到存储区的构建过程。CI构建的目的在于尽快告诉您是否已经破坏了构建,您不需要生成有用的二进制输出。

  • 总体上优化构建 - 合并项目,使用多线程构建,使用几个构建代理,这样CI构建就不必等待其他构建类型完成。只在夜间进行完全构建,这样你的构建服务器就专门用于CI,而你在工作时不会干扰它。保持源文件整洁(删除未使用的代码而不仅仅注释掉它,删除未使用的using/includes等)。

  • 投资更好的构建服务器硬件。如果没有顶级规格的机器,请增加更多的RAM和一个SSD来获得廉价的速度提升。确保你的构建服务器专用于CI构建,并没有用于任何可能减慢速度的其他用途。确保构建服务器和TFS服务器之间的网络是千兆位的。确保你没有在服务器上运行任何反病毒软件,或者至少其计划扫描是在夜间运行的,并且你的构建文件夹在实时扫描排除列表中。

  • 使用 TFS 检入策略,如果 CI 构建失败,停止开发人员检入代码,以便您立即停止并修复故障。


  • 我认为从CI“checkin”构建中删除测试可能会在后期引起问题。虽然我们目前只开始看到单元测试的好处,但我很容易想象开发人员检入代码时破坏了某些测试(然后回家),而每小时执行的进程太晚了,导致夜间的“大型”构建失败等等。 - lysergic-acid
    在提交代码之前,你的所有单元测试都应该已经通过了,否则你怎么知道它是安全的呢?如果一个夜间构建失败了,这难道就是世界末日吗?单个构建失败不应该成为问题,你应该计划好如何应对。 - Jason Williams
    我更喜欢让构建机器告诉我测试是否通过。您会在此之前手动运行所有测试吗? - lysergic-acid
    @Jason 经常出现单元测试在本地可以通过,但在构建代理上仍然失败的情况,因为它们通常是不同的设置。 - Siy Williams
    你所提到的TFS checkin策略是“门禁式检入”。 - The Muffin Man
    不完全正确...门控式签入是一种使用工作组合构建的方式,其中如果构建成功,则自动签入工作组合。但是,还有一种签入策略,即在任何服务器构建失败时都无法签入,这就是我所指的。然而,你说得对,现在已经有了门控式构建,并提供了更好的处理方式。 - Jason Williams

    11

    我正在处理一个超过500个项目的C#应用程序。这些项目同时被编译,且不设置CopyLocal。没有进行单元测试和代码覆盖率的情况下,编译时间约为37分钟。在没有任何代码更改的情况下,增量编译时间为13分钟。

    如果关闭并行编译并将CopyLocal设置为true,则编译时间会超过1小时40分钟。

    我有不同的配置用于本地构建、审核提交构建和服务器构建,并带有部署阶段(夜间构建)。

    以下是我的经验:

    1. 如果你想在不设置CopyLocal为false的情况下并行构建项目,将输出文件复制到一个目录中并不是一个好主意。当多个项目引用相同的程序集并且MSBuild尝试同时将该引用复制到输出文件夹时,我的程序集有时会被锁定。对我非常有帮助的解决方案是为所有引用设置CopyLocal为false,这样我的构建目录大小就降低了10倍(I/O减少了10倍)。我对本地构建和服务器构建有不同的设置方法。对于审核提交构建和完整部署构建也是如此。
    2. 如果启用并行构建,构建速度将更快,快得多。如果你有一台强大的构件服务器,/m:2构建应该比/m:1构建快两倍。这与项目之间的依赖关系无关(如果设置了CopyLocal为false)。
    3. 如果你想要快速增量编译,请减少项目之间的依赖关系。这对于完整构建(CopyLocal false)没有影响。增量编译时间取决于构建树中更改的项目位置。

    是的,MSBuild使用依赖项目的时间戳来确定项目是否需要重新构建。它将输入文件(代码文件、引用的程序集、临时文件等)的时间戳与输出程序集进行比较。如果有变化,你的项目就会被重新编译。尽量减少项目之间的依赖性以最小化重新编译。如果你的更改只涉及项目的“私有”部分,你的输出程序集将发生变化,程序集时间戳将发生变化,所有相关项目也都将被重新构建。你不能做太多改变这种情况。

    运行2次具有诊断性详细信息的构建,而不更改您的代码,并检查是否像我在这里描述的那样“完全构建目标“CoreCompile””。您的项目文件可能存在问题,导致每次重新编译项目。如果您没有更改任何内容,则构建日志不应包含“Building target“CoreCompile”completely”的记录。

    我们的构建服务器是虚拟机,而不是实际的硬件设备。使用虚拟机作为构建服务器并不是一个好主意,但这不是我的决定。

    如果您有多GB的内存,请尝试将其一部分用作内存驱动器。您的构建应该会更快 :)

    SSD驱动器对高I / O每天敏感。这会影响保修。

    希望它能帮助到某人... ;)


    6
    夜间构建不易受缓慢编译时间的影响,因此您应该集中优化检入触发的构建。我们遇到了同样的问题,并找到了以下方法来帮助:
    1. 只构建您更改的内容。这意味着将项目分解成较小的部分。
    2. 在 Debug 或 Release 中构建解决方案(不要同时构建两者)。让夜间构建同时构建两者。
    3. 保持单元测试小而快速,以便向开发人员快速提供结果反馈。
    4. 仅将非关键任务保留到夜间构建中(文档、长时间的自动化测试等)。
    5. 链接所有依赖配置,以便在您的构建成功时它们将随最近的更改一起构建;如果依赖配置失败,则您立即知道更改未与现有代码集成。
    我们试过并行 MSBuild 但我记不清是否为我们提供了更多帮助;可能是因为我们拥有的代理。此外,检出/检入时间对总时间产生了巨大影响,因此可能调整版本控制系统,使其仅检出已更改的文件会有所帮助。

    你能详细说明一下第五点和 VCS 调整吗?我没有完全理解第五点;另外,我如何影响我的 VCS(Git)的检出时间? - lysergic-acid
    1
    您可以使用侧面检出和 teamcity.git.use.local.mirrors 选项来减少 Git 中的结帐时间。如果将此选项设置为 true,则 TeamCity 在每个代理上创建存储库的本地镜像,在每次构建时更新它(由于更新是增量的,因此速度很快),并从此本地镜像克隆,这也很快,因为它在单台机器上完成。此外,在 TeamCity 7.0 中,还有一个选项可以在 Git 中执行浅克隆,这也可以减少结帐时间。 - neverov

    4

    我不能将Copy Local设置为false,因为我想保持项目文件不变(更加清晰)。在使用MSBuild时,有一些方法可以覆盖它。 - lysergic-acid
    关于RAM磁盘 - 不确定它是否会带来更好的结果,我们的CI服务器是一个虚拟机(VMware)。最后,减少项目数量 - 我们目前正在构建一个.SLN文件,不确定如何在不进行重大仓库重新架构的情况下完成它(无论如何都应该完成重构,但需要更多时间才能完成)。 - lysergic-acid

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接