Unity 的代码中嵌入了 探查器一个帮助您优化游戏的窗口。它显示了在游戏各个区域花费了多少时间。例如,它可以报告渲染、动画或游戏逻辑中花费的时间百分比。 更多信息
参见 术语表 标记,让您深入了解应用程序中哪些部分占用时间。下表说明了一些常用标记的作用。
主线程基础标记提供了应用程序花费的时间与编辑器和探查器活动花费的时间之间的清晰区分。ProfilerRecorder 也可以使用这些标记来获取主线程上帧的计时。
标记 | 功能 |
---|---|
PlayerLoop | 包含来自应用程序主循环的任何样本。如果您在播放器在编辑器中处于活动播放模式下运行时将目标设置为编辑器而不是播放模式,则 PlayerLoop 样本将嵌套在 EditorLoop 下。 |
EditorLoop (仅限编辑器标记) |
包含来自编辑器主循环的任何样本。这仅在您在编辑器中分析播放器时出现。当您使用探查器将目标设置为播放模式时,EditorLoop 样本显示渲染和运行包含播放器的编辑器所花费的时间。 探查器不会在 EditorLoop 标记下记录任何其他详细数据。这是因为编辑器代码在探查器正在分析播放模式时发出的样本会产生很大的性能分析开销。 Unity 将来自 EditorLoop 标记的任何样本分类为 CPU 探查器模块图表 中的其他。因此,EditorLoop 样本通常是该类别的最大贡献者。如果您想查看编辑器在此期间执行的操作,并更详细地了解还有哪些其他因素导致了其他类别,请将探查器目标更改为编辑器。 将探查器的目标切换到编辑器会更改探查器在 CPU 探查器模块的详细信息窗格中显示的内容,以及 CPU 探查器模块图表中的类别细分。这是因为以前隐藏在 EditorLoop 样本下的样本随后会将其贡献到各自的类别。 |
Profiler.CollectEditorStats (仅限编辑器标记) |
包含与收集不同活动探查器模块的统计信息相关的任何样本。 嵌套在 Profiler.CollectGlobalStats 标记下的样本指示播放器在收集特定模块的统计信息时产生的开销。所有其他子样本仅反映其在编辑器中的影响。 要消除特定模块产生的开销,请关闭模块的图表,或调用 Profiler.SetAreaEnabled。 注意:使用内置计数器的自定义探查器模块会启用内置计数器的区域,即使它所属的模块已被禁用。为防止探查器收集这些统计信息并产生收集开销,请确保内置探查器模块和自定义探查器模块都已禁用。 |
某些标记仅在您在 Unity 编辑器中进行性能分析时才会出现。这些标记不会出现在与播放器相关的活动中,并且仅与编辑器活动相关。仅限编辑器的标记包括安全检查,例如 GetComponentNullErrorWrapper(有助于识别空组件的使用)、CheckConsistency(验证对象设置)、CheckAllowDestructionRecursive(销毁检查)和与预制相关的活动。
默认情况下,在 CPU 探查器模块的层次结构视图中,带有仅限编辑器标记的样本堆栈会折叠起来,并命名为EditorOnly [SampleName]
。虽然这些样本堆栈或其子样本可能会导致托管分配,从而导致垃圾回收被触发,但如果它们已折叠,则不会将其 GC.Alloc 值贡献给其父样本。
要更改默认行为,请在模块详细信息窗格的右上角选择上下文菜单,然后禁用折叠仅限编辑器的样本设置。执行此操作后,您可以展开样本并将其 GC.Alloc 值贡献给封闭的标记。
此选项不影响时间线视图,并且样本及其子级会展开显示。您通常可以忽略带有这些标记的样本,因为它们与仅限编辑器的活动相关,并且对减少托管分配没有任何影响。但是,如果您遇到与它们对播放模式的性能影响相关的严重问题,则它们可能会有用。
除非您正在使用作业系统,否则大多数脚本代码都嵌套在以下标记下。有关作业系统样本的信息,请参阅此页面的多线程标记部分。
有关 Unity 更新循环的更多详细信息,请参阅有关事件函数的执行顺序的文档。如果您已通过Player Loop中的PlayerLoop.SetPlayerLoop插入了自己的回调,则您的脚本更新样本将显示在相应的 PlayerLoopSystem 主标记下(如果作为子系统输入),或者显示在它们自己下面(如果直接输入主循环)。
标记 | 功能 |
---|---|
BehaviourUpdate | 包含所有MonoBehaviour.Update方法的样本。 |
CoroutinesDelayedCalls | 包含其第一次 yield 之后的协程的所有样本。 |
FixedBehaviourUpdate | 包含所有Monobehaviour.FixedUpdate方法的样本。 |
PreLateUpdate.ScriptRunBehaviourLateUpdate | 包含所有Monobehaviour.LateUpdate方法的样本。 |
Update.ScriptRunBehaviourUpdate | 包含所有MonoBehaviour.Update和协程的样本。 |
这些标记包含 CPU 用于处理 GPU 数据或等待 GPU 完成处理数据的时间的样本。如果GPU 探查器模块不可用,或者它增加了过多的开销,则探查器模块详细信息窗格中的工具栏Unity 编辑器顶部的按钮和基本控件行,允许您以各种方式与编辑器交互(例如缩放、平移)。 更多信息
参见 术语表不会显示此信息。这些标记下的样本可以让您很好地了解应用程序是 CPU 绑定还是 GPU 绑定。
标记 | 功能 |
---|---|
WaitForTargetFPS | 指示应用程序等待Application.targetFrameRate指定的目标 FPS 所花费的时间。 如果样本是 Gfx.WaitForPresentOnGfxThread 的子样本,则它表示应用程序等待 GPU 所花费的时间。例如,这可能是 GPU 等待下一个垂直同步的时间,如果这是在QualitySettings.vSyncCount中配置的,或者如果在目标平台上强制执行垂直同步。但是,如果 GPU 尚未完成计算帧,则也会发出带有此标记的样本。 要确定导致带有此标记的样本占用大量时间的原因,请切换到CPU 探查器模块中的时间线视图。在此视图中,您可以检查渲染线程上发生了什么以及当前帧中此样本结束与周围帧中相同样本结束之间经过了多长时间。 如果持续时间大于应用程序的帧时间(基于目标帧率或垂直同步),则您的帧渲染或计算时间过长。如果是这种情况,请调查渲染线程,并查看它在 Gfx.PresentFrame 上花费了多少时间以及它为准备和向 GPU 发出命令所做的其他工作。如果渲染线程在 Gfx.PresentFrame 中花费了大量时间,则您的渲染工作是 GPU 绑定的。如果渲染线程的时间用于准备命令,则您的应用程序是 CPU 绑定的。 要找出需要关注的内容,如果您的应用程序是 GPU 绑定的,请使用 Unity 探查器或平台探查器分析 GPU 工作。有关更多信息,请参阅用户手册文档,了解如何优化图形性能。 注意:编辑器不会在 GPU 上进行垂直同步,而是使用 WaitForTargetFPS 来模拟垂直同步的延迟。某些平台,特别是 Android 和 iOS,会强制执行垂直同步或具有 30 或 60 的默认帧率上限。 |
Gfx.PresentFrame | 表示应用程序等待 GPU 渲染并呈现帧所花费的时间,包括等待垂直同步。 主线程上带有 WaitForTargetFPS 标记的样本显示等待垂直同步所花费的时间。 |
Gfx.ProcessCommands | 包含在渲染线程上处理渲染命令的所有内容。您的应用程序可能将部分处理时间用于等待垂直同步垂直同步 (VSync) 是一种显示设置,它将游戏的帧率限制为与显示器的刷新率匹配,以防止图像撕裂。 参见 术语表或来自主线程的新命令,您可以从其子样本 Gfx.WaitForPresentOnGfxThread 中看到。 |
Gfx.WaitForCommands | 指示渲染线程已准备好接收新命令。如果您看到此标记,则可能表示主线程存在瓶颈。 |
例如<GraphicsAPIName>.WaitForLastPresent 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 探查器模块的时间线视图。 如果渲染线程在 Camera.Render 中花费时间,则您的应用程序是 CPU 绑定的,并且可能花费了过多的时间将绘制调用或纹理发送到 GPU。 如果渲染线程在 Gfx.PresentFrame 中花费时间,则您的应用程序是 GPU 绑定的,或者它可能正在等待 GPU 上的垂直同步。Gfx.WaitForPresentOnGfxThread 的 WaitForTargetFPS 子样本表示应用程序等待垂直同步的 Present 阶段的一部分。Present 阶段是在 Unity 指示图形 API 交换缓冲区到此操作完成之间的时间段。 |
Gfx.WaitForRenderThread | 指示主线程正在等待渲染线程处理其命令流中的所有命令。带有此标记的样本仅在多线程渲染中出现。 |
这些示例突出显示了 Mono 或 IL2CPPUnity 开发的一种脚本后端,您可以在为某些平台构建项目时将其用作 Mono 的替代方案。 更多信息
参见 术语表 脚本后端为 Unity 中的脚本提供支持的框架。根据目标平台,Unity 支持三种不同的脚本后端:Mono、.NET 和 IL2CPP。但是,通用 Windows 平台仅支持两种:.NET 和 IL2CPP。 更多信息
参见 术语表 活动,并且有助于排查垃圾回收和分配方面的问题。
标记 | 功能 |
---|---|
GC.Alloc | 表示托管堆中的一个分配,其中包含受自动垃圾回收影响的托管分配。为了减少应用程序花费在自动垃圾回收上的时间,您应该尽量减少此类示例。 |
GC.Collect | 表示与垃圾回收相关的示例。每当 Unity 需要执行垃圾回收时,它都会停止运行您的程序代码,并且仅在垃圾回收器完成所有工作后恢复正常执行。注意:如果您已启用 增量垃圾回收,则垃圾回收器可能无法在一帧内完成其工作。 这种中断可能会导致应用程序执行延迟,延迟时间从不到一毫秒到数百毫秒不等。这取决于垃圾回收器需要处理多少内存以及应用程序运行的平台。有关更多信息,请参阅有关 了解自动内存管理 的文档。 |
Mono.JIT 仅限 Mono |
包含与脚本方法的即时编译相关的示例。当首次执行函数时,Mono 会对其进行编译,而 Mono.JIT 表示此编译开销。 |
UnsafeUtility.Malloc | 包含调用 UnsafeUtility.Malloc 分配非托管内存的示例。虽然垃圾回收器不会跟踪此内存,但分配内存可能会对性能产生重大影响,此示例会显示此影响。要调查此调用的来源,您可以在探查器窗口中为此标记启用 调用堆栈在运行时调用的方法列表,按后进先出堆栈组织。 参见 术语表 记录。 |
这些标记包含不测量消耗的 CPU 周期,而是突出显示与线程同步和作业系统相关信息的示例。当您看到这些示例时,请使用 CPU 探查器模块的 时间轴视图 检查同时在其他线程上发生的情况。
标记 | 功能 |
---|---|
空闲 | 包含指示工作线程处于非活动状态的时间长度的示例。工作线程在作业系统不使用它时处于非活动状态,并且进入等待模式,在此模式下它等待信号量。 空闲示例之间的小间隙通常发生在作业系统唤醒它们时,例如,安排新作业。较长的间隙可能表示未进行检测的原生作业正在线程上运行。 |
JobHandle.Complete | 包含指示作业上同步点何时发生的示例。同步点可能会对应用程序的性能产生影响,并可能干扰多线程作业代码的执行。为了更容易找到同步点发生的确切位置,请为此示例启用 调用堆栈 记录。在 CPU 探查器模块的时间轴视图中,您可以启用 流程事件 以查看此时完成的作业。 |
Semaphore.WaitForSignal | 包含描绘线程上同步点的示例。要查找它正在等待的线程,请在时间轴视图中检查在此示例之前结束的示例。 |
WaitForJobGroupID | 触发了 JobHandle 上的同步栅栏。这可能会导致 工作窃取,当工作线程完成其工作后查看其他工作线程的作业以完成时,就会发生这种情况。这些显示为在此标记下执行的作业示例。被“窃取”的作业不一定是正在等待的作业。 |
下表概述了一些高级物理 探查器标记放置在代码中以描述 CPU 或 GPU 事件,然后在 Unity 探查器窗口中显示。默认情况下添加到 Unity 代码中,或者您可以使用 ProfilerMarker API 添加您自己的自定义标记。 更多信息
参见 术语表。 FixedUpdate 调用所有这些测量。
标记 | 功能 | |
---|---|---|
Physics.FetchResults | 包含从 物理引擎一个模拟物理系统各个方面的系统,以便对象可以正确加速并受碰撞、重力和其它力的影响。 更多信息 参见 术语表 收集物理模拟结果的示例,例如接触流、触发器重叠和 关节一个物理组件,允许在刚体组件之间建立动态连接,通常允许一定程度的运动,例如铰链。 更多信息 参见 术语表 断裂事件。 |
|
Physics.Interpolation | 包含测量 Physics.Interpolation 方法执行时间的示例。此方法管理应用程序中所有物理对象的位移和旋转的插值。 | |
Physics.Processing | 包含在主线程上等待物理模拟在所有线程上完成所花费时间的示例。如果您的应用程序在 Physics.Processing 中花费大量时间,但场景中只有几个与物理相关的 游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、摄像机、路径点等等。游戏对象的功用由附加在其上的组件定义。 更多信息 参见 术语表,则可能表示工作线程由于作业窃取而承担了其他系统任务并报告为物理。这是因为在等待时,主线程会从高优先级队列中获取作业。 |
|
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 性能和优化 页面。
在此阶段,处理每个处于活动状态且已启用的动画师。无论剔除模式和可见性如何,都会处理活动动画师。
以Director.
开头的标记也可以涵盖 可播放对象一个 API,通过以称为 PlayableGraph 的树状结构组织和评估数据源,提供了一种创建工具、效果或其它游戏机制的方法。 更多信息
参见 术语表 和时间轴。
标记 | 功能 |
---|---|
Director.PrepareFrame | 设置、安排和等待Director.PrepareFrameJob 作业。这些作业评估所有活动 动画师组件模型上用于使用动画系统为该模型设置动画的组件。该组件引用一个控制动画的动画师控制器资源。 更多信息参见 术语表 的 状态机动画师控制器中角色或动画游戏对象可以处于的一组状态,以及这些状态之间的一组转换和一个用于记住当前状态的变量。可用的状态将取决于游戏类型的不同,但典型状态包括空闲、行走、跑步和跳跃等。 更多信息 参见 术语表。 |
Director.PrepareFrameJob (作业) | 评估单个活动动画师的状态机。处理动画师所需的时间会随着其状态机的复杂性而增加(即状态、转换、属性和图层的数量)。 实现 OnStateMachineEnter 或 OnStateMachineExit 侦听器的 StateMachineBehaviours 的状态机评估将受限于主线程。 |
Animators.PrepareFirstPass | 为下一个处理步骤做准备。 |
Animators.ProcessGraphJob | 构建、安排并等待Animator.ProcessGraph 作业的结果。 |
Animators.ProcessGraph (作业) | 评估所有连接的 AnimationClip 中的所有属性。 通过将所有剪辑的根运动属性混合在一起来计算根运动位移。 为当前 deltaTime 收集新的动画事件。 在评估AnimationClip的属性时,曲线片段会在帧之间本地缓存,以避免不必要地从AnimationClip的内存中读取。 |
Animators.FireAnimationEventsAndBehaviours | 触发所有动画事件允许您向导入的剪辑添加数据,这些数据确定某些操作应在动画时间内何时发生。例如,对于动画角色,您可能希望添加事件到行走和跑步循环中,以指示何时播放脚步声。 更多信息 参见术语表消息到状态机行为和MonoBehaviour上,除了OnStateMachineEnter或OnStateMachineExit监听器,它们已在 Director.PrepareFrameJob 中触发。 |
Animators.ApplyOnAnimatorMove | 应用根运动角色根节点的运动,无论它是由动画本身还是外部控制。 更多信息 参见术语表并触发MonoBehaviour上的OnAnimatorMove消息。 |
在此阶段,只有具有剔除模式Always Update
的Animator以及具有剔除模式UpdateTransform
或Cull Completely
的可见Animator会被处理。被根运动移出帧的具有Cull Completely
的Animator不会参与此阶段。由状态机行为和动画事件触发的用户代码禁用的Animator也不会参与。
标记 | 功能 |
---|---|
Animators.PrepareSecondPass | 设置后续处理步骤。这包括根据它们的剔除模式和可见性状态过滤掉Animator。 |
Animators.SortWriteJob | Transform系统一次只允许单个线程修改Transform层次结构。为了适应此约束,Animators.SortWriteJob 根据它们的Transform.root对Animators.WriteTransforms 作业进行分组。为了获得最佳性能,请避免在同一Transform层次结构中使用多个Animator,以提高作业调度和并行化的粒度。 |
Animators.ProcessAnimationsJob | 构建、调度和等待Animators.ProcessAnimations 作业的结果。 |
Animator.ProcessAnimations (作业) | 混合单个Animator当前活动AnimationClip上的所有属性,根运动除外。将它们应用于内部头像表示。 此标记的长度会随着动画和混合复杂度的增加而增加。 |
OnAnimatorIK | 设置动画IK。每个具有启用IK传递的动画控制器通过具有动画状态机和动画混合树的动画层控制动画,由动画参数控制。同一个动画控制器可以被具有动画组件的多个模型引用。 更多信息 参见术语表层都会调用一次。 |
Animators.WriteJob | 构建、调度和等待Animator.WriteTransform 作业的结果。这些作业将动画头像一个将动画从一个骨骼绑定重新定位到另一个骨骼绑定的接口。 更多信息参见术语表的变换姿势写入相关的GameObject变换层次结构。 |
Animators.WriteTransforms (作业) | 将所有动画变换从工作线程写入场景。 |
Animator.WriteProperties | 将任何不是变换姿势的动画属性写入目标对象。 |
CPU Profiler检测一些常见的性能问题并向您发出警告。这些警告显示在模块详细信息窗格中CPU Profiler模块的层次结构视图的“警告”列中。
Profiler检测一些您应该在性能关键上下文中避免的特定调用。它会显示警告以及这些操作影响性能的原因,如下所示
警告 | 描述 |
---|---|
Animation.DestroyAnimationClip Animation.AddClip Animation.RemoveClip Animation.Clone Animation.Deactivate |
指示已触发RebuildInternalState。RebuildInternalState是一个操作,它遍历Animation组件中每个剪辑的曲线列表,然后将每条曲线重新绑定到GameObject上组件的值。 这是一个资源密集型操作,因此您应尽可能避免在运行时调用这些方法。 |
AssetBundle.asset/allAssets | 指示Unity在AssetBundle加载未完成时(AssetBundleRequest.isDone为false)调用了AssetBundleRequest.assets/allAssets API。这会导致主线程暂停并等待加载操作完成。 |
AsyncUploadManager.AsyncBufferResized AsyncUploadManager.AsyncBufferDelete |
指示用于将数据上传到GPU的内部缓冲区由于空间不足而调整大小。这种调整大小速度很慢,会导致CPU活动出现峰值。 如果您有足够的内存可以预先分配更大的大小,则可以避免此警告。 您可以使用质量设置中的异步上传缓冲区大小设置来设置默认大小。 AsyncUploadManager.AsyncBufferResized标记指示新分配的大小,您可以将其用作默认缓冲区大小的参考。 |
Rigidbody.SetKinematic | 为Rigidbody重新创建非凸面MeshCollider。 |