前言
- 这篇文章主要介绍如何使用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 系统中, 可执行文件分为FBuild
和FBuildWorker
. 运行FBuild
的机器(本地机器)则负责发出分布式编译的任务, 将编译单元发送给不同的主机. 而运行了 FBuildWorker
的主机(远程机器)负责执行编译任务.
为得知所有可以参与编译的 Worker, FASTBuild 需要一个所有机器都有读写权限的网络位置FASTBUILD_BROKERAGE_PATH
. 远程机器在该位置下创建一个文件用于表示本机在线, 并且可以执行编译任务; 本地机器通过读取该位置下的所有文件, 获取所有远程主机, 并将编译单元发送至不同的Worker.
分布式编译
FASTBuild使用.bff
文件指定编译操作, 其作用与CMakeLists.txt
, Makefile
类似. 不同的是.bff
文件还用于保证编译环境的一致. .bff
文件使用一种FASTBuild自带的语法, 并在其Compiler()
等函数中定义了编译环境. 下面是UE源码相应的FBuild.bff
, 可以看见其中包含了编译用到的exe
和dll
文件.
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 作者推荐在 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.exe
与FBuildWorker.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源码
修改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.cpp
中FASTBuild_CachePath
硬编码为了F:\\Cache
, 你应当修改为正确的路径.
完成这些步骤后, 在%TEMP%\FASTBuild\FASTBuildLog.Log
中能够看见Shader编译成功.
可能出现的问题
- 超时. 个人在使用 100M 局域网, 28核CPU进行本地编译时经常发生超时. 可以尝试降低本地编译所使用的核数, 另外还需要确保Cache路径正确配置, 或者关闭Cache. (图片来源: https://github.com/fastbuild/fastbuild/issues/833)