Unity 的代码配备了性能分析器标记,可用于深入了解应用程序中占用时间的内容。下表列出了一些内置标记。
主线程基础标记可清晰区分在应用程序上花费的时间与在 Unity 编辑器和性能分析器活动中花费的时间。您还可以将这些标记与 ProfilerRecorder API 结合使用以获取主线程上一帧的时间信息,或者使用 FrameTimingManager API 来获取运行时的高级帧统计信息。
此外,还可以使用 CPU Main Thread Frame Time、CPU Render Thread Frame Time 和 CPU Total Frame Time 等渲染性能分析器计数器来获取应用程序的 CPU 使用情况的高级时间信息。有关更多信息,请参阅渲染性能分析器计数器参考。
| 标记 | 描述 |
|---|---|
| PlayerLoop | 包含源自应用程序主循环的所有样本。在播放器以活跃播放模式在编辑器中运行时,如果目标是编辑器而非播放模式,则 PlayerLoop 样本会嵌套在 EditorLoop 下。 |
|
EditorLoop (仅限编辑器的标记) |
包含源自编辑器主循环的所有样本。仅当在编辑器中对播放器进行性能分析时才会出现此样本。在使用性能分析器针对的目标是播放模式时,EditorLoop 样本会指示渲染和运行包含播放器的编辑器所花费的时间。 有关更多信息,请参阅播放模式和编辑模式样本。 |
|
Profiler.CollectEditorStats (仅限编辑器的标记) |
包含与收集不同活跃性能分析器模块的统计信息有关的所有样本。 在 Profiler.CollectGlobalStats 标记下嵌套的样本,展示了播放器在收集特定模块的统计数据时产生的开销。所有其他子样本仅反映它们在编辑器中的影响。要消除特定模块的开销,请关闭模块的图表或调用 Profiler.SetAreaEnabled。注意:使用内置计数器的自定义性能分析器模块会启用内置计数器的区域,即使其所属的模块已禁用也是如此。要防止性能分析器收集这些统计信息并产生收集开销,请确保同时禁用内置性能分析器模块和自定义性能分析器模块。 |
除非使用作业系统,否则大多数脚本代码都嵌套在以下标记下面。有关作业系统样本的信息,请参阅本页面的多线程标记部分。
有关 Unity 更新循环的更多详细信息,请参阅事件函数执行顺序相关文档。您可以使用 PlayerLoop.SetPlayerLoop 将自己的回调插入到 Player Loop 中。如果直接将回调插入到主循环中,则脚本更新样本将自行显示。如果将回调作为子系统插入,则样本将显示在相应的主 PlayerLoopSystem 标记下。
| 标记 | 描述 |
|---|---|
| BehaviourUpdate | 包含 MonoBehaviour.Update 方法的所有样本。 |
| CoroutinesDelayedCalls | 包含首次生成后的所有协程样本。 |
| FixedBehaviourUpdate | 包含 Monobehaviour.FixedUpdate 方法的所有样本。 |
| PreLateUpdate.ScriptRunBehaviourLateUpdate | 包含 Monobehaviour.LateUpdate 方法的所有样本。 |
| Update.ScriptRunBehaviourUpdate | 包含 MonoBehaviour.Update 和协程的所有样本。 |
这些标记包含 CPU 在其中花时间来处理 GPU 的数据,或者可能在其中等待 GPU 完成的样本。如果 GPU 性能分析器模块不可用或者增加过多开销,则性能分析器模块详细信息面板中的工具栏不会显示此信息。这些标记下的样本可用于很好地了解应用程序的性能瓶颈是在 CPU 还是 GPU。
| 标记 | 描述 |
|---|---|
| WaitForTargetFPS | 指示应用程序在等待 Application.targetFrameRate 指定的目标 FPS 时所花费的时间。如果该样本是 Gfx.WaitForPresentOnGfxThread 的子样本,则代表应用程序等待 GPU 所花费的时间。例如,如果在 QualitySettings.vSyncCount 中进行了配置,或者如果在目标平台上实施了 vSync,这可能是 GPU 等待下一个 VSync 所花费的时间。不过,即使 GPU 尚未完成一帧的计算,也会发出具有此标记的样本。 要确定导致具有此标记的样本为何耗时过长,可切换到 CPU 性能分析器模块块中的时间轴视图。在此视图中,可以检查渲染线程的运行情况,以及在当前帧与相邻帧中此样本结束时间的间隔。 如果该间隔持续时间大于应用程序的帧时间(基于目标帧率或 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__垂直同步 (VSync) 是一种显示设置,可限制游戏帧频,使其与显示器的刷新率相匹配,以防止图像撕裂。 See in Glossary 或主线程的新命令;可通过子样本 Gfx.WaitForPresentOnGfxThread 来了解此内容。 |
| Gfx.WaitForCommands | 指示渲染线程已准备好接受新命令。如果看到此标记,则可能指示主线程上存在瓶颈。 |
<GraphicsAPIName>.WaitForLastPresent 例如:GfxDeviceD3D11.WaitForLastPresent GfxDeviceD3D12.WaitForLastPresent GfxDeviceMetal.WaitForLastPresent |
当主线程等待 GPU 将帧号翻转到屏幕 (Time.frameCount - QualitySettings.maxQueuedFrames + 1) 时,会出现带有此标记的样本。这意味着如果 QualitySettings.maxQueuedFrames 大于 1,则此时间用于等待 GPU 翻转应用程序在前一个主线程帧中请求渲染的帧。有关此样本的更多详细信息以及 Unity 帧管线的概述,请参阅关于修复时间增量的 Unity 博客文章。 |
| Gfx.WaitForPresentOnGfxThread | 指示主线程已准备好开始渲染下一帧,但是渲染线程未结束对于 GPU 呈现该帧的等待。这可能表明应用程序是受限于 GPU。要查看渲染线程同时在哪些方面花费时间,请查看 CPU 性能分析器模块的时间轴视图。 如果渲染线程在 Camera.Render 中花费时间,则表明应用程序受限于 CPU,可能在向 GPU 发送绘制调用或纹理数据方面耗时过长。 如果渲染线程在 Gfx.PresentFrame 中花费时间,则表示应用程序受限于 GPU,也有可能是在 GPU 上等待 VSync。Gfx.WaitForPresentOnGfxThread 的 WaitForTargetFPS 子样本表示当前阶段应用程序等待 VSync 所花费的时间占比。当前阶段是从 Unity 指示图形 API 交换缓冲区到此操作完成之间的一段时间。 |
| Gfx.WaitForRenderThread | 指示主线程正在等待渲染线程处理位于命令流中的所有命令。带有此标记的样本仅出现在多线程渲染中。 |
这些样本突出显示 Mono 或__ IL2CPP__种由 Unity 开发的脚本后端,可在为某些平台构建项目时替代 Mono。更多信息
See in Glossary 脚本后端活动,对于垃圾收集和分配问题的故障排除非常有用。
| 标记 | 描述 |
|---|---|
| GC.Alloc | 表示托管堆(包含受自动垃圾回收影响的托管分配)中的分配。要减少应用程序在自动垃圾收集方面所花费的时间,应该尽量减少这些类型的样本。 |
| GC.Collect | 表示与垃圾收集相关的样本。每当 Unity 需要执行垃圾收集时,它都会停止运行程序代码,并且仅在垃圾回收器完成所有工作后才恢复正常执行。注意:如果启用了增量垃圾收集,垃圾回收器可能无法在一帧内完成工作。 这种中断可能会导致应用程序的执行出现延迟,延迟时间短则不到一毫秒,长则可达数百毫秒。这取决于垃圾回收器需要处理的内存量以及应用程序运行的平台。如需了解更多信息,请参阅有关了解自动内存管理的文档。 |
|
Mono.JIT 仅适用于 Mono |
包含与脚本方法的即时编译相关的样本。当函数首次执行时,Mono 会对其进行编译,Mono.JIT 代表的就是这种编译所产生的开销。 |
| UnsafeUtility.Malloc | 包含调用 UnsafeUtility.Malloc 以分配非托管内存的样本。虽然垃圾回收器不跟踪此内存,但分配内存可能会产生显著性能影响(随此样本出现)。要调查此调用的源,可以在性能分析器 (Profiler) 窗口中为此标记启用调用栈 (Call Stack) 记录。 |
这些标记包含的样本不测量消耗的 CPU 周期,而是突出显示与线程同步和作业系统相关的信息。查看这些样本时,使用 CPU 性能分析器模块的时间轴 (Timeline) 视图可以检查其他线程上同时发生的操作。
| 标记 | 描述 |
|---|---|
| Idle | 包含指示工作线程处于非活动状态的时间长度的样本。工作线程在作业系统不使用它的任何时间都处于非活动状态,会进入等待模式以等待信号标。 JobSystem 唤醒 Idle 样本时(例如,为了调度新作业),这些样本之间通常会出现较小的间隙时间。较长的间隙可能表示尚未检测的原生作业正在线程上运行。 |
| JobHandle.Complete | 包含指示作业同步点何时发生的样本。同步点可能会对应用程序产生性能影响,并可能会干扰多线程作业代码的执行。要更容易找到同步点发生的确切位置,请为此样本启用调用栈 (Call Stack) 记录。在 CPU 性能分析器模块的时间轴 (Timeline) 视图中,可以启用流程事件 (Flow Events) 以查看此时完成的作业。 |
| Semaphore.WaitForSignal | 包含描述线程上的同步点的样本。要找出所等待的线程,请在时间轴 (Timeline) 视图中查看在此线程之前不久结束的所有样本。 |
| WaitForJobGroupID | 此标记意味着在 JobHandle 上触发了同步栅栏。这可能导致任务窃取的情况,即某个工作线程完成自身任务后,会去查看并执行其他工作线程的作业任务。在该标记下显示的作业示例,就是这些被执行的“窃取”任务。被“窃取”的作业任务并非一定是原本在等待的任务。 |
下表概括了一些高级的物理性能分析器标记。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 方法的执行时间的样本。此方法处理与布料及其蒙皮网格相关的更新。 |
|
有关脚本生命周期及脚本生命周期内的一般样本的更多信息,请参阅事件函数的执行顺序的相关文档。
下表包含动画系统中的标记。有关动画系统性能的一般信息,请参阅 Mecanim 性能与优化页面。
在此阶段,将处理每个激活并启用的 Animator。无论剔除模式和可见性如何,都会处理活动的 Animator。
以 Director. 开头的标记还可以涵盖播放对象 (Playables) 和时间轴 (Timeline)
| 标记 | 描述 |
|---|---|
| Director.PrepareFrame | 设置、调度和等待 Director.PrepareFrameJob 作业。这些作业评估所有激活的 Animator 组件的状态机。 |
| Director.PrepareFrameJob (job) | 计算单个活动 Animator 的状态机。处理 Animator 所需的时间,会随着其状态机的复杂程度增加而变长,也就是与状态数量、过渡数量以及层数量相关。 对具有实现 OnStateMachineEnter 或 OnStateMachineExit 监听器的 StateMachineBehaviours 的状态机的评估将受限于主线程。 |
| Animators.PrepareFirstPass | 为后续处理步骤做好准备。 |
| Animators.ProcessGraphJob | 构建、调度并等待 Animator.ProcessGraph 作业的结果。 |
| Animators.ProcessGraph (job) | 评估所有连接的动画剪辑 (AnimationClips) 中的所有属性。 通过将所有剪辑的根运动属性混合在一起来计算根运动位移。 收集当前 deltaTime 的新动画事件。在评估动画剪辑 (AnimationClips) 的属性时,曲线段会缓存在帧之间的本地,以避免不必要地多次读取动画剪辑 (AnimationClips) 的内存。 |
| Animators.FireAnimationEventsAndBehaviours | 在 StateMachineBehaviours 和 MonoBehaviour 上触发所有动画事件消息,但在 Director.PrepareFrameJob 中已触发的 OnStateMachineEnter 或 OnStateMachineExit 监听器除外。 |
| Animators.ApplyOnAnimatorMove | 应用根运动并在 MonoBehaviours 上触发 OnAnimatorMove 消息。 |
在此阶段,仅处理具有剔除模式 Always Update 的 Animator 和具有剔除模式 UpdateTransform 或 Cull Completely 的可见 Animator。通过根运动移出帧且具有 Cull Completely 的 Animator 不会参与此阶段。由 StateMachineBehaviours 和 AnimationEvents 触发的用户代码所禁用的 Animator 也是如此
| 标记 | 功能 |
|---|---|
| Animators.PrepareSecondPass | 设置后续处理步骤。这包括根据剔除模式和可见性状态对 Animator 进行筛选。 |
| Animators.SortWriteJob | 变换系统一次只允许单个线程修改变换层级结构 (Transform Hierarchy)。为了适应此约束,Animators.SortWriteJob 按 Transform.root 对 Animators.WriteTransforms 作业进行分组。为了获得最佳性能,请避免在同一变换层级结构中使用多个 Animator,这样可以提高任务调度和并行处理的精细度。 |
| Animators.ProcessAnimationsJob | 构建、调度并等待 Animators.ProcessAnimations 作业的结果。 |
| Animator.ProcessAnimations (job) | 对单个 Animator 中当前处于激活状态的动画剪辑 (AnimationClips) 的所有属性进行混合,但不包括根运动的属性。将它们应用于内部的角色模型 (avatar) 表征。 此标记的持续时长会随动画及混合的复杂程度而变化。 |
| OnAnimatorIK | 设置动画 IK。为每个启用 IK pass 的 Animator Controller 层进行一次此调用。 |
| Animators.WriteJob | 构建、调度并等待 Animator.WriteTransform 作业的结果。这些作业会将动画角色模型 (avatar) 的变换姿势写入相关游戏对象的变换层级结构。 |
| Animators.WriteTransforms (job) | 将所有动画变换从工作线程写入场景。 |
| Animator.WriteProperties | 将任何不是变换姿势的动画属性写入目标对象。 |
CPU 性能分析器可检测到一些常见性能问题,并发出相关警告。这些警告会显示在模块详细信息面板中的 CPU 性能分析器模块的层级结构视图中的警告 (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 活动出现峰值。 如果能够腾出内存来预先分配更大的空间,就可以避免此警告。 您可以使用质量设置 (Quality Settings) 中的异步上传缓冲区大小 (Async Upload Buffer Size) 设置来设置默认大小。 AsyncUploadManager.AsyncBufferResize 标记指示新分配的大小,您可以将其用作默认缓冲区大小的参考。 |
| Rigidbody.SetKinematic | 为刚体重新创建非凸面体 MeshCollider。 |