游戏测试 Unity 性能优化系列—加载与资源管理

侑虎科技 · 2021年06月09日 · 2713 次阅读

我们曾在四年前对于 Unity 的主流模块的性能优化知识点逐一做过讲解,俗称 “小白版”。随着这几年引擎本身、硬件设备、制作标准等等的升级,UWA 也不断更新优化规则和方法并持续输出给广大开发者。作为"升级版"的性能优化手册,【Unity 性能优化系列】将力图以浅显易懂的表达,让更多开发者受用。本期就将分享加载与资源管理相关的知识点。

经常会有一些团队询问:为什么我的游戏加载这么缓慢呢,能否做到像 *** 游戏一样秒切场景呢?游戏发烫又是什么原因呢?为了防止大家辛苦做出来的游戏在真机上卡成翔或者秒变暖手宝,我们要充分利用好 UWA 报告中的加载模块和资源管理模块,下文我们将来逐步分析。

一、加载模块中的关注项

这里我们先来科普一些加载相关的关注指标。下图是 Overview 报告中的加载模块的页签,我们看到左边有这么几项重要参数指标:



1、Loading.UpdatePreloading
这是 Unity 引擎最主要的加载函数。该项一般在切换场景时或异步加载资源时开销较大。一般来说,加载资源越多越复杂,则其 Load.UpdatePreloading 的耗时也越大。

该函数优化前,建议先定位其耗时占用瓶颈。通过报告的 CPU 调用堆栈即可查看该函数在运行过程中的详细堆栈走势,对函数的耗时分配一目了然,从而有的放矢的进行优化。



2、Resources.UnloadUnusedAssets
该函数指卸载未使用的资源,开销主要取决于场景中的 Assets 和 Object 的数量,数量越多,则耗时越高。在性能优化时,除了耗时峰值之外,我们还需关注该函数的调用次数。

一般情况下,场景切换过程中, 引擎会自动调用一次,UWA 建议在 10~15 分钟的时候手动调用一次。

同时,研发团队可尝试在游戏运行时,通过 Resources.UnloadAsset 来去除已经确定不再使用的某一资源,该 API 对于去除单一资源的效率很高,同时也可以降低 Resources.UnloadUnusedAssets 统一处理时的压力。

下图为报告中加载相关函数的堆栈信息,在堆栈中 GarbageCollectAssetsProfile 是由于调用了 Resources.UnloadAssetsUnused 导致的,如果此项占用过高,则需要关注是否主动调用 Resrouces.UnloadUnusedAssets 过于频繁。



3、GC.Collect
GC 调用频率主要受堆内存影响,当函数的堆内存分配量越多、越频繁,GC 就会越快到来。所以当我们的 GC.Collect 函数的调用频率较为频繁(如下图所示),特别是随着游戏运行时间增加,越来越频繁时,就需要我们留意是否存在高分配、频繁分配堆内存的函数操作了,这部分就可以借助 GOT Online 的 Mono 模式排查是否有 Mono 分配过快或过高的现象。



4、Instantiate

这里统计的是资源实例化的耗时,当项目的资源越复杂、实例化数量越多,卡顿感就越明显,但这部分往往是被大家容易忽略的,那 UWA 是如何处理好这部分的问题呢?下文我们将结合 UWA 真人真机测试报告中【资源管理】模块来进行具体讲解。


二、资源管理

这里的资源管理讲的是资源的调用频率、耗时等策略,因为影响加载体验的无非两个角度:加载的频率和每加载一次的耗时。在真人真机测试的报告中,我们可以看到【资源管理】标签后,包含以下检测项:

这么多功能,我们要关注哪些细节呢?说下几个核心点:

1、关注耗时较高的加载
无论是 AssetBundle 还是资源加载,耗时较高的都需要重点关注。这里我们打开一个资源加载的页签,可以看到下方是整个运行过程中的资源调用详情,最后一栏是耗时。

在资源具体信息中,勾选某个资源,就可以看到它在运行过程中的调用细节。对应上面的截图,我们可以进一步排查下这个 AssetBundle 加载是否需要那么多耗时。



2、短期时间内调用次数密集的重点关注
无论是 AssetBundle 还是资源加载,都要关注加载的频率。通常对于频繁加载的对象,我们可以通过建立缓存池的方法,先加载一次后将其加入缓存,后续就无须进行加载了。

如下图中,这些频繁加载的 AssetBundle,可能原来有每次 5ms 或者 50ms 的耗时,后面可直接为 0

这里再提下,我们也需要留意一帧内相同资源被多次加载的问题。
如下图,这一帧里调用 5 次,这个是不对的。



3、留意不存在资源

在资源加载的列表中,有的项目会出现【不存在】资源的情况,说明这些资源都是由于不在指定路径下导致加载失败的资源。一般情况下,这类资源是伴随着版本迭代,进行删除/迁移后,没有修改/注释对应的代码导致的。

加载这些【不存在】资源仅导致了一小部分 CPU 开销,但更重要是,排查这些【不存在】可以避免逻辑上的问题导致闪退和卡死等现象。

4、频繁实例化/Destroy
操作次数较高或耗时较高的资源。频繁的 Instantiate 会造成一定的堆内存分配,从而会加快系统调用 GC 的频率。更重要的是,频繁的实例化会造成 CPU 耗时产生一定的峰值,导致游戏的流畅性受到影响,所以这部分也是我们需要关注的。

对于这种频繁实例化的资源,通过缓存池复用实例化次数过多的 GameObject,进而减少 GameObject 实例化的耗时。

5、Activate 和 Deactivate
这个排查方式与实例化是类似的,主要关注调用频率和耗时。

对比 Activate 和 Deactivate 的调用次数,因为如两者相差过大,说明存在无用的 Activate/Deactivate 操作。

例如,某个资源的 Activate 操作次数非常多(如下图中的 Gold_2 和 Gold_4),为什么次数那么高?是否有必要呢?我们可复制该资源名称,在 Deactivate 资源列表中进行搜索查看是否确实需要这么多次状态的激活。


Gold_2 的 Deactivate


Gold_4 的 Deactivate

这说明相差的 1 万多次的 Deactivate 操作都是无意义的。

对于以上这种资源,我们可以通过在 C# 端创建一个特例缓存,记录这个对象的 Active 的状态(True or False),在调用 SetActive 之前,先判断一下当前的状态是否已经是想要切换到的状态,如果不是才调用。这是因为 SetActive 的操作是会从 C# 走到 C++ 层的,所以我们在 C# 进行状态判断可以减少这种跨语言的操作,从而避免不必要的耗时。

6、AssetBunde 驻留优化
之所以关注这个参数,是因为它影响了项目运行过程中的内存占用,要知道 Unity 内存一部分是由 AssetBundle 驻留导致的 Serializedfile 相关的,一般来说我们建议控制的 AssetBundle 资源数量在 1000 以下。考虑到这个指标和项目本身的复杂度有关,所以大家需要自身做些实验,好权衡 CPU 和内存之间的天平。

资源的加载可以使用缓存池的方式来进行优化,AssetBundle 的加载也是类似的。对于同一个 AssetBundle 进行频繁的加载通常是不合理的(如下图所示),对于频繁加载卸载的 AssetBundle,建议将其加入缓存,常驻于内存中。




三、Shader.Parse/CreateGPUProgram

Shader 资源如果解析加载策略不当,也会造成 CPU 开销较大。由于 Shader 的内存占用很小,但是加载的耗时又比较高,所以我们建议在理想情况下是在项目开始运行时就把所有的 Shader 资源全部加载完成,然后缓存。

1、Shader.Parse

该函数的耗时主要是由于 Shader 的加载和解析,通常是由于 Shader 的重复加载导致的,在优化时要看一下具体的 Shader 加载情况,具体可以从以下三点着手:

(1)避免使用 Standard,使用其他 Shader 代替 Standard Shader。注意排查是否因为模型导入而导致 Standard Shader 被加载进入 AssetBundle 中;



(2)解决 Shader 冗余问题,这部分可以结合 Shader 的内存走势查看,如下图所示。



如果大家的 Shader 资源并不是缓存在内存中的,切出场景时则会释放 Shader,切入场景会加载 Shader,导致了大量的重复开销。解决这个问题,只需要把 Shader 进行剥离,通过依赖关系将其做成单独的 AB,然后加载后就缓存住不卸载,那么后续就不需要再对此 Shader 进行加载了。

(3)减少 Shader 的 Keyword。
研发团队可以参考下面的资料:
《一种 Shader 变体收集和打包编译优化的思路》
https://answer.uwa4d.com/question/5da86670e84db43d6efbda72

2、Shader.CreateGPUProgram

该 API 的 CPU 占用是 Shader 第一次渲染时创建 GPU 程序的耗时,其耗时与渲染 Shader 的复杂程度相关。对此,建议研发团队将 Shader 通过 ShaderVariantCollection 进行加载,并在加载后并 Warmup,从而避免 Shader 在游戏运行时产生 Shader.CreateGPUProgram 的耗时。

以上就是加载在优化时需要关注的一些问题,如何操作还需要大家结合项目实际情况,同时结合 UWA 的线上测评服务可以快速地帮助大家定位到性能瓶颈。

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册