Profiler doesn't show where the CPU time is spent

hello, guys, I’m trying to profile my game on a connected Android device. For whatever reason, in the hierarchy it is shown where the CPU time is consumed by nodes but if a node says like 70% and I expand it, the nodes inside of it don’t sum to the given percentage altogether. Some info is missing, I tried to enable Deep profiling, it doesn’t allow me to (it stays grey), switch to other views, but no luck. I still can’t see where the CPU time is spent. Can you give me any hints, thanks!

1 Like

The unreported CPU time is likely spent on internal engine logic, though sometimes I’m not sure myself.

GC (Garbage collection) is often a source of CPU slowdown, and I see you have 279 calls to GC.Alloc. Any Garbage you allocate now (on this frame) must be cleaned up automatically by the CPU at some point. Generally, you create garbage when you use the “new” keyword or create/destroy GameObjects.

One way to get more out of the profiler is use “UnityEngine.Profiling.Profiler.BeginSample(“sampleName”)” and “UnityEngine.Profiling.Profiler.EndSample()” to create a new category in that profiler hierarchy overview and see the impact of the code between the beginSample and endSample. You might be able to identify what chunks of code in your RoomStateHandler.Update() cause the most slowdown if you cut it into several samples.

One thing to consider when using the profiler is that a behavior taking 90% CPU isn’t bad if the CPU isn’t doing much else at the time. I find the “time ms” and “self ms” metrics to be much more useful than the percents under “total” and “self”.

Thank you but I’m not using JavaScript. Everything is MonoBehaviour thus executing within the Unity Engine.

@MSplitz-PsychoK thank you for your insight, I’ll give it a try. However notice how time ms reflects the percentage shown. Still the subnodes’ total ms doesn’t sum up to the root node’s time ms

Most Unity internal functionality should be wrapped in profiler samplers. If there is some functionality that takes up some time and does not have a sample, you’d see an “Unaccounted Time between X and Y” sample (at least in Timeline view).
However, what you’re looking at is the Update() method sample of your RoomStateHandler. When the engine is calling that Method, it puts a sample around it. When that method (or any method called from within) calls into Unity API, that will usually get a sample as well, which is what you’re seeing. Therefore, most of the time you’re missing would be your own code, that is not wrapped in samplers. Deep profiling will inject samplers for every Managed (Mono/C#) Method. You can manually do the same with the API @MSplitz-PsychoK mentioned, or the newer:

Regarding Deep Profiling on Android. You’ll need a Mono build, IL2CPP is sadly not supported for Deep Profiling. (We can’t inject the samplers into AOT’d code on the fly, only JIT code). Next, Deep Profiling comes with an overhead so it is not running this way by default. You can enable it through adb though.

adb shell am start -n com.YourCompany.YourGame/com.unity3d.player.UnityPlayerActivity -e 'unity' '-deepprofiling'

You’ll need at least 2017.3 and currently, due to a bug in mono, the Scripting Runtime Version .Net 4.x Equivalent.

Regarding Allocations: Every new on a class type will allocate. Structs live on the stack and do not allocate themselves, unless something is causing boxing (such as making them nullable or passing them into a method that takes them as object). And yes, making an allocation takes time and so does garbage collection. You could turn on the graph for GarbageCollection and see, if that caused anything. However, since your Update loops already took a combined 92% I kinda doubt that there is additional time spend on GC but I can’t tell definitively from the screenshot. (btw, you can also save the profiling data and attach it here, if you want to)

I’m guessing your RoomStateHandler is kind of a level load? It looks like it is instantiating, activating and deactivating a bunch of objects. Maybe you should think about pooling common Prefabs or using Scenes for this which you can load and unload additively and asynchronous?

2 Likes