Version: 2020.3
应用程序性能分析
Profiler 窗口

常见性能分析器标记

Unity 的代码配备了大量性能分析器标记,可用于深入了解应用程序中占用时间的内容。下表说明了一些常见标记的作用:

主线程基础标记

主线程基础标记可清晰区分在应用程序上花费的时间与在编辑器和性能分析器活动中花费的时间。ProfilerRecorder 也可以使用这些标记来获取主线程上帧的时间使用情况。

标记 功能
PlayerLoop 包含源自应用程序主循环的所有样本。如果播放器以活跃运行模式在编辑器中运行时,目标是编辑器而不是运行模式,则 PlayerLoop 样本会嵌套在 EditorLoop 下。
EditorLoop
(仅限编辑器的标记)
包含源自编辑器主循环的所有样本。仅当在 Editor 中对播放器进行性能分析时才会出现此样本。使用性能分析器将运行模式作为目标时,EditorLoop 样本会显示渲染和运行包含播放器的编辑器所花费的时间。

性能分析器不会在 EditorLoop 标记下记录任何进一步的详细数据。这是因为性能分析器在对运行模式进行性能分析时,编辑器代码发出的样本会产生大量性能分析开销。

Unity 将来自 EditorLoop 标记的任何样本都在 CPU 分析器模块图表中分类为 Others。因此,EditorLoop 样本通常是该类别的最大贡献者。如果想了解编辑器在这段时间内的操作,并且还想获得有关 Others 类别的其他贡献者的更详细细分,请将性能分析器目标更改为编辑器

将性能分析器的目标切换为编辑器会更改性能分析器在 CPU 性能分析器模块的详细信息面板中显示的内容,以及 CPU 性能分析器模块图表中的类别细分。这是因为之前隐藏在 EditorLoop 样本下的样本随后会归类到其各自的类别中。
Profiler.CollectEditorStats
(仅限编辑器的标记)
包含与收集不同活跃性能分析器模块的统计信息有关的任何样本。

嵌套在 Profiler.CollectGlobalStats 标记下的样本指示播放器在收集特定模块的统计信息时产生的开销量。所有其他子样本仅反映它们在编辑器中的效果。

要消除特定模块产生的开销,请关闭模块的图表,或调用 Profiler.SetAreaEnabled。

注意:使用内置计数器的自定义性能分析器模块会启用内置计数器的区域(即使它所属的模块已禁用)。要防止性能分析器收集这些统计信息和产生收集开销,请确保禁用内置性能分析器模块和自定义性能分析器模块。

仅限编辑器的标记

某些标记仅当在 Unity 编辑器中进行性能分析时才会出现。这些标记不会出现在与播放器相关的活动中,仅与编辑器活动相关。仅限编辑器的标记包括如下的安全性检查:GetComponentNullErrorWrapper(有助于确定 null 组件使用情况)、CheckConsistency(验证对象设置)、CheckAllowDestructionRecursive(损坏检查)以及预制件相关活动。

默认情况下,在 CPU 性能分析器模块的 Hierarchy 视图中,带有仅限编辑器的标记的样本栈会进行折叠并命名为 EditorOnly [SampleName]。虽然这些样本栈或其子样本可能会导致托管分配,从而导致触发垃圾收集,但它们在折叠时不会影响其父样本的 GC.Alloc 值。

要更改默认行为,请单击模块详细信息面板右上角,选择上下文菜单,然后禁用 Collapse EditorOnly Samples 设置。完成此操作后,可以展开样本并将其 GC.Alloc 值贡献给所属标记。

此选项不影响 Timeline 视图,并且样本及其子项显示为展开状态。通常可以忽略带有这些标记的样本,因为它们与仅限编辑器的活动相关,对减少托管分配没有任何影响。但是,如果在它们对运行模式的性能影响方面遇到严重问题,则它们可用于调查。

脚本更新标记

除非使用作业系统,否则大多数脚本代码都嵌套在以下标记下面。有关作业系统样本的信息,请参阅本页面的多线程标记部分。

有关 Unity 更新循环的更多详细信息,请参阅事件函数执行顺序相关文档。如果已通过 PlayerLoop.SetPlayerLoop 将自己的回调插入到播放器循环中,则脚本更新样本会出现在相应的主 PlayerLoopSystem 标记下(如果作为子系统进入,或者如果自行直接进入主循环中)。

标记 功能
BehaviourUpdate 包含 MonoBehaviour.Update 方法的所有样本。
CoroutinesDelayedCalls 包含首次生成后的所有协程样本。
FixedBehaviourUpdate 包含 Monobehaviour.FixedUpdate 方法的所有样本。
PreLateUpdate.ScriptRunBehaviourLateUpdate 包含 Monobehaviour.LateUpdate 方法的所有样本。
Update.ScriptRunBehaviourUpdate 包含所有 MonoBehaviour.Update协程样本。

渲染和 VSync 标记

这些标记包含 CPU 在其中花时间来处理 GPU 的数据,或者可能在其中等待 GPU 完成的样本。如果 GPU 性能分析器模块不可用或者增加过多开销,则性能分析器模块详细信息面板中的工具栏不会显示此信息。这些标记下的样本可用于很好地了解应用程序是 CPU 密集型还是 GPU 密集型。

标记 功能
WaitForTargetFPS 指示应用程序等待 Application.targetFrameRate 指定的目标 FPS 所花费的时间量。

如果样本是 Gfx.WaitForPresentOnGfxThread 的子样本,则指示应用程序等待 GPU 所花费的时间量。例如,这可能是 GPU 等待下一个 VSync 所花费的时间(如果在 QualitySettings.vSyncCount 中进行了配置,或者如果在目标平台上强制实施了 vSync)。但是,如果 GPU 尚未完成帧计算,也会发出带有此标记的样本。

要确定导致带有此标记的样本使用大量时间的原因,请切换到 CPU 性能分析器模块中的 Timeline 视图。在此视图中,可以检查渲染线程上发生的情况,以及在当前帧中结束的此样本与在周围帧中结束的相同样本之间所经过的时间量。

如果持续时间大于应用程序帧时间(基于目标帧速率或 vSync),则帧渲染或计算时间过长。如果是这种情况,请调查渲染线程,并查看相对于它为做好准备和向 GPU 发出命令而进行的其他工作,它在 Gfx.PresentFrame 方面花费的时间量。如果渲染线程在 Gfx.PresentFrame 方面花费了大量时间,则渲染工作是 GPU 密集型。如果渲染线程的时间花费在准备命令上,则应用程序是 CPU 密集型。

要了解要关注的内容,如果应用程序是 GPU 密集型,请使用 Unity 性能分析器或平台性能分析器对 GPU 工作进行性能分析。有关更多信息,请参阅有关如何优化图形性能的用户手册文档。

注意:编辑器不在 GPU 上进行 VSync,而是使用 WaitForTargetFPS 模拟 VSync 的延迟。某些平台(尤其是 Android 和 iOS)强制执行 VSync 或默认帧率上限为 30 或 60。
Gfx.PresentFrame 表示应用程序在等待 GPU 渲染和呈现帧方面花费的时间,其中包括等待 VSync 的时间。

主线程上带有 WaitForTargetFPS 标记的样本会显示等待 VSync 所花费的时间量。
Gfx.ProcessCommands 包含对渲染线程上渲染命令的所有处理。应用程序可能会花费此处理时间的一部分来等待 VSync 或主线程的新命令;可通过子样本 Gfx.WaitForPresentOnGfxThread 来了解此内容。
Gfx.WaitForCommands 指示渲染线程已准备好接受新命令。如果看到此标记,则可能指示主线程上存在瓶颈。
<GraphicsAPIName>.WaitForLastPresent e.g.
GfxDeviceD3D11.WaitForLastPresent
GfxDeviceD3D12.WaitForLastPresent
GfxDeviceMetal.WaitForLastPresent
当主线程等待 GPU 将帧号翻转到屏幕 (Time.frameCount - QualitySettings.maxQueuedFrames + 1`) 时,会出现带有此标记的样本。这意味着如果 QualitySettings.maxQueuedFrames 大于 1,则此时间用于等待 GPU 翻转应用程序在前一个主线程帧中请求渲染的帧。

有关此样本的更多详细信息以及 Unity 的帧管线概述,请参阅 Unity 关于修复 Delta Time 的博客文章
Gfx.WaitForPresentOnGfxThread 指示主线程已准备好开始渲染下一帧,但是渲染线程未结束对于 GPU 呈现该帧的等待。这可能表明应用程序是 GPU 密集型。要查看渲染线程同时在哪些方面花费时间,请查看 CPU 性能分析器模块的 Timeline 视图

如果渲染线程将时间花费在 Camera.Render 中,则表示应用程序为 CPU 密集型并且可能花费太多时间向 GPU 发送绘制调用或纹理。

如果渲染线程将时间花费在 Gfx.PresentFrame 中,则表示应用程序是 GPU 密集型,或者可能在等待 GPU 上的 VSync。Gfx.WaitForPresentOnGfxThread 的 WaitForTargetFPS 子样本可以指示应用程序用于等待 VSync 的呈现阶段的一段时间。呈现阶段是从 Unity 指示图形 API 交换缓冲区到此操作完成时间之间的一段时间。
Gfx.WaitForRenderThread 指示主线程正在等待渲染线程处理位于命令流中的所有命令。带有此标记的样本仅出现在多线程渲染中。

后端脚本标记

这些样本突出显示 Mono 或 IL2CPP 脚本后端活动,对于垃圾收集和分配问题的故障排除非常有用。

标记 功能
GC.Alloc 表示托管堆(包含受自动垃圾回收影响的托管分配)中的分配。要减少应用程序在自动垃圾收集方面所花费的时间,应该尽量减少这些类型的样本。
GC.Collect 表示与垃圾收集相关的样本。每当 Unity 需要执行垃圾收集时,它都会停止运行程序代码,并且仅在垃圾回收器完成所有工作后才恢复正常执行。注意:如果启用了增量垃圾收集,则垃圾回收器可能不会在单个帧内完成其工作。

此中断可能会导致应用程序执行出现延迟,从不到一毫秒到数百毫秒不等。这取决于垃圾回收器需要处理的内存量以及应用程序运行的平台。有关更多信息,请参阅了解自动内存管理的相关文档。
Mono.JIT
仅限 Mono
包含与脚本方法的即时编译相关的样本。当函数首次执行时,Mono 会编译它,Mono.JIT 表示此编译开销。
UnsafeUtility.Malloc 包含调用 UnsafeUtility.Malloc 以分配非托管内存的样本。虽然垃圾回收器不跟踪此内存,但分配内存可能会产生显著性能影响(随此样本出现)。要调查此调用的源,可以在 Profiler 窗口中为此标记启用调用栈记录。

多线程标记

这些标记包含的样本不测量消耗的 CPU 周期,而是突出显示与线程同步和作业系统相关的信息。查看这些样本时,使用 CPU 性能分析器模块的 Timeline 视图可以检查其他线程上同时发生的操作。

标记 功能
Idle 包含指示工作线程处于非活动状态的时间长度的样本。工作线程在作业系统不使用它的任何时间都处于非活动状态,会进入等待模式以等待信号标。

Idle 样本之间的小间隙通常发生在作业系统唤醒它们时(例如用于安排新作业)。较长的间隙可能指示尚未检测的本机作业在线程上运行。
JobHandle.Complete 包含指示作业同步点何时发生的样本。同步点可能会对应用程序产生性能影响,并可能会干扰多线程作业代码的执行。要更容易找到同步点发生的确切位置,请为此样本启用调用栈记录。在 CPU 性能分析器模块的 Timeline 视图中,可以启用 Flow Events 以查看此时完成的作业。
Semaphore.WaitForSignal 包含描述线程上的同步点的样本。要找出所等待的线程,请在 Timeline 视图中查看在此线程之前不久结束的所有样本。
WaitForJobGroupID 触发了 JobHandle 上的同步栅栏 (Sync Fence)。这可能导致工作窃取。一个工作线程完成工作,然后等待其他工作线程完成工作时,就会发生工作窃取。这些显示为在此标记下执行的作业样本。“被窃取”的作业不一定是所等待的作业。

物理标记

下表概括了一些高级物理性能分析器标记。FixedUpdate 会调用所有这些测量。

标记 功能
Physics.FetchResults 包含从物理引擎收集物理模拟结果的样本,例如接触流、触发器重叠和关节断裂事件。
Physics.Interpolation 包含测量 Physics.Interpolation 方法的执行时间的样本。此方法管理应用程序中所有物理对象的位置和旋转的插值。
Physics.Processing 包含花费时间等待主线程直到物理模拟在所有线程上完成的样本。如果应用程序在 Physics.Processing 方面花费大量时间,但是在场景中只有几个与物理相关的游戏对象,这可能指示工作线程由于工作窃取而拾取了其他系统任务并报告为物理。这是因为在等待期间,主线程会从高优先级队列中拾取作业。
Physics.ProcessingCloth 包含测量 Physics.ProcessingCloth 方法的执行时间的样本。此方法处理所有布料物理作业。展开此样本可显示物理引擎内部完成的工作的低级细节。
Physics.ProcessReports 包含与通过回调(如 OnTriggerEnter)将物理数据转发到脚本所花费的时间相对应的样本。注意:这些样本不计算所需的数据,因为它们已经在 FetchResults 期间做好准备。

有四个不同的子阶段:
Physics.Contacts 包含测量 Physics.Contacts 的执行时间的样本。这会处理 OnCollisionEnter、OnCollisionExit 和 OnCollisionStay 事件。
Physics.JointBreaks 包含测量 Physics.JointBreaks 的执行时间的样本。这会处理与受损关节相关的更新和消息。
Physics.TriggerEnterExits 包含测量 Physics.TriggerEnterExits 的执行时间的样本。这会处理 OnTriggerEnter 和 OnTriggerExit 事件。
Physics.TriggerStays 包含测量 Physics.TriggerStays 的执行时间的样本。这会处理 OnTriggerStay 事件。
Physics.Simulate 包含测量为 Physics.Simulate 方法处理先决条件所花费的时间量的样本。此方法指示物理引擎运行其模拟,这会更新当前物理的状态。
Physics.UpdateBodies 包含更新所有物理体的位置和旋转的样本。对于每个具有刚体组件的游戏对象,带有此标记的样本会从物理引擎读取姿势并将其写入变换组件。
Physics.UpdateCloth 包含测量 Physics.UpdateCloth 方法的执行时间的样本。此方法处理与布料及其蒙皮网格相关的更新。

有关脚本生命周期及脚本生命周期内的一般样本的更多信息,请参阅事件函数的执行顺序的相关文档。

性能警告

CPU 性能分析器可检测到一些常见性能问题,并发出相关警告。这些警告会显示在 CPU 性能分析器模块的模块详细信息面板 Hierarchy 视图中的 Warning 列内。

性能分析器可以检测到在性能至关重要的背景中应避免的某些特定调用。这种情况下会显示警告以及操作对性能产生影响的原因,如下所示:

Warning 描述
Animation.DestroyAnimationClip
Animation.AddClip
Animation.RemoveClip
Animation.Clone
Animation.Deactivate
指示已触发 RebuildInternalState。RebuildInternalState 是一种操作,它遍历动画组件中每个剪辑的曲线列表,然后将每条曲线重新绑定到游戏对象上某个组件中的值。

这是一种资源密集型操作,因此应尽可能避免在运行时调用这些方法。
AssetBundle.asset/allAssets 指示在 AssetBundle 加载未完成时(AssetBundleRequest.isDone 为 false),Unity 调用了 AssetBundleRequest.assets/allAssets API。这会导致主线程卡顿并等待加载操作完成。
AsyncUploadManager.AsyncBufferResized
AsyncUploadManager.AsyncBufferDelete
指示用于将数据上传到 GPU 的内部缓冲区调整了大小,因为它不够大。这种大小调整比较缓慢,会导致 CPU 活动出现峰值。

如果可以腾出内存来提前分配较大的大小,则可以避免此警告。

可以使用质量设置中的 Async Upload Buffer Size 设置默认大小。

AsyncUploadManager.AsyncBufferResized 标记指示新分配的大小,可以将其用作默认缓冲区大小的指南。
Rigidbody.SetKinematic 为刚体重新创建非凸面体 MeshCollider。
应用程序性能分析
Profiler 窗口