Unity’s code is instrumented with a large number of Profiler markers that give you insight into what is taking up time in your application. The following tables explain what some of the common markers do:
The main thread base markers provide a clear separation between the time spent on your application and time spent on the Editor and Profiler activities. The Recorder can also use these markers to get the timing of a frame on the main thread.
|PlayerLoop||Contains any samples that originate from your application’s main loop. If you target the Editor instead of Play mode while the Player is running within the Editor in active Play mode, PlayerLoop samples nest under the EditorLoop.|
(Editor only marker)
|Contains any samples that originate from the Editor’s main loop. This is only present while you profile a player in the Editor. When you target Play mode with the Profiler, EditorLoop samples show the amount of time spent rendering and running the Editor that contains the Player.
The Profiler does not record any further detailed data under the EditorLoop marker. This is because the samples that Editor code emits while the Profiler is profiling Play mode create a large profiling overhead.
Unity categorizes any samples from the EditorLoop marker as Others in the CPU Profiler module charts. As a result, EditorLoop samples are usually the biggest contributor to that category. If you want to see what the Editor does in this time and also get a more detailed breakdown of what else contributes to the Others category, change the Profiler target to the Editor instead.
Switching the Profiler’s target to Editor changes what the Profiler displays in the CPU Profiler module’s detail pane, as well as the Category breakdown in the CPU Profiler module’s charts. This is because the samples that were previously hidden under the EditorLoop sample then contribute towards their respective categories.
(Editor only marker)
|Contains any samples that relate to collecting statistics for different active Profiler modules.
Samples that are nested under the Profiler.CollectGlobalStats marker indicate how much overhead the Player incurs when it collects the statistics of a particular module. All other child samples only reflect their effect in the Editor.
To remove the overhead that a particular module incurs, close the module’s chart, or call Profiler.SetAreaEnabled.
Note: Custom Profiler modules that use built-in counters enable the built-in counter’s area, even if the module it belongs to has been disabled. To prevent the Profiler from collecting these statistics and creating collection overhead, make sure that both the built-in Profiler module and your custom Profiler module are disabled.
There are certain markers that only appear when you profile in the Unity Editor. These markers do not appear in Player-related activity, and only relate to Editor activity. Editor-only markers include security checks like the GetComponentNullErrorWrapper, which helps to identify a null component usage; CheckConsistency, which validates object setup; CheckAllowDestructionRecursive, which is a destruction check; and Prefab-related activities.
By default, in the CPU Profiler module’s Hierarchy view, sample stacks with Editor-only markers are collapsed and are named
EditorOnly [SampleName]. While these sample stacks or their child samples might cause managed allocations that can lead to garbage collection getting triggered, they do not contribute to the GC.Alloc value of their parent sample if they are collapsed.
To change the default behavior, in the top right of the module details pane, select the context menu and disable the Collapse EditorOnly Samples setting. When you do this, you can expand the sample and contribute its GC.Alloc value to the enclosing marker.
This option does not affect the Timeline view, and the samples and their children appear expanded. You can usually ignore the samples with these markers, because they relate to Editor-only activity and don’t have any impact on reducing managed allocations. However, they can be useful to investigate if you are having serious issues with the performance impact they have on Play mode.
Unless you are using the Job System, most of your scripting code is nested underneath the following markers. For information on Job System samples, see the Multi threading markers section of this page.
For further details on Unity’s Update Loop, see the documentation on Order of Execution of Event Functions. If you have inserted your own callbacks into the Player Loop via PlayerLoop.SetPlayerLoop, your script update samples appear under the respective main PlayerLoopSystem marker, if entered as a subsystem, or on their own, if entered directly into the main loop.
|Update.ScriptRunBehaviourUpdate||Contains all samples of MonoBehaviour.Update and Coroutines.|
|BehaviourUpdate||Contains all samples of MonoBehaviour.Update methods.|
|CoroutinesDelayedCalls||Contains all samples of Coroutines after their first yield.|
|PreLateUpdate.ScriptRunBehaviourLateUpdate||Contains all samples of Monobehaviour.LateUpdate methods.|
|FixedBehaviourUpdate||Contains all samples of Monobehaviour.FixedUpdate methods.|
These markers contain the samples where the CPU spends time processing data for the GPU, or where it might be waiting for the GPU to finish. If the GPU Profiler module is not available, or it adds too much overhead, the toolbar in the Profiler module details pane does not show this information. The samples under these markers can give you a good idea of whether your application is CPU-bound or GPU-bound.
|WaitForTargetFPS||Indicates how much time your application spent waiting for the targeted FPS that Application.targetFrameRate specifies.
If the sample is a subsample of Gfx.WaitForPresentOnGfxThread, it represents the amount of time that your application spent waiting for the GPU. For example, this could be time that the GPU spent waiting for the next VSync, if that is configured in QualitySettings.vSyncCount, or if vSync is enforced on your target platform. However, samples with this marker are also emitted if the GPU hasn’t finished computing the frame.
To determine what is causing samples with this marker to use a lot of time, switch to the Timeline view in the CPU Profiler module. In this view, you can check what happened on the render thread and how much time passed between this sample ending in the current frame and the same sample ending in surrounding frames.
If the duration is larger than your application’s frame time should be (based on the targeted frame rate or vSync) your frames are taking too long to render or compute. If that’s the case, investigate the render thread and see how much time it spent on Gfx.PresentFrame over other work it did to prepare and issue commands to the GPU. If the render thread spent a large amount of time in Gfx.PresentFrame, your rendering work is GPU-bound. If the render thread’s time was spent preparing commands, your application is CPU-bound.
To find out what to focus on, if your application is GPU bound, profile the GPU work with the Unity Profiler or a platform profiler. For more information, see the User manual documentation on how to optimize graphics performance.
Note: The Editor doesn’t VSync on the GPU and instead uses WaitForTargetFPS to simulate the delay for VSync. Some platforms, in particular Android and iOS, enforce VSync or have a default frame rate cap of 30 or 60.
|Gfx.ProcessCommands||Contains all processing of the rendering commands on the render thread. Your application might have spent some of this processing time waiting for VSync or new commands from the main thread, which you can see from its child sample Gfx.WaitForPresentOnGfxThread.|
|Gfx.WaitForCommands||Indicates that the render thread was ready for new commands. If you see this marker, it might indicate a bottleneck on the main thread.|
|Gfx.PresentFrame||Represents the time your application spent waiting for the GPU to render and present the frame, which includes waiting for VSync.
Samples with the WaitForTargetFPS marker on the main thread show how much time is spent waiting for VSync.
|Gfx.WaitForPresentOnGfxThread||Indicates that the main thread was ready to start rendering the next frame, but the render thread did not finish waiting for the GPU to present the frame. This might indicate that your application is GPU-bound. To see what the render thread is simultaneously spending time on, check the CPU Profiler module’s Timeline view.
If the render thread spends time in Camera.Render, your application is CPU-bound and might be spending too much time sending draw calls or textures to the GPU.
If the render thread spends time in Gfx.PresentFrame, your application is GPU-bound, or it might be waiting for VSync on the GPU. A WaitForTargetFPS sub-sample of Gfx.WaitForPresentOnGfxThread represents the portion of the Present phase that your application spent waiting for VSync. The Present phase is the portion of time between Unity instructing the graphics API to swap the buffers, to the time that this operation completed.
|Gfx.WaitForRenderThread||Indicates that the main thread was waiting for the render thread to process all the commands in its command stream. Samples with this marker only appear in multithreaded rendering.|
The samples highlight Mono or IL2CPP scripting backend activities and are useful for troubleshooting issues with garbage collection and allocation.
|GC.Collect||Represents samples that relate to garbage collection. Whenever Unity needs to perform garbage collection, it stops running your program code and only resumes normal execution when the garbage collector has finished all its work. Note: If you have enabled Incremental Garbage Collection the garbage collector might not finish its work in a single frame.
This interruption might cause delays in the execution of your application that last anywhere from less than one millisecond to hundreds of milliseconds. This depends on how much memory the garbage collector needs to process and the platform your application is running on. For more information, see the documentation on Understanding automatic memory management.
|GC.Alloc||Represents an allocation in the managed heap that contains managed allocations that are subject to automatic garbage collection. To reduce the time your application spends on automatic garbage collection, you should minimize these types of samples.|
|Contains samples that relate to just-in-time compilation of a scripting method. When a function is executed for the first time, Mono compiles it and Mono.JIT represents this compilation overhead.|
These markers contain samples that do not measure the CPU cycles consumed, but instead highlight information that relates to thread synchronization and the Job System. When you see these samples, use the CPU Profiler module’s Timeline view to check what’s happening on other threads at the same time.
|Idle||Contains samples that indicate the length of time that a Worker Thread is inactive for. A worker thread is inactive any time that the Job System does not utilize it, and it goes into wait mode, where it waits on the semaphore.
Small gaps between Idle samples usually happen when the Job System wakes them up, for example, to schedule new jobs. Longer gaps might indicate that a native job that has not been instrumented is running on the thread.
|Semaphore.WaitForSignal||Contains a sample that depicts a synchronization point on a thread. To find the thread it is waiting for, check the Timeline view for samples that ended shortly before this one.|
|WaitForJobGroupID||A Sync Fence on a JobHandle was triggered. This might lead to work stealing, which happens when a worker finishes its work and then looks at other workers’ jobs to complete. These show up as job samples executed under this marker. Jobs that were “stolen” are not necessarily the jobs that were being waited for.|
The following table outlines some of the high-level physics Profiler markers. FixedUpdate calls all of these measurements.
|Physics.Simulate||Contains samples that measure the amount of time spent working on the pre-requisites for the Physics.Simulate method. This method instructs the physics engine to run its simulation, which updates the state of the current physics.|
|Physics.Processing||Contains samples that spent time waiting on the main thread until the physics simulation completed across all threads. If your application spends a lot of time in Physics.Processing but only has a few physics related GameObjects in the Scene, it might indicate that worker threads picked up other systems tasks due to job stealing and reported as physics. This is because while waiting, the main thread picks up jobs from the high priority queue.|
|Physics.ProcessingCloth||Contains samples that measure the execution time of the Physics.ProcessingCloth method. This method processes all cloth physics jobs. Expand this sample to show the low-level detail of the work done internally in the physics engine.|
|Physics.FetchResults||Contains samples that collect the results of the physics simulation from the physics engine, such as contact streams, trigger overlaps, and joint breakage events.|
|Physics.UpdateBodies||Contains samples that update all the physics bodies’ positions and rotations. For each gameObject that has a Rigidbody component, samples with this marker read the pose from the physics engine and write it to the Transform.|
|Physics.ProcessReports||Contains samples that correspond to time spent forwarding physics data to scripts via callbacks such as OnTriggerEnter. Note: These samples do not compute the data required because they have already been prepared during FetchResults.
There are four distinct sub-stages:
|Physics.TriggerEnterExits||Contains samples that measure the execution time of Physics.TriggerEnterExits. This processes OnTriggerEnter and OnTriggerExit events.|
|Physics.TriggerStays||Contains samples that measure the execution time of Physics.TriggerStays. This processes OnTriggerStay events.|
|Physics.Contacts||Contains samples that measure the execution time of Physics.Contacts. This processes OnCollisionEnter, OnCollisionExit, and OnCollisionStay events.|
|Physics.JointBreaks||Contains samples that measure the execution time of Physics.JointBreaks. This processes updates and messages related to broken joints.|
|Physics.UpdateCloth||Contains samples that measure the execution time of the Physics.UpdateCloth method. This method processes updates that relate to cloth and their Skinned Meshes.|
|Physics.Interpolation||Contains samples that measure the execution time of the Physics.Interpolation method. This method manages the interpolation of positions and rotations for all the physics objects in your application.|
For more information about script life cycles and general samples within a script life cycle, see the documentation on Order of Execution for Event Functions.
The CPU Profiler detects some common performance issues and warns you about them. These appear in the Warning column of the CPU Profiler module’s Hierarchy view in the module details pane.
The Profiler detects some specific calls that you should avoid in performance-critical contexts. It displays the warnings with the reasons the operations are affecting performance as follows:
|AssetBundle.asset/allAssets||Indicates that Unity called the AssetBundleRequest.assets/allAssets API while the AssetBundle loading was not complete (AssetBundleRequest.isDone is false). This causes a stall on the main thread and waits for the loading operation to complete.|
|Rigidbody.SetKinematic||Recreate non-convex MeshCollider for Rigidbody|
|Indicates that RebuildInternalState has been triggered. RebuildInternalState is an operation that goes through the list of curves for each clip in the Animation component, and then rebinds each curve to a value on a component, on a GameObject.
This is a resource-intensive operation, so you should avoid calling these methods at runtime as much as possible.
|Indicates that the internal buffer for uploading data to the GPU is resized because it’s not big enough. This resizing is slow and causes spikes in CPU activity.
You can avoid this warning if you can spare the memory to allocate a larger size up front.
You can use Async Upload Buffer Size setting in Quality Settings to set the default size.
The AsyncUploadManager.AsyncBufferResized marker indicates the newly allocated size which you can use as a guide for the default buffer size.