【Unity】使用Profiler进行性能分析

1介绍

如果我们游戏运行很慢,卡顿甚至卡死,我们就知道游戏出现了性能问题。在我们尝试修复问题之前,我们首先要知道是什么造成了这种问题。不同的问题需要不同的解决方案。如果我们尝试猜测问题或根据其他项目对游戏进行调整,这会非常浪费时间甚至会使问题变得更加糟糕。

这个时候,我们就需要对问题进行分析。分析是在运行我们游戏的时候对各个方面进行测量。使用 profiling 工具,当我们游戏运行的时候,可以看到屏幕后面发生的事情并且根据这些信息跟踪造成性能问题的原因。通过查看 profiling 工具,我们可以测量我们修改后的结果,这样我们就可以判断我们的修复是否有效。

在这篇文章中,我们将:

  • 使用 Unity 自带的的 Profiler 工具去收集性能差的游戏数据。
  • 分析这些数据并且使用分析的结果去跟踪性能问题。
  • 提供修复这些问题的链接。

让一个游戏运行顺畅是一个平衡的过程。在获得理想结果之前,我们可能要对游戏进行好几次的修改和验证。知道如何使用 profiling 工具去分析我们的问题意味着我们能够确定游戏的问题是什么并且知道下一步要怎么做。

2友情提醒

这篇文章会帮助我们跟踪到造成 Unity 游戏运行缓慢、卡顿甚至卡死的位置。如果我们有其它问题,比如崩溃或图像异常,这篇文章可能不会有太大的帮助。如果我们游戏中出现了这篇文章没所提到的一些问题,可以尝试搜索 Unity 手册、Unity 社区或 Unity 解答。

如果我们对 Profiler 窗口或如何使用 Profiler 不熟悉的话,建议先看这篇文章

3对游戏性能的简介

帧率是衡量游戏性能的标准。游戏里面的帧跟动画的帧类似。它只是绘制到屏幕的游戏画面。绘制一帧到屏幕被称为渲染一帧。帧率或帧被渲染的速度以每秒来衡量( FPS )。

现在大多数游戏都是以60 FPS 为目标。通常30 FPS 以上被认为是可以接受的,特别是对于一些对反应速度要求不高的游戏,如解谜或冒险游戏。一些游戏对帧率要求比较高,如 VR ,90 FPS 都会被嫌弃。帧率在30 FPS 以下,玩家体验通常会比较差,图像可能会卡顿、操作起来也很迟钝。然而,不仅仅速度重要,帧率稳定也很重要。帧率发生变对玩家来说是很明显的。不稳定的帧率通常比稳定但是帧率低的游戏更糟糕。

尽管帧率是谈论游戏性能经常提到的,但是要尝试去改善游戏性能的时候考虑渲染一帧所需要的毫秒数会更有用。有两个原因,首先这种一种更精准的测量方法。当我们尝试改善我们游戏性能的时候,每毫秒都能算出我们的目标。其次,帧率的相对变化意味着不同的规模帧率也是不一样的。从60 FPS 到50 FPS 代表处理时间增加了3.3毫秒,但是如果从 30 FPS到 20 FPS代表处理时间增加了16.6毫秒。同样是降低了10 FPS ,但是渲染一帧所花费的时间是截然不同的。

了解普遍帧率渲染一帧所需要花费多少毫秒是非常有帮助的。要找到这个花费的时间,我们应该遵循这个公式1000/[渴望的帧率]。使用这个公式,我们可以知道每秒渲染30 FPS ,那么渲染每帧花费在33.3毫秒以内。一个游戏要运行到60 FPS , 那么渲染每帧花费在16.6毫秒以内。

对于渲染的每一帧,Unity 都必须要执行很多不同的任务。简单来说,Unity 必须更新游戏的状态,拿到游戏的快照并且渲染到屏幕。每帧必须要执行的任务包括读取用户输入、执行脚本、灯光运算。除此之外,还有一些一帧内执行多次的操作,如物理计算。当所有的任务执行的足够快,我们的游戏将会有一个稳定的,可接受的帧率。当所有的任务执行得不够快,会花费更长的时间去渲染,并且帧率会下降。

扫描二维码关注公众号,回复: 15688800 查看本文章

知道哪个任务执行时间长,对如何解决游戏性能问题是至关重要的。一旦我们知道哪个任务在减低帧率,我们可以尝试优化那部分内容。这就是为什么分析如此重要: profiling 工具可以显示在给定的帧中每个任务花费多长时间。

4记录分析数据

为了研究我们的游戏性能,我们必须记录游戏性能不佳的数据。为了获得更加精准的分析数据,我们要打一个测试包( development build )运行在目标硬件上,并且记录分析数据。

如果我们还不熟悉打包和真机调试流程,点击这里查看操作指南。

使用真机收集数据

  • 使用 development build 方式打包,在目标机上运行。
  • 在性能问题出来之前开始记录分析数据。
  • 一旦出现性能问题,点击 Profiler 窗口上方任意位置,暂停游戏并选择一帧。
  • 在 Profiler 窗口的上方,选择显示性能较差的帧。这可能是低于我们要求帧率的“尖峰”或是有代表性的帧。我们可以使用左右按键或前进后退键在帧之间更好的移动。

我们已经获取游戏中性能较差的分析数据。下一步,让我们学习如何分析这些数据。

5分析数据

在得出任何关于游戏性能结论之前,我们必须学习如何阅读和分析显示在 Profiler 窗口的性能数据。我们知道,当 Unity 无法及时的完成渲染所需要的所有任务时,帧率会下降。我们将会使用 Profiler 窗口查看到底执行了什么任务,任务花费多长时间以及按什么顺序执行的。这些信息会帮助会帮助我们找到是哪些部分任务造成渲染时间过长。

最好去学习如何分析而不是学习一系列的步骤。自己理解这些数据更有用,这样当我们遇到了新问题的时候可以自己去研究。即使我们只是学会了在 Unity 解答上搜索,这也是一个伟大的开始。

为了学习如何分析,我们将会使用 CPU 分析器作为例子,这可能是我们在研究帧率问题上用的最多的分析器。

5.1 CPU分析器

当我们在 Profiler 窗口看 CPU 分析器的时候,我们可以看到CPU完成每帧所花费的时间。

我们可以看到时间花费的彩色浪图。不同的颜色代表时间花在渲染操作上,物理计算上等等。那些关键字标明哪些颜色代表哪些任务。

在接下来的截图中,我们可以到这一帧的主要时间花费在渲染操作上。下方的 CPU 时间指示器表明了我们的总的CPU时间在这一帧花费了85.95毫秒。

层次结构图

让我们使用 CPU 分析器的层次结构视图去深挖当前数据并且更精确的查看当前帧哪一个任务花费CPU时间最多。当选中CPU分析器的时候,我们可以在Profiler窗口的下半屏看到当前帧的详细信息。查看Profiler窗口的下半屏,我们在做上方可以使用下拉菜单选择结构视图。这可以让我们看到 CPU 上正在发生的任务的详细信息。

在层次结构图中,点击任何列的列头按该值排序。比如,点击 Time ms 按花费时间最长开始排序,点击 Calls 按当前高亮的帧调用次数最多的函数排序。在以上截图中,我们按照耗时排序,我们可以看到 CPU 最耗时的函数是 Camera.Render 。

如果一个函数名字的左右有小箭头,我们可以展开看到这个函数调用了其他哪些函数和他们的性能影响。 Self ms 列表明这个函数自己的耗时, Time me 列表明这个函数和它调用的其它函数的耗时。

在这种情况下,我们可以看到 Camera.Render 下,最耗时的函数是 Shadows.RenderJob 。即使我们对这个具体的函数还不太了解,但是我们已经有关于我们游戏问题的信息了。我们知道我们的问题跟渲染有关,这个当前最耗时的任务跟是 shaodws 有关。

我们可以在层次结构图做的另一个有用的事是比较我们游戏的帧,这样我们可以明白性能是如何随着时间的变化而变化的。我们使用 CPU 分析器一帧一帧的分析出单个最耗时的函数。当我们点击 CPU 分析器层级结构图上的函数名的时候,函数相关数据会高亮。

比如,我们在层级结构视图点击 Gfx.WaitForPresent ,跟 Gfx.WaitForPresent 相关的渲染函数会高亮显示。

时间线视图

现在让我们使用 CPU 分析器的时间视图学习更多关于我们渲染的问题。时间视图显示了两个东西: CPU 任务执行的顺序和哪个线程负责哪个任务。我们可以在Profiler窗口的下半屏的左上角使用下拉按钮选择时间线视图(那里之前显示的是结构视图选项)

线程可以同时运行多个单独任务。当一个线程执行任务的时,另一个线程可以执行完全独立的任务。 Unity 的渲染进程包含三种类型的线程: main thread 、 render thread 和 worker threads 。了解哪些线程负责哪些任务是非常有帮助的:一旦我们知道了哪个线程执行了的任务最慢,我们就明白应该集中精力去优化那些线程执行的操作。

我们可以放大时间线视图来更仔细的查看单个任务。被其它函数调用的也会显示出来。在这个例子中,我们放大了 Shadows.RenderJob 看到了组成这个任务的单个任务。我们可以看到 Shadows.RenderJob 是在 main thread 调用的。我们同样看到 worker threads 执行了跟 shadows 相关的任务。 WaitingForJob 任务出现在 main thread 上,表明 main thread 正在等待 worker thread 完成任务。从这我们可以得出结论, shadows 相关的渲染操作在 main thread 和 worker threads 花费太长时间了。我们现在知道问题所在了。

5.2 其他分析器

尽管在跟踪与帧率相关的性能问题时,CPU 分析器是最常用的工具,其他分析器也同样非常有用。熟悉其它分析器提供的信息是一个不错的主意。

按照上面的步骤,尝试学习其他几个不同的分析器每帧提供了什么信息。比如,尝试使用渲染分析器,了解不同帧的渲染统计数据是如何变化的。


6确定造成性能问题的原因

既然我们熟悉了在分析器中读取和分析性能数据的过程,我们就可以开始找到造成性能问题的原因了。


6.1 排除垂直同步的影响

垂直同步简称 VSync ,用来匹配游戏帧率与屏幕刷新速度。垂直同步会影响游戏帧率,并且它的影响会显示在Profiler窗口上。如果我们不明确在看什么,还容易认为这是一个性能问题,所以在我们继续研究性能问题之前要学会如何排除掉垂直同步。

在 CPU 分析器中隐藏垂直同步信息

我们可以在 CPU 分析器中选择要隐藏的信息。这可以让我们忽略对当前研究没有帮助的信息。

隐藏垂直同步的步骤如下:

  • 点击 CPU分 析器
  • 在 Profile r窗口的顶部, CPU 分析器区域显示当前关注的数据,点击标记为 VSync 的黄色正方形就可以隐藏垂直同步的信息


在层级结构视图中无视垂直同步信息

没有办法在层级结构视图中隐藏垂直同步信息,但我们知道了它长什么样子,我们就可以无视它。

无论什么时候,我们在层级结构视图中看到WaitForTargetFPS,这意味着我们的游戏在等待垂直同步,我们不需要研究这个函数,忽略它就好。

屏蔽垂直同步

垂直同步不能在所有的平台上都屏蔽:许多(如 IOS )强制使用垂直同步。但如果我们正在为一个不需要强制使用垂直同步的平台开发,我们就可以屏蔽掉垂直同步。点击 Edit -> Project Settings -> Quality ,找到 VSync Count ,下来菜单选中 Don't Sync


6.2 渲染分析器

渲染是造成性能问题的常见原因。尝试修复渲染问题之前,确认我们的游戏是 CPU 密集还是 GPU 密集是很关键的,因为不同的情况,解决方法也不一样。

简单的说, CPU 负责决定绘制什么,而 GPU 负责绘制。如果渲染问题归咎于 CPU 耗时太多,那游戏就属于 CPU 密集,如果渲染问题归咎于 GPU 耗时太多,那游戏就属于 GPU 密集。


辨别我们的游戏是否是 GPU 密集

辨别我们的游戏是否是 GPU 密集最快捷的方法是使用 GPU 分析器。不幸的是,并不是所有的设备或驱动都支持这个分析器。在我们使用 GPU 分析器之前,我们先检测GPU分析器在目标设备上是否可用。

检测GPU分析器在目标设备上是否可用,我们应该执行以下步骤:

  • 在 Profiler 窗口左上角,选择 Add profiler
  • 通过下拉菜单选择 GPU

如果 GPU 分析器不支持使用,我们会在 GPU 正常显示数据的区域看到一条以“GPU Profiling is not supported”开头的信息。

如果没有没有看到这条信息,这意味着 GPU 分析器在目标设备上是支持的。如果 GPU 分析器是可用的,那执行一下步骤就能非常快速辨别出我们游戏是否是 GPU 密集:

  • 点击 GPU 分析器
  • 查看屏幕正中心区域,那里显示了当前选中帧的 CPU 和 GPU 耗时。


    如果 GPU 耗时大于 CPU 耗时,则我们可以确认我们的游戏是 GPU 密集。

如果 GPU 分析器不能在目标设备上使用,我们仍然可以辨别我们的游戏是否是 GPU 密集。我们可以通过观察 CPU 分析器。如果我们看到 CPU 正在等待 GPU 完成任务,这就是意味着我们游戏是 GPU 密集。为了查明是否存在这种情况,我们可以执行以下步骤:

  • 点击选择 CPU 分析器。
  • 检查 Profiler 窗口底部区域显示的当前帧信息和分析器信息。
  • 在区域左上角的下拉菜单中选择层级结构视图。
  • 选择 Time ms 列头,函数耗时按时间排序。

如果函数 Gfx.WaitForPresent 是 CPU 分析器中最耗时的函数,这表明 CPU 正在等待 GPU 。这意味着我们的游戏是 GPU 密集。

解决我们游戏是 GPU 密集时的渲染问题。
如果我们已经确认我们的游戏是 GPU 密集,我们应该阅读这篇文章
辨别我们的游戏是否是 CPU 密集

如果我们还没有确认造成性能问题的原因,现在我们研究基于 CPU 的渲染问题。

  • 点击选择 CPU 分析器。
  • 随着时间的推移,Profiler窗口顶部信息会显示分析数据,检测图像中代表渲染的部分。我们可以通过点击关键字旁边的带颜色的正方形图片显示或隐藏这些数据。

如果慢帧的大部分时间被渲染占用了,这意味着渲染可能是造成我们问题的原因。我们可以按以下步骤继续挖掘来确认:

  • 点击选择 CPU 分析器。
  • 检测 Profiler 窗口显示的当前帧信息和分析器信息。
  • 在分析数据区域左上角下拉菜单中选择层级结构。
  • 选择列头 Time ms ,按函数耗时排序。
  • 点击选择最顶部的函数。

如果选中的是一个渲染函数,CPU 分析器图像将会高亮显示 Rendering 图像。如果是这种情况,这意味着渲染相关的操作是造成我们性能问题的原因,也可以确认我们的游戏是 CPU 密集。留意函数名和是哪个线程执行这个函数。当我们尝试解决问题的时候,这些信息是很有用的。

解决我们的游戏是 CPU 密集时的渲染问题。
如果我们已经确认我们的游戏是 CPU 密集的渲染问题时,我们应该阅读这篇文章

6.3垃圾回收分析器

接下来,我们检查垃圾回收是否会造成瓶颈。垃圾回收是 Unity 自动内存管理的特性,这可能是一个缓慢的操作。

  • 点击选择 CPU 分析器。
  • 在 Profiler 窗口,可以可以发黄色本部分代表垃圾回收,可以名字旁边的小色块显示或取消垃圾回收的数据采集。注意,你可以拖拽你感兴趣的部分的名称,重新排序它们,在下面的截图中,我们拖拽 GarbageCollector 到顶部,并且点击关掉了其他方面的数据。

如果慢帧的大部分时间被垃圾回收占用了,这指明了我们有垃圾回收过度的问题。我们可以深入研究以确认问题。

  • 点击选择CPU分析器,检测Profiler窗口底部显示的当前帧相信信息。
  • 底部区域左上角下拉按钮选择层次结构视图。
  • 选择 Time ms 列头,以函数耗时排序。

如果 GC.Collect() 函数出现,并且耗时比较多,我们就可以确认我们的游戏有垃圾回收问题。

解决垃圾回收问题

如果我们确认我们的额游戏有垃圾回收问题,我们应该阅读这篇文章

6.4 物理分析器

如果我们排除了渲染和垃圾会回收问题,我们检查负责的物理计算是否是造成我们性能问题的原因。

  • 点击选择 CPU 分析器。
  • 在 Profiler 窗口显示数据的顶部,检测代表 Physics 的图像(橙色图像)。我们通过点击名字旁边的带颜色正方形来显示或隐藏图像。

如果慢帧的大部分时间被物理占用,那么可以确认物理计算是造成我们问题的原因。我们可以进一步研究以确认问题:

  • 点击选中 CPU 分析器,检测 Profiler 下方区域显示的当前帧详细信息。
  • 在底部区域左上角的下拉菜单选择层级结构视图。
  • 选择 Time ms 列头,按函数耗时排序。
  • 点击选择顶部的函数。

如果选中的是一个物理函数,CPU 分析器图像将会高亮显示 Physics 图像。如果是这种情况,我们可以确定造成性能问题的原因和物理计算相关。


解决物理问题

如果我们确认我们的问题是物理引起,以下资料会有帮助:

6.5 运行缓慢的脚本

现在我们检测缓慢的或过度复杂的脚本是否是造成性能问题的原因。脚本,这里将的是非 Unity 引擎代码。这些脚本通常是我们自己写的,或者是第三方插件引入的。

  • 点击选择 CPU 分析器
  • 在 Profiler 窗口显示数据的顶部,检测代表Script的图像,我们可以通过点击关键字旁边的颜色方块显示或隐藏图像数据。

如果慢帧大部分时间被scripts占用,那可以确认开发者写的脚本是造成问题的原因。我们可以继续研究确认问题:

  • 点击选中CPU分析器,检测Profiler窗口下方的当前帧详细数据。
  • 在底部区域左上角的下拉菜单选择层级结构视图。
  • 选择 Time ms 列头,按函数耗时排序。
  • 点击选择顶部的函数。

如果是自己写的脚本,CPU 分析器图像将会高亮显示 Scripts 图像。这种情况下,我们可以确认造成性能问题的原因与我们写的脚本有关。

请注意,上面有一种特殊情况:当我们游戏包含渲染相关的函数,如 Image Effects 脚本或 OnWillRenderObject 或 OnPreCull 函数,这些将会出现在渲染分析器而不是脚本分析器。

尽管起初有点小混乱,但是平常使用层级结构视图和时间线视图检测代码的时候,也能够跟踪到相关的代码。


解决缓慢代码问题

如果我们确定自己写的脚本是造成性能问题的原因,这里有一些简单的技巧可以改善性能。下面是一个关于代码优化的资源:


7其他造成性能问题的原因

虽然我们已经讨论了性能问题最常见的四个原因,但是我们游戏可能有一些这里没有提到的性能问题。这种情况下,我们应该用以上的一些方法来收集数据,研究 CPU 分析器并且找到造成问题的函数名字。一旦我们知道了函数名字,我们可以通过搜索 Unity 手册、Unity 社区和 Unity 解答来或者这个函数的一些信息和如何减少这些函数消耗的方法。

学习推荐:unity性能分析之Profiler工具的使用_mob60475702a1ff的技术博客_51CTO博客

猜你喜欢

转载自blog.csdn.net/qq_38721111/article/details/126752698
今日推荐