Unity中的基准化性能分析:如何开始

作为一个Unity开发者,你肯定想要你的用户喜欢你的游戏,并且在各平台直接有一个平滑的体验过渡。我要告诉你,我们已经让基准化性能分析变得简单起来,如果你想要开发着重于性能的游戏或unity工具,继续往下读

原文地址 https://blogs.unity3d.com/2018/09/25/performance-benchmarking-in-unity-how-to-get-started/

本文中,我将讲解通过一组unity提供的工具来让你轻易的收集性能指标并基于它们进行基准化分析:包括 UnityEditor 内置的 UnityTestRunner,unity性能测试扩展工具,unity 性能分析报告。

为什么要在Unity中 进行基准化性能分析

作为一名unity开发人员,你或许会发现你可能处于如下境地:你的工程不久之前还跑的很快很流畅,但是伴随着一些修改,现在场景运行明显变慢,开始掉帧,以及一些其他的性能问题开始不断的涌现出来。追寻哪些改变导致了这些性能问题是非常困难的一件事。

如果你是unity的合作伙伴,你可能想要知道你的SDK ,驱动,平台,packages或其他产品带来的性能上的改变。又或者你希望用你的产品收集不同unity版本的性能数据,但是并不清楚如何进行操作和比较。

以下就是一组通过建立基准化分析来切实的节省时间的例子。接下来,让我来为你展示你将如何收集性能指标,并通过它们创建基准化分析,并且将性能指标的变化可视化的表现出来。


基准化分析流程

下载示例工程

在这一步,我们将关注 UnityPerformanceBenchmark sample performance test 工程的测试代码。
从github下载最新的XRAutomatedTests release.你可以在PerformanceTests文件夹下找到UnityPerformanceBenchmark 工程。

在Unity TestRunner中编写性能测试

UnityPerformanceBenchmark 工程中包含了一些 场景,它们将会在使用 Unity Performance Testing Extension进行性能测试时用到。
第一步我们要做的是,看一下如何使用Unity Performance Testing Extension在Unity Test Runner
中编写性能测试。在开始操作前我们可以先了解一下这些工具的背景信息。

--------下面是要用到的工具的简介,如果知道的可以跳过--------

  • Unity Test Runner

我们将使用Unity Test Runner运行性能测试。Unity Test Runner是Unity Editor内置的测试框架,它可以让你在编辑模式或运行模式下,在你的目标平台上,如Standalone, Android 或 iOS,测试你的代码。如果你对Unity Test Runner不是很了解可以看这里的相关文档Unity Test Runner documentation

  • Unity Performance Testing Extension

Unity Performance Testing Extension是Unity Editor的一个package,提供了一些列的API和test case attributes 来让你在编辑器或播放器(Player)采样和统计 Unity Profile 标签和一些非Porfile的自定义指标。你可以通过以下链接得到更多的相关信息Unity Performance Testing Extension documentation,我们也会在里面看一些样例。
Unity Performance Test Extension 需要unity的版本在2018.1以上。请确保你在2018.1级以上的版本 运行UnityPerformanceBenchmark工程的测试案例,或则其它用到Unity Performance Test Extension的地方。

通过命令行打开示例工程

Unity性能基准(Benchmark)工程继承了UnityTestRunner 的一个特殊接口IPrebuildSetup,我们可以继承一个在UnityTestRunner运行性测试之前的一个(Setup)装载方法。
UnityPerformanceBenchmark的第一件事IPrebuildSetup。Setup 处理命令行参数来查询unity的player build的设置。这让我们可以灵活的在同一个工程中构建性能测试,可以基于是不同的平台,渲染线程模式,显卡API,脚本实现,跨平台设置如立体的渲染路径和虚拟现实SDK。
因此我们需要从命令行来打开UnityPerformanceBenchmark 工程,这样我们可以出入我们在使用UnityTestRunner运行测试时想要的构建参数
样例:在windows运行UnityPerformanceBenchmark 来构建Android 播放器

1 Unity.exe -projectPath
2 C:\XRAutomatedTests-2018.2\PerformanceTests\UnityPerformanceBenchmark
3 -testPlatform Android -buildTarget Android -playergraphicsapi=OpenGLES3 -mtRendering -scriptingbackend=mono

在这里我们从Windows运行Unity并用它来构建了基于 OpenGLES3 的API,多线程渲染,mono 脚本后端编译的 Android 播放器。
样例:在 OSX 平台运行 Unity 来构建 IOS 播放器

1 /Unity -projectPath /XRAutomatedTests-2018.2/PerformanceTests/UnityPerformanceBenchmark
2 -testPlatform iOS -buildTarget iOS -playergraphicsapi=OpenGLES3 -mtRendering -scriptingbackend=mono
3 -appleDeveloperTeamID=
4 -iOSProvisioningProfileID=

在这里我在OSX运行Unity来构建基于 GpenGLES3 API,多线程渲染,mono 脚本后端编译的 IOS 播放器。同时我们还提供两个一个开发团队账号,并且配置了需要部署到一个ios设备的描述信息。
当我们像上面一样通过命令行来运行 unity 时,IPrebuildSetup 使用的命令行参数将保存在内存中。Setup 方法解析并用它来构建播放器。
这种从命令行运行的方式不需要使用UnityTestRunner运行测试,这样做很好的避免了为每一个播放器配置拆分一个工程。
我已经在wiki上详细的介绍了用命令行开启工程测试工程或运行测试时要用到的命令行选项 How to Run the Unity Performance Benchmark Tests. 要想学习更多关于如何在测试工程中解析 player build 的设置,可以看一下 UnityPerformanceBenchmark 测试工程中 Scripts 文件夹下的 RenderPerformancePrebuildStep.cs 文件。

打开 Test Runner 窗口

开启 UnityPerformanceBenchmark 工程后,我们需要打开 Unity 编辑器的 Unity Test Runner 窗口。

  • 2018.1中,菜单栏中依次点击 Window > TestRunner
  • 2018.2中,菜单栏中依次点击 Window > Genreal > TestRunner

Unity Test Runner 窗口打开后效果如下图。


包含测试的Unity Test Runner

这些就是我们的unity 性能测试。我们可以通过点击窗口左上角的运行按钮来运行它们,或点击右上角的Run all in player 按钮在真机或平台上运行。

调试小提示
如果你想调试 IPrebuildSetup.Setup方法中的代码

  1. 在 Visual Studio 中给你的代码设置断点。
  2. 使用 Visual Studio Tool for Unity 扩展来连接到 Unity 编辑器。
  3. 在 Unity Test Runner 窗口中点击 " Run All " 或 " Run Select " 按钮来运行测试。

在你打断点的地方,Visual Studio 会暂停你的程序来方便你进行测试。

Unity 性能测试样例

让我们来一个性能测试样例 ,以便于我们更好的理解它是如何运行的。
样例:在一个 Unity 性能测试中采样 Profiler Markers(描述标签)

[PerformanceUnityTest]
public IEnumerator SpiralFlame_RenderPerformance()
{
    yield return SceneManager
            .LoadSceneAsync(spiralSceneName, LoadSceneMode.Additive);
 
    SetActiveScene(spiralSceneName);
 
    // Instantiate performance test object in scene
    var renderPerformanceTest =     
        SetupPerfTest();
 
    // allow time to settle before taking measurements
    yield return new WaitForSecondsRealtime(SettleTime);
 
    // use ProfilerMarkers API from Performance Test Extension
    using (Measure.ProfilerMarkers(SamplerNames))
    {
 
        // Set CaptureMetrics flag to TRUE; 
        // let's start capturing metrics
        renderPerformanceTest.component.CaptureMetrics = true;
 
        // Run the MonoBehaviour Test
        yield return renderPerformanceTest;
    }
 
    yield return SceneManager.UnloadSceneAsync(spiralSceneName);
}

在这个样例中,我们的测试名称为 SpiralFlame_RenderPerformance。从方法的 描述标签[PerformanceUnityTest]我们得知,这是一个Unity性能测试。
所有 UnityPerformanceBenchmark 测试工程中的测试都遵循我们在此测试方法中看到的规则。

  1. 加载测试场景
  2. 激活场景以便我们的测试方法可以与之互通
  3. 创建一个 DynamicRenderPerformanceMonoBehaviourTest 的类型物体,并把它添加到场景中。(这一步是在 SetupPerfTest 这个泛型方法中做的)
  4. 等待一个常量时间,来让场景在我们采样指标之前,加载并创建完测试物体。
  5. 设置我们基于Performance Test Extension API 的描述标签来进行采样。
  6. 通知性能测试我们已经准备好开始采样指标了
  7. yield turn 测试物体,来采样渲染循环中的性能指标。

我们也可以在 RenderPerformanceMonoBehaviourTestBase 的基类(一个继承自MonoBehaviour的类)中采样自定义指标(不存在于Unity profile markers,帧数统计或运行时间的指标)
样例:在 Monobehaviour 脚本中采样自定义指标

private void Update()
    {
        if (CaptureMetrics)
        {
            FrameCount++;
            SampleFps();
#if ENABLE_VR
            if (XRSettings.enabled)
            {
                SampleGpuTimeLastFrame();
            }
#endif
        }
 
        if (IsMetricsCaptured)
        {
            EndMetricCapture();
        }
    }
private void SampleFps()
{
    Fps = GetFps();
    Measure.Custom(FpsSg, Fps);
    startFrameCount = Time.renderedFrameCount;
}
private void SampleGpuTimeLastFrame()
{
    var gpuTimeLastFrame = GetGpuTimeLastFrame();
    Measure.Custom(GpuTimeLastFrameSg, gpuTimeLastFrame * 1000);
}
public void EndMetricCapture()
{
    CaptureMetrics = false;
#if UNITY_ANALYTICS && UNITY_2018_2_OR_NEWER
    Measure.Custom(startupTimeSg, appStartupTime);
#endif
}

在上面这个样例中,我们采样了FPS(每秒帧数、帧率),GpuTimeLastFrame(开启XR的情况下)和应用开始时间(在UnityAnalytics 启用并且我们运行在Unity 2018.2或更新的版本的情况下,也就是要说有我们要用到的API)

IsTestFinished Property (测试是否结束 属性)

在最后,我们留意到,RenderPerformanceMonoBehaviourTestBase基类继承了一个公共布尔属性 IsTestFinished 。我们继承这个属性是因为我们的 RenderPerformanceMonoBehaviourTestBase 类继承了IMonoBehaviourTest
接口。
这个属性非常重要,因为UnityTestRunner 通过它知道测试何时结束。当它的值为真时,测试结束。继承这段逻辑取决于你想要什么时候决定让UnityTestRunner结束测试。
样例:在 IsTestFinished 中属性采样自定义指标

public bool IsTestFinished
{
    get
    {
        bool isTestFinished = false;
 
        if (IsMetricsCaptured)
        {
            Measure.Custom(objCountSg, RenderedGameObjects);
            Measure.Custom(trianglesSg, Tris);
            Measure.Custom(verticesSg, Verts);
            isTestFinished = true;
        }
 
        return isTestFinished;
    }
}

在这个最终样例中,我们在测试结束时,采样了场景中渲染物体的数量,三角形数量,顶点数。

SampleGroupDefinition(采样组定义)

现在我们已经通过这些样例了解了我们如何在性能测试扩展中进行调用方法采样指标,让我们来讨论一下如何我们开始时要如何进行配置。

这些Measure.*测试方法通常使用一个被称作 SampleGroupDefinition(采样组定义) 的结构体作为参数。当我们创建一个新的 SampleGroupDefinition 时,我们会定义一些我们采样时要收集的数据。

样例:定义一个新的SampleGroupDefinition来采样GpuTimeLastFrame(前一帧GPU耗时) ,使用毫秒作为采样单位,用最小值统计采样数据

下面是 GpuTimeLastFrame 采样的 SampleGroupDefinition。通过它我们让性能测试扩展得知如何收集GpuTimeLastFrame采样数据并统计他们。

SampleGroupDefinition 来自动态场景渲染性能测试样例,所以我们已经选定了使用最小集统计我们的采样数据。但是为什么我们不使用更通用的统计方法,如中位数或平均值?

答案是这个场景是动态的。在动态场景中,在同样的场景运行给了不同渲染值的同样的代码的情况下,使用中位数或平均值可能是不可靠的或不一致的。如果我们想要在一个动态场景中追踪统计单一的渲染指标,这貌似是 我们能做的最好的方案。无论如何,当我们为我们的静态场景定义类似的 SampleGroupDefinition 时,我们肯定会用中位数。

new SampleGroupDefinition(GpuTimeLastFrameName, SampleUnit.Millisecond, AggregationType.Min)

样例:为帧率定义一个新的 SampleGroupDefinition ,使用 none 作为采样单位,使用中位数统计采样值,其价值更高

下面是一个 FPS 的 SampleGroupDefinition 。FPS 没有一个分散的测量单位;就是 FPS,所以我们这里使用 SampleUnit.None 。我们使用中位数进行统计。这是一个静态场景所以我们无需担心不可预期的渲染过程。我们为采样组明确的建立了一个15%的系数,并且给increaselsBetter参数传入true,因为fps提高是一件好事情。

最后的两个参数在我们通过命令行运行时将被收集并保存在 results.xml 文件内,后续将会使用它们来在 Unity Performance Benchmark Reporter 中建立基准

new SampleGroupDefinition(FpsName, SampleUnit.None, AggregationType.Median, threshold: 0.15, increaseIsBetter: true)

在测试完成时,我们之前启用的所有测量单位都会被PerformanceTestingExtension(性能测试扩展)统计。

Measurement Types (测量类型)

我要指出一点,在我们的代码样例中,我们使用了一组不同的Unity Performance Testing Extension APIs(Unity 性能测试扩展API),它们是

  • Measure.ProfilerMarkers 和
  • Measure.Custom

Unity Performance Testing Extension 提供了其他的测量方法,他们或许可以满足你再unity性能测试时想要特殊测量的。这些附加的方法有:

  • Measure.Method
  • Measure.Frames
  • Measure.Scope
  • Measure.FrameTimes

可以在 Unity Performance Testing Extension documentation 文档中学习了解不同的测量方法,尤其是 “Taking measurements” 这一章。

在 Unity Test Runner 中运行性能测试

现在我们将开始关注一些其它的样例,那就是我们如何使用 Unity Performance Testing Extension 和 Unity Test Runner 来编写我们的性能测试,让我们看一下我们如何让它运作起来。

执行我们的性能测试有两种主要的方法。

  1. 使用命令行工具用运行参数运行Unity。这是一种比较推荐的方式,因为 Unity Performance Test Extension 可以为我们生成一个 result.xml ,这样我们便可以在 Unity Performance Benchmark Reporter 中去观察和比较这些结果。

  2. 直接在Editor内运行,如果你有下面两种需求,那么推荐你这种方式。

    a.只是想运行一下测试并在 Unity Test Runner 窗口内观察一下结果,并不需要收集结果备以后使用。
    b.只想看一下测试能不能跑起来,并且需要调试你的测试代码。

使用命令行测试选项运行性能测试

这里有两个从命令行使用 Unity Test Runner 进行性能测试的例子。这些例子看起来有些相似,因为我们已经在之前的讨论的如何从命令行开启 UnityPerformanceBenchmark 工程中构建过同样的例子。

样例:从windows运行 Android 平台的 unityPerformanceBenchmark Performance 测试

我们在 windows 运行 unity 构建 Android 运行样例。基于OpenGLES3 图形接口,多线程渲染,Mono脚本后台编译。

1 Unity.exe -runTests [-batchmode] -projectPath
2 C:\XRAutomatedTests-2018.2\PerformanceTests\UnityPerformanceBenchmark -testPlatform Android -buildTarget Android -playergraphicsapi=OpenGLES3 -mtRendering -scriptingbackend=mono -testResults
3 C:\PerfTests\results\PerfBenchmark_Android_OpenGLES3_MtRendering_Mono.xml -logfile
4 C:\PerfTests\logs\PerfBenchmark_Android_OpenGLES3_MtRendering_Mono_UnityLog.txt

样例:在 OSX平台运行 IOS 平台的unityPerformanceBenchmark Performance 测试

我们在 OSX 上运行 Unity 构建 IOS 运行样例。样例基于 OpenGLES3 图形接口, 多线程渲染,Mono 脚本后台编译。并提供部署到IOS设备上需要的开发者团队账号和配置描述信息。

1 ./Unity -runTests [-batchmode] -projectPath /XRAutomatedTests-2018.2/PerformanceTests/UnityPerformanceBenchmark
2 -testPlatform iOS -buildTarget iOS -playergraphicsapi=OpenGLES3
3 -mtRendering -scriptingbackend=mono
4 -appleDeveloperTeamID=
5 -iOSProvisioningProfileID= -testResults /PerfTests/results/PerfBenchmark_Android_OpenGLES3_MtRendering_Mono.xml
6 -logfile /PerfTests/logs/PerfBenchmark_Android_OpenGLES3_MtRendering_Mono_UnityLog.txt

以上的两个例子,我们介绍过用3-4行的命令行选项来帮助我们运行我们的测试,这可以取代使用命令行选项打开Unity Editor 将参数传递给 IPrebuildSetup.Setup 方法。

-runTests

这个选项告诉 Unity 我们想要运行我们的测试。

-testResults

这个选项指定Unity Test Runer 需要保存性能测试结果的xml文件的文件名和路径。

-logfile

这个选项告诉 Unity Editor 要保存log文件的路径和文件名。这是一个可选参数,但是当你检查失败和问题这个文件对你将是非常有用的。

-batchMode

这个选项将会强制 Unity Editor 用无头模式(精简模式)开启。当我们只需要运行播放器性能测试,且不需要完全打开 Unity Editor 时,我们会用到这个参数。这在自动执行测试时会节约大量的时间。如果不使用这个选项,那么 Unity Editor 在开始测试前会完全打开。

在 Unity ,在我们的集成化环境中,我们经常会使用 batchmode (无头模式)运行我们的性能测试。

**样例:从命令行运行 UnityPerformanceBenchmark 测试 **


从命令行运行

在 Unity Eidtor 中运行性能测试

在 Unity Test Runner 窗口顶部,当PlayMode(播放模式)为选中状态时(PlayMode 指,测试运行在构建的播放器上或在Eidtor的播放窗口),我们可以

  1. Run All (运行所有) - 点击这个按钮会运行所有PlayMode标签下的测试
  2. Run Selected(运行选择) - 点击此按钮将会运行选择的测试或某节点及所有的子节点
  3. Run all in Player(运行所有播放器中的) - 点击此按钮,会按照构建设置对播放器进行构建,并在其中运行测试。

必要条件
在 Editor 的 Test Runner 窗口中运行 Performance Testing Extension 0.1.50之前的性能测试,不会产生 Unity Performance Benchmark Reporter 需要的 result.xml 文件。当然如果你使用 Performance Testing Extension 0.1.50 及之后的版本,results.xml 文件将会生成在 Assets\StreamingAssets 工程目录下。
当你使用的是 Performance Testing Extension 0.1.50之前的版本,并且在你的性能测试做完时想要创建一个 results.xml 文件,你需要从命名行运行Unity并带上必要的参数。当心,无论何时当你使用 -runTests 运行Unity时,Editor 会打开并开始运行测试。
result.xml 文件将会包含测试运行的结果和meta数据,我们将使用Unity Performance Benchmark Reporter 把他们生成基准结果,并和之后的测试进行比较。

样例:在 Unity Eidtor 中运行性能测试

Editor中运行

观察测试结果

如果我们是在 Editor 内运行的测试,通过选择 Unity Test Runner 窗口内的每一条测试我们可以在窗口的地步看到收集的值。

样例:在Unity Test Runner 内观察性能测试采样统计

Editor内观察测试统计结果

如果你想看你从命令行执行的 Unity Performance 测试结果,那么你需要使用 Unity Performance Benchmark Reporter (或则打开result .xml 文件来看,但是它的可读性很差)

接下来,让我们来讨论一下如果使用 Unity Performance Benchmark Reporter 来查看和比较测试结果。

引入 Unity Performance Benchmark Reporter

Unity Performance Benchmark Reporter 可以将性能基准指标和后来的性能指标(包括在 Unity Test Runner 中使用Unity Performance Testing Extension生成的)用一种html的可视化方式展现出来。

reporter 是基于.NET Core 2.x 构建的,所以可以运行于.NET 支持的不同平台(windows,OSX,等)。因此,要运行他请确保你已经安装了.NET Core SDK.

执行 Unity Performance Benchmark reporter 需要在命令行引入它的 assembly 如下所示。

dotnet UnityPerformanceBenchmarkReporter.dll
--baseline=D:\UnityPerf\baseline.xml
--results=D:\UnityPerf\results --reportdirpath=d:\UnityPerf

在 reporter 跑起来后,一个 UnityPerformanceBenchmark 文件夹会被创建,其中包括一个 html 报告器,一些 .css, .js,和图片文件。打开 html 报告来看一下 reuslt.xml 文件内的性能指标采样结果的可视化结果。

命令行参数

-results

一个文件夹路径,用来存放 html 报告器要用到的非基准的 result.xml 文件

至少要有一个 -results 值传给 UnityPerformanceBenchmarkReporter.dll .这是唯一的一个必要值。

这个参数还可以用来指定一个单独的基准的 xml result 文件。并且,你可以通过重复这些参数来指定多个不同的目录或文件。

--results=D:\UnityPerf\results --results=D:\UnityPerf\results.xml

–baseline

用来和其它结果比较的基准 result.xml 文件的路径。

-reportdirpath

报告器用来创建性能基准报告的目录。在 UnityPerformanceBenchmark 的子目录下创建。

如果报告器未指定路径,UnityPerformanceBenchmark 的子目录将会被创建在 调用 UnityPerformanceBenchmarkReporter.dll 的目录。

比较性能测试结果

让我们来用 Performance Benchmark Reporter 做一些性能测试比较。

样例:尝试改变 VR-enabled Gear VR 场景的配置参数来提升帧率

我这里有一个包含如下特性的场景

  • 732 个物体
  • 95,898 个三角面
  • 69,740 个定点


    我们的Gear VR 场景

我跑一个 Unity Performance Test 测试来测试场景采样标准 来帮我了解

你可能感兴趣的:(Unity中的基准化性能分析:如何开始)