news 2026/7/26 13:18:36

Unreal对C++做了什么 第 16 章 · 编译加速、Hot Reload 与 Live Coding

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unreal对C++做了什么 第 16 章 · 编译加速、Hot Reload 与 Live Coding

第 16 章 · 编译加速、Hot Reload 与 Live Coding

Unreal 项目动辄百万行 C++,完整编译一次可能要十几分钟甚至更久。引擎和 UBT 用了几种策略把编译时间压下去:Unity Build(合并编译单元)、预编译头(PCH)、以及可选的Include What You Use(IWYU)

另一方面,改几行代码就要重新编译、重启编辑器、重新加载关卡,迭代会非常慢。所以 Unreal 又提供了Hot ReloadLive Coding——在不重启进程的情况下替换已加载的 C++ 代码。

本章讲这三件事:编译加速的原理与代价、Hot Reload 的机制与约束、Live Coding 的替代方案。


16.1 Unity Build:合并编译单元

为什么慢

标准 C++ 的编译模型是"一个 .cpp 一个编译单元"。每个 .cpp 独立编译,会:

  • 重复解析它 include 的所有头文件(可能几十上百个);
  • 产生大量 .obj 文件,链接器要逐个读入并解析符号。

头文件被大量重复解析是 C++ 编译慢的主因之一。改一个被广泛 include 的头文件,会触发一大批 .cpp 重编。

Unity Build 做了什么

Unity Build 把多个 .cpp合并成一个大的 .cpp再编译。例如:

MyModule 原本有: A.cpp, B.cpp, C.cpp, ... , Z.cpp (几十个文件) Unity Build 后变成: MyModule.cpp (里面是 #include "Private/A.cpp" 等,把多个源文件当头文件一样包含进来)

这样:

  • 头文件只在合并后的"大 .cpp"里解析一次(在同一个编译单元内,头文件只需解析一遍);
  • 产生的 .obj 数量大幅减少,链接次数减少;
  • 但任何一个"子 .cpp"的改动都会导致整个 Unity 文件重编,所以增量编译时,改一个文件可能触发整个模块重编。

引擎通常会在"编译时间"和"增量编译时间"之间做权衡,例如每个模块只合并成几个 Unity 文件,而不是一个巨大的文件。

代价与陷阱

当多个"子 .cpp"被包含进同一个编译单元时,它们共享同一个翻译单元作用域,会带来:

1) 匿名命名空间中的符号冲突

// A.cppnamespace{intHelper(){return1;}}// B.cppnamespace{intHelper(){return2;}// 与 A.cpp 的 Helper 冲突!}

在单独编译时,两个匿名命名空间彼此不可见。合并后它们在同一个编译单元,两个Helper冲突。解决:给匿名命名空间里的符号起唯一名字,或改成 static 函数并确保名字不同。

2) static 变量同名

// A.cppstaticints_Counter=0;// B.cppstaticints_Counter=0;// 合并后重复定义

解决:用更具体的名字(如s_ACounters_BCounter),或放在匿名命名空间里并保证名字唯一。

3) using namespace 污染

某个 .cpp 里写了using namespace std;,在单独编译时只影响该文件。合并后会影响整个 Unity 里的所有"子 .cpp"。解决:避免在头文件里 using namespace;在 .cpp 里尽量在局部使用,或不用。

如何为单个文件禁用 Unity Build

如果某个 .cpp 必须单独编译(例如上面冲突无法轻易解决),可以在 .Build.cs 里排除它:

// 在 ModuleRules 中bUseUnityBuild=true;// 默认// 若要排除某个文件,通常通过命名约定或 Unreal 的 list 文件配置,具体查当前版本文档

不同版本引擎的配置方式可能略有不同,有的通过特殊文件名(如*.NoUnity.cpp)排除,有的在 .Build.cs 里用 API 指定。


16.2 预编译头(PCH)

预编译头的思路是:把一坨常用的、几乎每个 .cpp 都会 include 的头文件预先编译成二进制形式(PCH),后续每个 .cpp 编译时先加载这份 PCH,再只编译自己特有的部分。

Unreal 里常见的是每个模块有一个 PCH,例如MyModule.cpp作为 PCH 的"宿主"(先被编译,生成 PCH),里面 include 了CoreMinimal.h、本模块的 Public 头文件等。其他 .cpp 第一行是:

#include"MyModule.h"// 或引擎规定的 PCH 头文件

这样这些 .cpp 不需要重复解析 Engine、Core、CoreUObject 等一大串头文件,编译会快很多。

代价:PCH 本身要维护。往 PCH 里加一个很少用到的头文件,会让所有用这个 PCH 的 .cpp 都变慢一点。所以 PCH 里只放"几乎所有 .cpp 都需要"的头文件。


16.3 Include What You Use(IWYU)

传统 Unreal 风格是每个 .cpp 先 include 一个巨大的 PCH(或 CoreMinimal.h),再 include 自己需要的少量头文件。这样简单,但 PCH 会很大,任何 PCH 的变动都会影响很多文件。

IWYU的思路是:每个 .cpp 只 include真正用到的头文件,不依赖一个巨大的公共 PCH。这样:

  • 改一个头文件时,只有真正 include 它的 .cpp 需要重编;
  • 增量编译更细粒度,编译时间更可控。

迁移到 IWYU 需要:

  • 清理每个 .cpp 的 include,去掉未使用的,补上直接依赖的(而不是依赖"别的头文件帮我 include 了");
  • 可能调整 PCH 策略(例如不用模块级大 PCH,或缩小 PCH 内容)。

引擎和部分项目已经支持或部分采用了 IWYU,具体可查当前版本的构建选项。


16.4 Hot Reload

工作原理(概念)

Hot Reload 的大致流程是:

  1. 你修改了某个模块的 C++ 代码并保存;
  2. 编辑器(或已运行的游戏)检测到变更,触发该模块的重新编译;
  3. 编译得到新的 dll;
  4. 运行时加载新 dll,用新代码替换旧代码(通过替换函数指针、虚表等);
  5. 已有的 UObject 实例、CDO 等需要与"新类"对齐——例如 CDO 会按新类重新生成或更新,蓝图等引用要重新绑定到新的 UClass。

这样你可以改逻辑、改属性默认值等,而不必关掉编辑器、重新启动、重新打开关卡。

对代码编写的约束

Hot Reload 不是"任意 C++ 改动都能热更"。它有很多限制:

构造函数

  • 构造函数在 Hot Reload 时可能被重新执行(用于更新 CDO)。所以构造函数里不要做"只做一次"的全局状态修改(如注册到单例、写文件),否则 Hot Reload 一次就执行一次。
  • 避免在构造函数里依赖尚未初始化的全局状态(如 GEngine、GWorld),因为 Hot Reload 时这些可能处于不一致状态。

结构变更

  • 增加/删除 UPROPERTY、改变继承关系等,可能无法正确热更,或导致旧实例与新的 UClass 不匹配。引擎会尽力更新,但复杂变更仍可能要求重启。

静态变量

  • 模块内的静态变量在 dll 被"替换"后,新旧 dll 的静态变量可能并存或行为异常。依赖静态变量的代码在 Hot Reload 后可能出错。

蓝图与 CDO

  • Hot Reload 会尝试更新 CDO 和蓝图对 C++ 类的引用。多数简单改动没问题,但若类结构变化大,可能出现蓝图报错或需要重新保存蓝图。

因此,开发时若发现 Hot Reload 后行为异常,首先尝试完全重启编辑器和游戏再验证,以排除热更带来的状态不一致。


16.5 Live Coding

Live Coding是 Unreal 提供的另一种"不重启进程就应用 C++ 修改"的机制,通常与 Hot Reload 二选一使用。

与 Hot Reload 的差异

  • Hot Reload:重新编译整个模块的 dll,然后替换进程中已加载的 dll,并做 CDO/蓝图等更新。
  • Live Coding:更偏向"补丁"式更新——只重新编译改动的函数或类,把新机器码"打"进正在运行的进程,替换对应函数的入口地址,而不替换整个 dll。

Live Coding 的优点是理论上更快(只编译改动的部分),缺点是:

  • 对"结构级"变更支持更差:例如新增 UPROPERTY、改虚函数表布局,往往无法通过 Live Coding 应用,需要完整编译或 Hot Reload。
  • 依赖编译器和链接器对"增量/补丁"的支持,不同平台和配置下稳定性可能不同。

使用建议

  • 小改动(改函数实现、改默认值、加日志):可优先尝试 Live Coding 或 Hot Reload,看谁更稳定。
  • 大改动(改类布局、增删 UPROPERTY/UFUNCTION、改继承):直接完整编译并重启更稳妥。
  • 若 Hot Reload / Live Coding 后出现诡异 bug,先关掉热更,完整编译并重启再复现,以确认不是热更导致的状态不一致。

16.6 小结:编译与迭代的取舍

手段目的代价/注意
Unity Build减少重复解析头文件、减少 .obj 数量匿名/static 冲突、增量时可能整模块重编
PCH一次解析常用头文件,多文件复用PCH 不宜过大、不宜乱加头文件
IWYU细粒度依赖,改善增量编译需要维护精确的 include
Hot Reload改代码不重启进程构造函数/静态/结构变更有限制
Live Coding更快地应用小改动结构变更支持差,平台差异

理解这些机制,有助于在"编译时间"和"迭代体验"之间做合理预期,并在遇到编译或热更问题时知道该从哪排查。


一句话总结

Unreal 用 Unity Build 和 PCH 降低编译时间,用可选的 IWYU 改善增量编译;用 Hot Reload 或 Live Coding 在运行时应用 C++ 修改,但二者都对构造函数、静态变量和类结构变更有约束,复杂改动仍需完整编译并重启。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 14:34:21

Phi-3 Forest Lab完整指南:Sage Green UI定制+128K上下文调优全流程

Phi-3 Forest Lab完整指南:Sage Green UI定制128K上下文调优全流程 1. 项目概述 "在森林的深处,听见智慧的呼吸。"Phi-3 Forest Lab是一个基于微软Phi-3 Mini 128K Instruct模型构建的极简主义AI对话终端,将前沿AI技术与自然美学…

作者头像 李华
网站建设 2026/7/14 14:34:22

StructBERT-Large语义匹配工具部署:支持LDAP认证+审计日志+敏感词过滤的企业安全增强版

StructBERT-Large语义匹配工具部署:支持LDAP认证审计日志敏感词过滤的企业安全增强版 今天我们来聊聊一个在企业内部特别实用的工具——一个经过安全加固的StructBERT-Large语义匹配工具。如果你正在寻找一个能理解中文句子含义、判断它们是否在说同一件事&#xf…

作者头像 李华
网站建设 2026/7/14 14:34:22

告别Claude Code限额!OpenCode+GLM免费平替方案实测

告别Claude Code限额!OpenCodeGLM免费平替方案实测 1. 为什么需要OpenCode替代方案 作为一名长期使用AI编程助手的开发者,我深刻体会到Claude Code的两个痛点:一是使用限额严格,二是价格昂贵。在连续使用OpenCodeGLM组合一周后&…

作者头像 李华
网站建设 2026/7/14 14:34:24

Qwen3.5-9B开发者必看:Gradio API接口文档与curl/python调用示例

Qwen3.5-9B开发者必看:Gradio API接口文档与curl/python调用示例 1. 模型概述与核心特性 Qwen3.5-9B是阿里云推出的新一代多模态大语言模型,基于创新的混合架构设计,为开发者提供了强大的视觉-语言理解与生成能力。该模型在unslooth平台上以…

作者头像 李华
网站建设 2026/7/14 14:34:23

Git误操作急救指南:从新手避坑到高级救场,一文守住代码生命线

在现代软件工程开发体系中,Git作为分布式版本控制系统的标杆,已成为全球开发者及研发团队的标配工具。它不仅承担着代码迭代轨迹的记录功能,更构建了团队协作的核心流转机制——从单人开发的版本回溯,到多人协作的代码合并、分支管…

作者头像 李华