一. 追踪性能问题

性能评估对大多数软件产品来说是一个系统的过程。首先,我们要确定的是系统性能表现的最高与最低的衡量权值,比如内存可用量,可以接受的CPU占用率,并发用户的数量,等等。然后,我们会针对发布到特定平台的应用程序,进行脚本化测试,同时对收集插桩数据。但完成数据的采集后,我们分析和查找其中的性能瓶颈。如果发现了问题,我们需要针对该问题进行真因分析(RCA),通过改变程序配置或者程序代码解决该问题,并重复此过程修复其他问题。

虽然游戏开发是一个非常艺术化的工程,但它也是个技术含量很高的事情,所以仍然需要客观的认真对待。我们应该了解游戏产品的受众对象,才会搞清楚我们的游戏将会在怎样的硬件配置下运行,也才会知晓游戏性能需要满足哪些目标条件。我们可以在运行时对游戏进行性能采样测试,从多个系统(GPU,CPU,内存,物理引擎,渲染管线等等)收集性能指标数据,并我们心中期望的可接受值进行对比。我们使用插桩数据去定位我们程序中的性能瓶颈,使用Unity引擎附带的分析工具,来确定问题的根本来源。最后,根据问题的类型,我们提出一些修复方案,提升游戏的性能表现。

不过,在我们提出修复方案并花时间去修复的前提是,我们要能够证明该性能问题的是确确实实存在的。在项目未进行系统优化之前,没有充足的理由,就去修改重构代码是不明智的选择。一旦我们有足够的证据证明性能问题,下一步就是定位瓶颈点。明白为什么出现此性能问题非常重要。否则我们仅仅凭借无根据的猜想,将会在修复问题上花费更多的时间。如此已来也意味着我们只是治标而不治本,该问题会在以后的工作中以另一种或者以我们还未察觉到的形式来影响性能。

在本章节中,将会有如下内容:

  • 如何使用Unity Profiler获取分析数据
  • 如何分析Profiler数据查找性能瓶颈
  • 如何从技术上定位性能问题的本因并如何避免该问题的发生

    通过对列举出的性能问题的深入了解,你就可以更清晰的学习以后关于性能问题解决方案的相关章节内容。

1.性能分析工具Profiler

Unity Profiler是Unity编辑器中内置的工具,我们通过他提供的可视化的关于Unity3D运行时多个子系统的性能统计报告,很方便快捷的缩小我们的性能问题查找范围。它可提供相关数据的的子系统如下:

  • CPU的使用率
  • 简明或者详细的渲染情况和GPU的信息
  • 运行时内存的分配情况和总的使用量
  • 声源或者声音数据的使用情况
  • 物理引擎(2D 和 3D)的使用情况
  • 网络消息接受发送和处理情况
  • 视频播放的信息
  • 基本或详细的UI性能信息(2017版本中的新功能)
  • 全局光照统计信息(2017版本中的新功能)

通常有两种方式去充分利用一个分析工具:插桩(Instrumentation)和基准测试(BenchMark)。

插桩,可以简单的解释为:通过深入的分析指定目标函数的调用情况,内存分配情况等,生成精细的测试分析图来查找问题内因的一种测试技术。但在性能分析的初期,这并不是一种有效的测试方式,因为改技术本身会带来一部分的性能消耗。

当一个U3D程序处于开发模式(在Build Setting 中设置Development Build),编译器会开启额外的标志位,让程序在运行时候派发特殊的事件,性能分析模块输出log并存储相关信息。这些额外的工作量就会在运行时提高CPU占用率和增加内存占用总量。更糟糕的是,如果在编辑器模式下的Profiler窗口进行分析,为了更细窗口数据,重新渲染窗口以及其他一些相关的后台操作,会造成更多的CPU和内存的占用。这些性能上的消耗并非所有时候都可以忽略,在一些体量比较大 的项目中,开启和关闭调试前后的性能表现有很大的差别。有时候这种差别会带来很明显的问题,比如会出现事件派发事件的错乱,或者在异步编程时候出现资源竞争(Race Condition)。这是我们在运行时分析代码而带来的代价,应该时刻谨记这一点。

在我们过早的去对每一行代码分析之前,对程序做一些更表象的宏观分析是明智的。我们应该收集一些基本的数据,把我们的游戏发布到指定平台在运行时进行一些场景测试( Test Scenarios)。设计一些简单,耗时短的测试用例,比如关卡的某一部分,某一种游戏玩法,去模拟玩家的某一个操作,密切关注一些明显的性能问题。这些问题会严重的影响以后的性能深入的分析。

这种做法通常被称为基准测试。我们最为关心的性能指标为FPS,内存赵勇,CPU / GPU温度, CPU的占用峰值。这些都是决定是否进行性能优化的重要的基本评价项。同时,在玩家能察觉出来的性能问题上花时间,也可以省下我们很多的时间,俗话说,好钢用到刀刃上。

进行完基准测试后表明仍然需要进一步分析的情况下,我们才会去进行插桩测试。在进行基准测试时,一定要确保实在目标平台上运行我们的游戏而得到的真实数据,而并非在编辑器模式下获得的数据,由于编辑器模式下,编辑器的额外消耗会影响数据的真实性,或者隐藏了潜在的资源竞争问题。因此,我们需要在指定硬件平台上对分析工具应用钩子(Hook)。

一些Unity开发者对于在编辑器模式下运行速度比打包成单独应用程序运行要快很多。这种现象在进行比如对音频数据,预设体数据等序列化数据操作时候更为明显。这是因为编辑器模式下,编辑器会缓存之前处理过序列化数据,所以要比单独的程序包能更快的获得数据

接下来让我们揭晓如何使用Unity Profiler连接到指定硬件设备上并获取精确的基准测试数据。

已经熟悉此操作的读者可以直接跳过这一部分进行下面内容的阅读。

1.1 启动调试Profiler

接下来我们会对各种一下环境下运行的游戏如何连接Unity Profiler进行简单的介绍

  • 本地程序实例,包括编辑器模式下运行的或者以独立端运行的
  • WebGL实例,运行在浏览器中
  • IOS设备中运行的程序实例,如 Iphone或者Ipad设备中
  • Android设备中运行的程序实例,Android平板或者手机设备
  • 编辑器模式下的Profiler本身

我们会简明的说明在各个环境中的需要的设置步骤

1.1.1 编辑器或者独立端环境下连接

使用Profiler的唯一途径是在Unity编辑器中启动然后连接到一个运行的程序实例,该实例可以是在编辑器的Play 模式下运行,也可以是运行在本地的或者远程设备上的程序。

可以通过Window->Profiler打开分析器窗口

如果编辑器已经处于运行模式下我们可以立刻在Profiler窗口中立刻看到报告数据

为了在独立客户端中使用Profiler,必须确保在打包之前在Build Setting 中必须勾选Development Build选项和Autoconnect Profiler选项

在Profiler窗口中可以点击Connected-Player来选择是调试编辑器中运行的实例还是本地运行的客户端。

注意在调试单独程序端的时候,切换到Unity编辑器,由于此时程序段处于在后台运行,所以会停止所有的数据收集。

注意,在Unity 5的版本中,Development Build选项的名称是 Use Development Mode, Connected Player选项的名称是Active Profiler。

1.1.2 连接WebGL程序实例

Profiler同样可以用于连接WebGL程序实例,方法是在编译打包程序前勾选Development Build 和Autoconnect Profiler选项。打包后程序将会在系统默认浏览器中打开。这样可以使我们在更加接近于真实的测试情景下观测我们的程序在不同浏览器中的运行情况(虽然需要我们来回切换系统的默认浏览器)。

不幸的是,程序只在第一次从编辑器启动的时候才会和Profiler建立连接。就目前的版本而言(2017)我们无法做到和已经运行在浏览器的WebGL客户端进行调试。虽然由于编辑器的性能消耗会影响我们的基准测试,但我们别无选择。

1.1.3 连接远程Ios设备

调试器同样可以与运行在IOS设备上的远程客户端进行调试,比如Ipad或者Iphone。前提是确保设备连接的wifi和编辑器连接的网络处于同一局域网。

注意,只有当Unity编辑器运行在Apple Mac设备上时候此连接方式才可用

建立连接的步骤如下:

  • 在打包程序之前,确保Development Build 和Autoconnect Profiler选项已经勾选
  • 将Mac设备和Ios设备连接到处于同一个局域网的wifi上
  • 通过数据线连接Mac和Ios设备
  • 像往常一样运行Buld & Run
  • 打开Profiler窗口在Connect Player中心厕设备

你将会看到Ios设备的分析数据出现在Profiler窗口中

Unity引擎内的Profiler模块使用54998-555511范围内的端口去广播分析数据,所以在有防火墙的时候请确保这些端口可以正常通信

如果对于连接Ios分析调试工程有其他疑难问题,可以查阅官方文档:https://docs.unity3d.com/Manual/TroubleShootingIPhone.html

1.1.4 连接远程Android设备

将Android设备连接到Unity Profiler有两种方式:通过WIFI连接或者使用ADB连接。这两种方式在Mac和Pc设备上都可以使用。

使用Wifi连接设备步骤如下:

  • 在打包构建之前请确保Development Build 和Autoconnect Profiler选项勾选
  • 将Android设备和电脑设备连接到同一个局域网的Wifi中
  • 使用USE数据线将Android设备连接到电脑中
  • 像往常一样点击 Build & Run选项构建打包工程
  • 在编辑器中打开Profiler窗口,在Connected Player中选择Android设备

然后程序会进行打包并通过USB连接线推送到设备中,Profiler会通过wifi和设备通信相关的调试信息,这些信息就会在Profiler窗口中出现

第二种方式是使用ADB。ADB是安卓SDK工具包中包含的一套工具。步骤如下:

  • 请先确保按照官方文档https://docs.unity3d.com/Manual/android-sdksetup.html设置了Androi SDK/NDK环境
  • 通过USB数据线连接设备到电脑上
  • 确保Development Build 和Autoconnect Profiler选项勾选
  • 像往常一样点击 Build & Run选项构建打包工程
  • 在编辑器中打开Profiler窗口,在Connected Player中选择Android设备

你将会看到Profiler信息出现在编辑器的Profiler窗口中

如果对于打包并调试Android工程有疑问,可以访问官方文档https://docs.unity3d.com/Manual/TroubleShootingAndroid.html

1.1.5 编辑器调试

我们也可以直接调试编辑器,通常用于分析一个编辑器拓展脚本的性能表现。通过开启Profiler窗口中的Profile Editor选项,同时在Connected Player中选择Editor选项。如下面截图所示:

注意两个选项都必须设置才会起效,如果只是在Connected Player中选择了Editor而没有开始Profile Editor选项,则会Profiler窗口中的性能数据则是在编辑器中处于运行模式下的游戏应用性能数据。

1.2 性能分析窗口

接下来我们介绍Profiler展示在性能分析窗口中的一些重要的特性。 性能分析窗口主要分为四大部分:

  • 分析控制器(Profiler Controls)
  • 分析器时间轴(Timeline View)
  • 分解视图控制器(Breakdown View Controls)
  • 分解视图(Breakdown View)

这几部分的分布如图:

我们将逐一详细说明各部分的作用

1.2.1 分析控制器

如上一截图所示,分析控制器区域包含很多的下拉菜单和按钮用来控制我们想要分析的系统和对该系统数据分析的深度。

1.2.1.1 添加分析器

通常情况下,编辑器会默认在分析器时间轴区域展示Unity引擎中几个重要的子系统。这些子系统的相关数据分布在不同区域中。Add Profiler选项可以让我们添加或者回复被移除的某一特定系统。我们会在下面的分析器时间轴部分揭示所有可添加的子系统。

1.2.1.2 记录

开启Record选项可以让分析器开始记录数据,并在关闭选项前持续记录。注意运行时数据必须在应用处于运行时才会记录。如果程序运行在编辑器中,意味着编辑器需要处于Play模式下并未被暂停。如果程序是单独运行的客户端,以为这该程序窗口必须处于激活状态。如果Profile Editor选项选中,则数据值来源于编辑器本身。

1.2.1.3 深度记录

默认情况下分析器只会记录常见的Unity脚本的函数调用,比如Awake(),Start(),Update(),和FiexedUpdate()。开启Deep Profile选项,将会以更高的插桩等级去重新编译脚本,允许分析器去分析每一个函数的每一次调用。这样会在运行时相比默认情况下有更多的插桩带来的性能消耗,同时为了记录所有的函数调用堆栈,将会有更多的内存消耗。因此因而,在一些大型的工程项目中,深度记录甚至无法进行,或许在记录开始之前Unity已经由于内存耗尽,也可能因为深度记录造成运行的卡顿,导致整个测试毫无意义。

注意,开启深度记录选项需要整个工程的脚本重新编译,分析器才可以工作。所以应该尽量避免频繁的开启关闭该选项。

由于盲目的对所有的函数调用进行分析,所以我们在测试的大部分时间都开启深度记录是很不明智的。当我们使用默认记录深度分析无法找到问题本因,或者我们将指定场景分离出来单独进行性能分析的时候,才建议开启深度记录。如果在大型的工程或场景中不得不进行深一层的分析,但Deep Profie选项又在运行时带来太多的负面效应,其实也是有可行的方案的,我们将会在“针对代码段进行分析“一节进行描述。

1.2.1.4 分析编辑器

Profile Editor选项允许我们针对编辑器本身进行性能分析。这对于分析我们自己开发的编辑器扩展脚本的性能很有用。

记得一定要在Connected Player选中中选择Editor才可以。

1.2.1.5 链接的用户

Connected Player下拉菜单中,我们可以选择我们我们需要调试分析的用户端实例。这个可以是编辑器中的程序,或者单独运行在本地的程序,或者运行在远程设备中的程序。

1.2.1.6 清空

该选项可以立刻清空在时间轴中显示的所有数据

1.2.1.7 加载

Load选项将会打开一个对话框,用来选择之前存储的分析数据文件。

1.2.1.8 保存

Save选项将会把时间轴中的所有数据存储到一个文件中。一个文件只能存储300帧的分析数据。这通常就已经足够保存我们想要的东西了。因为当一个消耗峰值出现后,在该峰值消失在时间轴左边前,我们会有5到10秒的时间去暂停程序并保存相关数据以供以后的研究。任何保存的文件可以通过Load选项在我们想要进一步分析的时候加载进入分析器。

1.2.1.9 目标帧的选择

帧数计数器(Frame Counter)显示了分析器已经分析了多少帧和当前时间轴显示的是哪一帧。有两个按钮可以将当前选中的帧前移或者倒退一帧。Current按钮可以把选中的帧设置为当前最新出现的一帧并保持。这样可以让分解视图中的内容始终显示当前运行时的那一帧数据。

1.2.2 分析器时间轴

基准测试,又称为标杆分析。

results matching ""

    No results matching ""