Search Unity

Bug profiling bug in coroutine 2020.1

Discussion in 'Editor & General Support' started by laurentlavigne, Sep 12, 2020.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,365
    this will spam the console with
    upload_2020-9-11_19-58-17.png


    Code (CSharp):
    1. while (true)
    2.         {
    3.             UnityEngine.Profiling.Profiler.BeginSample("fill the input arrays");
    4.             //
    5.             emitterZero.CopyTo(emitterValues, 0);
    6.             Vector4 _tmp = new Vector4();
    7.             int skippy=0;
    8.             for (int i = 0; i < settings.Length; i++)
    9.             {
    10.                 foreach (var e in settings[i].emitters)
    11.                 {
    12.                     var pos = World2Grid(e.transform.position);
    13.                     _tmp.Set(i == 0 ? 1 : 0, i == 1 ? 1 : 0, i == 2 ? 1 : 0, i == 3 ? 1 : 0);
    14.                     if (pos.x >= 0 && pos.x < size.x && pos.y >= 0 && pos.y < size.y)
    15.                         emitterValues[pos.x + pos.y * size.x] += e.value * _tmp;
    16.                     if (skippy % 500 ==0)
    17.                         yield return null;
    18.                     skippy++;
    19.                 }
    20.             }
    21.             //
    22.             UnityEngine.Profiling.Profiler.EndSample();
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,914
    Interesting. I'm not sure this is a bug per-se, simply because the way iterator methods are implemented in C#, separation of the samples over a yield statement means the sample statements are actually executed in different method calls.

    C# iterators (which coroutines are an example of) are actually compiled into a class that implements IEnumerator. All of the code in your coroutine is basically split up into sections based on yield statement boundaries to create a state machine. Every time MoveNext is called on the object, the code runs one of those sections, and the state of the machine changes to whatever code block is after the yield statement.

    Here's a better explanation: https://csharpindepth.com/articles/IteratorBlockImplementation

    In other words.. the error you're getting is actually correct, as your sampler begin and end are technically not happening in the same stack frame.

    What would be the goal of sampling a coroutine like this anyway? If it worked, the sample would include the entire frame duration for all of the frames that the coroutine execution is split over. A large majority of that time is likely... well.. the entire rest of the game running, not this method.
     
    laurentlavigne likes this.
  3. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,365
    Ok that's interesting.
    I seem to remember that it used to work and allowed to conveniently profile a coroutine, that must have been in between the yield.
     
  4. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,461
    @PraetorBlue is correct and any return between a profiler Begin and End call would cause trouble. If you used ProfilerMarker.Auto it'd be fine (i.e. not throw errors and end the sample on leaving the scope/returning automatically) but the profiler marker wouldn't pic up profiling on returning to the coroutine again because that would need a new call to Auto.

    So really, in this case you'd ought to call EndSample before the yield return and BeginSample after.