使用FASTBuild加速Unreal Engine编译

前言

  • 这篇文章主要介绍如何使用FASTBuild分布式编译Unreal, 为什么以及如何修改FASTBuild源码. 编译的引擎版本为4.26.2, 但是修改方法应该也适用于后续的版本.
  • 我在Github仓库上传了修改后的源码, 按照 Readme 中记录的方法应该能够完成编译核使用.
  • 也可以按照[分布式编译FASTBuild]记录的方法, 直接配置和使用Unreal官方修改完成后的FASTBuild.

FASTBuild 原理简介

这个部分将会依据个人理解简单地介绍 FASTBuild 进行分布式编译的原理, 部分术语可能与官方不一致.

当我们编译大型项目的时候, 会有很多独立的编译单元(unit, 或者可以简单地理解为很多个cpp文件). 如果我们只用一台16核的机器, 同一时间最多只能够有16个unit被编译; 但是如果我们将 unit 发送到不同的机器上, 那么同一时间就可以编译 5 倍, 甚至 10 倍的编译单元. FASTBuild 正是通过使用多台机器同时编译多个 unit, 从而达到加速编译的目的.

FASTBuild架构

使用FASTBuild加速Unreal Engine编译

FASTBuild 系统中, 可执行文件分为FBuildFBuildWorker. 运行FBuild的机器(本地机器)则负责发出分布式编译的任务, 将编译单元发送给不同的主机. 而运行了 FBuildWorker 的主机(远程机器)负责执行编译任务.

为得知所有可以参与编译的 Worker, FASTBuild 需要一个所有机器都有读写权限的网络位置FASTBUILD_BROKERAGE_PATH. 远程机器在该位置下创建一个文件用于表示本机在线, 并且可以执行编译任务; 本地机器通过读取该位置下的所有文件, 获取所有远程主机, 并将编译单元发送至不同的Worker.

分布式编译

FASTBuild使用.bff文件指定编译操作, 其作用与CMakeLists.txt, Makefile类似. 不同的是.bff文件还用于保证编译环境的一致. .bff文件使用一种FASTBuild自带的语法, 并在其Compiler()等函数中定义了编译环境. 下面是UE源码相应的FBuild.bff, 可以看见其中包含了编译用到的exedll文件.

Compiler('UECompiler') 
{
	.Root = 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\bin\HostX64\x64'
	.CLFilterRoot = 'C:\UnrealEngine\Engine\Build\Windows\cl-filter'
	.Executable = '$CLFilterRoot$\cl-filter.exe'
	.ExtraFiles =
	{
		'$Root$/cl.exe'
		'$Root$/c1.dll'
		'$Root$/c1xx.dll'
		'$Root$/c2.dll'
		'$Root$/msvcp140_atomic_wait.dll'
		'$Root$/vcruntime140.dll'
		'$Root$/vcruntime140_1.dll'
		'$Root$/1033/mspft140ui.dll'
		'$CLFilterRoot$/1033/clui.dll'
		'$Root$/mspdbsrv.exe'
		'$Root$/mspdbcore.dll'
		'$Root$/mspft140.dll'
		'$Root$/msobj140.dll'
		'$Root$/mspdb140.dll'
		'$Root$/msvcp140.dll'
		'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC/Redist/MSVC\14.28.29910/x64\Microsoft.VC142.CRT/vccorlib140.dll'
		'$Root$/tbbmalloc.dll'
	}
	.CompilerFamily = 'msvc'
}

完成编译环境的定义后, FASTBuild 会根据.bff文件, 将编译依赖发送至所有主机上, 保证了一致的编译环境.

Cache

FASTBuild 提供了缓存功能, 能够将编译结果缓存至FASTBUILD_CACHE_PATH中. 这样下次编译的时候, 就可以直接从缓存中获取编译结果. 另外, 如果缓存位置为网络位置, 局域网中的其他人也能够使用缓存. 下面是 FASTBuild 作者对 Cache 的测试结果.
使用FASTBuild加速Unreal Engine编译

网络

由于编译时需要大量地发送和接收文件, 因此网络很容易成为编译时的性能瓶颈. FASTBuild 作者推荐在 1000M 的局域网下进行分布式编译, 个人测试 100M 也勉强凑合. 当然, 如果你所在的网络并不是局域网, 那么就需要慎重考虑了, 因为这很有可能会导致编译减速…

准备工作

  • 主机: 需要同一局域网内的多台主机, 虽然FASTBuild文档上提到远程机器不需要配置编译环境, 但是个人实际使用时还是为所有机器安装了Visual Studio. 另外, 如果需要编译shader, 请在所有机器上安装DirectX.
  • FASTBuild源码: 前往Github下载, 教程中使用版本为v1.04. 你也可以尝试我修改后的版本, 这个版本完成了后面提到的源码修改, 只需要自行编译一次即可使用.
  • FASTBuild可执行文件: 前往FASTBuild官网下载.
  • FASTBuild Monitor: 前往Github下载, 下载完成后注意只保留FBDashboard.exe, FBDashboard.exe.config.

环境变量

  • FASTBUILD_BROKERAGE_PATH: 网络位置, 要求所有远程机器以及本地机器都具有读写权限.
  • FASTBUILD_CACHE_PATH: 网络位置或者本机位置均可, 用于缓存编译结果.
  • PATH: 将FASTBuild可执行文件加入至系统环境变量中.

编译FASTBuild

由于后续需要修改 FASTBuild 源码, 我们首先尝试编译FASTBuild, 这里以 Windows 和 VS2019 为例.

首先是版本号的获取方法:

  • VS: 这里只提供2019的示例, C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Redist\MSVC路径下14.xx.xxxxx即为版本号. VS2017可能路径有些许差异.
  • Windows SDK: C:\Program Files (x86)\Windows Kits\10\bin路径下10.0.xxxxx.x即为版本号. 通常会有多个, 建议选择数字最大/最新的一个.
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CuiuNLsy-1622184017954)(FASTBuild/VersionNumberPath.png)]

然后根据上一步获取的版本号修改bff文件:

  • Visual Studio: 修改External/SDK/VisualStudio/VS2019.bff, 将默认的版本号14.28.29333修改为准备工作中找到的 VS 版本号. 如果你使用的是 Enterprise 或者其他版本的 VS2019, 还应该将文件中的Community字样修改为正确的版本. 如果你使用的是 VS2017, 你应该修改文件VS2017.bff, 也有可能需要修改VisualStudio.bff以强制 FASTBuild 使用正确的编译器.
  • Windows SDK: 修改External/SDK/Windows/Windows10SDK.bff, 将默认的版本号10.0.17763.0修改为准备工作中找到的 Windows 版本号.

完成修改后进入Code目录, 执行命令FBuild.exe All-x64-Release -dist -clean, 应当能够看见 FASTBuild 成功编译.

分布式编译FASTBuild

注意事项:

  • 如果你的主机在局域网不同的子网中, 这一步可能失败. 可以先跳过这一步, 完成源代码的修改后再继续.
  • 请确保FBuild.exeFBuildWorker.exe的版本一致, 如果使用自行编译的FBuildWorker, FBuild也应该使用自己编译的版本.

配置本地主机

本地主机执行分布式编译需要:

  • .bff文件: FASTBuild 源码中已包含, 在[编译FASTBuild]中已经完成了版本号修改.
  • FBuild.exe: 在[环境变量]中已经添加至系统环境变量
  • 环境变量FASTBUILD_BROKERAGE_PATH: 用于获取 Worker 的网络位置, 在[环境变量]中已经完成了添加

配置远程主机

  • 环境变量FASTBUILD_BROKERAGE_PATH: 添加这个环境变量, 并且要保证所有机器上的FASTBUILD_BROKERAGE_PATH一致.
  • FBuildWorker.exe: 在远程主机中直接运行即可.
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4zxpVFtT-1622184017955)(FASTBuild/FBuildWorker.png)]

分布式编译

完成配置后, 进入Code/目录, 继续执行FBuild.exe All-x64-Release -dist -clean, 应当能看见:

  • x workers found in '...', 表明了当前有多少个可用的Worker, 数量应当与远程主机数量一致.
  • Obj: ... <LOCAL>, 表明了编译任务在本机执行, 也有可能LOCAL被替换为远程主机, 这证明已经完成分布式编译了.
    使用FASTBuild加速Unreal Engine编译

使用FASTBuild分布式编译Unreal Engine源码

修改IP地址

原始的 FASTBuild 默认通过主机名称寻找远程主机, 这里修改为使用主机IP地址.

代码修改比较简单, 可以参考这次Commit.

适应Unreal官方FASTbuild.cs

Unreal 官方修改了 FATSBuild, 并以二进制可执行文件的方式放在了引擎源码目录Engine/Extras/ThirdPartyNotUE/FASTBuild中, 另外还提供了FASTBuild.cs用于生成bff文件. 官方修改的思路是在bff文件中, 为Unreal编译器添加额外的Compiler. Engine/Extras/ThirdPartyNotUE/FASTBuild/fbuild_0.99.diff中记录了修改的内容, 需要参考该文件修改 FASTBuild 源码. 也可以参考这次Commit.

压缩编译结果

根据《腾讯游戏开发精粹》, 此阶段的性能瓶颈应当出现在网络部分, 这是因为FASTBuild只压缩编译单元, 没有压缩编译结果. 所以这里对编译结果进行压缩, 然后再传输和解压.

修改的内容不多, 可以参考这次Commit.

分布式编译 Unreal 源码

  • 这时候直接编译应当能够在FASTBuild Dashboard中看到编译单元被分配到不同的机器上.
  • 建议检查源码Engine/Intermediate/Build/fbuild.bff, 注意检查.CachePath, .BrokeragePath是否正确, 以及.Extrafiles中所有文件是否能一一对应. 另外这里给出部分fbuild.bff作为参考, 如果文件没有正确生成, 需要在工程中修改FASTBuild.cs.
Compiler('UEResourceCompiler') 
{
	.Executable = '$WindowsSDKBasePath$/bin/10.0.18362.0/x64/rc.exe'
	.CompilerFamily  = 'custom'
}

Compiler('UECompiler') 
{
	.Root = 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\bin\HostX64\x64'
	.CLFilterRoot = 'C:\UnrealEngine\Engine\Build\Windows\cl-filter'
	.Executable = '$CLFilterRoot$\cl-filter.exe'
	.ExtraFiles =
	{
		'$Root$/cl.exe'
		'$Root$/c1.dll'
		'$Root$/c1xx.dll'
		'$Root$/c2.dll'
		'$Root$/msvcp140_atomic_wait.dll'
		'$Root$/vcruntime140.dll'
		'$Root$/vcruntime140_1.dll'
		'$Root$/1033/mspft140ui.dll'
		'$CLFilterRoot$/1033/clui.dll'
		'$Root$/mspdbsrv.exe'
		'$Root$/mspdbcore.dll'
		'$Root$/mspft140.dll'
		'$Root$/msobj140.dll'
		'$Root$/mspdb140.dll'
		'$Root$/msvcp140.dll'
		'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC/Redist/MSVC\14.28.29910/x64\Microsoft.VC142.CRT/vccorlib140.dll'
		'$Root$/tbbmalloc.dll'
	}
	.CompilerFamily = 'msvc'
}

使用FASTBuild分布式编译项目

  • 如果编译的是自行编译的引擎生成的项目, 那么 FASTBuild 等经过上面的步骤均已正确配置, Unreal会自动使用FASTBuild进行编译. 也可以修改%APPDATA%/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml控制FASTBuild参数. 修改的字段可以参考FASTBuild.cs.
  • 这个阶段可能出现超时问题, 即本机一直在等待远程主机编译, 而远程主机处于空闲状态. 可以使用这些方法: 降低本地编译的核数(在FASTBuild.cs的调用命令中添加-jx, x为核数), 正确配置Cache路径.
// Interesting flags for FASTBuild:
// -nostoponerror, -verbose, -monitor (if FASTBuild Monitor Visual Studio Extension is installed!)
// Yassine: The -clean is to bypass the FASTBuild internal
// dependencies checks (cached in the fdb) as it could create some conflicts with UBT.
// Basically we want FB to stupidly compile what UBT tells it to.
string FBCommandLine	= $"-j16 -monitor -summary {DistArgument} {CacheArgument} {IDEArgument} -clean -config \"{BffFilePath}\" {NoStopOnErrorArgument} {ForceRemoteArgument}";

使用FASTBuild分布式编译Shader

分布式编译Shader需要修改引擎编译工具源码, 基本的思路是将XGE的编译方式修改为FASTBuild. 可以参考这次Commit. 注意我将ShaderCompilerFASTBuild.cppFASTBuild_CachePath硬编码为了F:\\Cache, 你应当修改为正确的路径.

完成这些步骤后, 在%TEMP%\FASTBuild\FASTBuildLog.Log中能够看见Shader编译成功.

可能出现的问题

  • 超时. 个人在使用 100M 局域网, 28核CPU进行本地编译时经常发生超时. 可以尝试降低本地编译所使用的核数, 另外还需要确保Cache路径正确配置, 或者关闭Cache. (图片来源: https://github.com/fastbuild/fastbuild/issues/833)
    使用FASTBuild加速Unreal Engine编译

参考文献

  1. 《腾讯游戏开发精粹》
  2. 保姆式教你使用FASTBuild对UE4进行联机编译
  3. FASTBuild 文档
  4. Shader编译参考
上一篇:c++与unreal学习


下一篇:ubuntu下向163发送邮件