Search Unity

How to debug massive GC.Alloc (>10MB) on specific frames, profiler is a real black box

Discussion in 'Scripting' started by Whatever560, Jul 24, 2018.

  1. Whatever560

    Whatever560

    Joined:
    Jan 5, 2016
    Posts:
    519
    I have specific events with huge GC.Allocs causing FPS to stutter and I can't figure out how to hunt them down. I have some heavy code happening or some prefabs loading at those times and it mostly happens in coroutines. Here is a screenshot of the profiler and the code probably running at this point.

    Any idea on how to start?

    If I may say, the rest of the game runs pretty smoothly. I keep my code healthy with good practices.

    I want to point out this specific issue : Sometimes there is no stutter if I add the first yield statement in the following code. However this statement makes the game like "waiting for something for ages" like 3-4sec between all actions of the game (based on the coroutine queue). If I remove it everything is instantaneous with some local frame freeze (< 1s)

    Thanks for your help


    upload_2018-7-24_13-11-45.png

    Code (CSharp):
    1. private IEnumerator AnimationCoroutine(Action action, object[] froms)
    2.         {
    3.             //*****************
    4.             //
    5.             //If I add this, I got no more spikes but my coroutines take ages to launch.
    6.             //
    7.             // yield return null;
    8.             //*************************************************
    9.  
    10.             m_AnimationDone = false;
    11.             var timer = Time.time;
    12.             try
    13.             {
    14.                 action();
    15.             }
    16.             catch (Exception e)
    17.             {
    18.                 Debug.LogException(e);
    19.                 Debug.LogError($"Exception happened into coroutine {from}. See previous exception stack trace");
    20.                 m_AnimationDone = true;
    21.             }
    22.             var throttle = ThrottleTimer.GetThrottleTimer(ThrottleTimer.ANIM_CHECK_TIMER, 2);
    23.             while (!m_AnimationDone)
    24.             {
    25.                 if (Time.time - timer > 7 )
    26.                 {
    27.                     Debug.LogErrorFormat("Quitting Blocking animation from {0} taking more than {1}s", from, (Time.time - timer));
    28.                     m_AnimationDone = true;
    29.                 }
    30.                 yield return null;
    31.             }
    32.  
    33.         }
     
  2. FlashMuller

    FlashMuller

    Joined:
    Sep 25, 2013
    Posts:
    451
    Some thoughts:
    Run the code without being a coroutine and using Deep Profile for a better understanding.
    Debug.Log can be part of the problem as it is quite heavy on performance and gc.
    try / catch should not be part of a successful program execution, as it will generate garbage collection for the created exception e.
     
  3. Whatever560

    Whatever560

    Joined:
    Jan 5, 2016
    Posts:
    519
    Thanks for your answer.
    - I'll try deep profiling, you say this as if it was not possible to use it to see what happens in coroutines. Is it the case ?
    - I'm pretty sure you're right, though 10MB seems a huge lot for the little logging I'm doing, I'll check this.
    - The try catch triggers only if an uncontrolled exception occurs in the Action() for debugging purposes. There is none atm so it seems out of cause