Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Understanding unitys garbage collector.

Discussion in 'Scripting' started by Sandler, Nov 22, 2015.

  1. Sandler

    Sandler

    Joined:
    Nov 6, 2015
    Posts:
    240
    Hey guys im kinda confused how the garbage collector in c# works.
    I got following code inside of my update function:

    Code (CSharp):
    1.  
    2. for (int m = 0; m < amC; m++)
    3.         {
    4.         EdgeCollider2D mf = allMeshes[m];
    5.         for (int i = 0; i < mf.pointCount; i++)
    6.          {
    7.                  point = mf.points[i];
    8.          }
    9. }
    10.  
    The point is set about 600 times per update circle.
    But it seems to be enough that the gc starts collectin and takes around 16-20 ms, all 2 seconds.
    This drops my framerate from 1000 fps to ~55 fps. But i dont create any objects?? Is this normal or did i do something wrong.
    Thanks alot!
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Most data like this in Unity that has an array of points/verts/etc are actually copies of the underlying array every time you access them (this is really annoying IMO, they really should offer a cleaner way to read/write this data without creating new arrays every time).

    So as you loop over the 'pointCount' and read the 'points' array, a brand new copy is being created every time.

    Instead copy the array up front, then loop over the copy:

    Code (csharp):
    1.  
    2. for (int m = 0; m < amC; m++)
    3. {
    4.     var points = allMeshes[m].points;
    5.     for (int i = 0; i < points.Length; i++)
    6.     {
    7.         point = points[i];
    8.     }
    9. }
    10.  
     
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    @lordofduct has it right.

    I believe the logic is that meshes exist in the graphics side of your hardware. Messing directly with graphics memory isn't a good idea. So Unity copies the points over into a new array when you access it.

    It should probably be called something else to make it obvious you aren't touching anything directly.
     
  4. Sandler

    Sandler

    Joined:
    Nov 6, 2015
    Posts:
    240
    Hey yes thats how i solved it.. but it still feels kinda strange. Im accessing the edge collider 2ds points. There is no mesh affected by it. The allMeshes name is a collection of EdgeCollder2D, wrong typo.

    Im new to c#, but arrays are references, still if those are arrays of structs?

    How can i know if unity gives me the original array or a copy?
    Thanks alot!
     
  5. btft

    btft

    Joined:
    Aug 11, 2015
    Posts:
    14
    From what reflection of Unity code tells me, EdgeCollider2D.points is extern call to another library, probably written in C++. Therefore I see a point of getting a copy, not a reference here, because probably that library handles more direct access to resources.
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Yes, they're reference types. Always reference types.

    It's almost always a copy.

    I'd say documentation would tell you, but it doesn't always tell you.

    You kind of sort of just have to know. Hence why I'm very displeased with their interface for it. It really should be a function like "GetPoints" or something, rather than a property.

    And they really should have a way to pass in an optional array or list to be populated with the data. So that you can recycle your objects more easily. Sort of how things like Stream.Read works.
     
    Kiwasi and hippocoder like this.
  7. btft

    btft

    Joined:
    Aug 11, 2015
    Posts:
    14
    Property's get {} is same as GetProperty() for compiler. No idea whay you're implying @lordofduct .
     
  8. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I think he is referring to having an api call named GetPoints() so that the end user doesn't have to guess at the missing documentation.
     
    lordofduct and Kiwasi like this.
  9. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    A property implies minimal overhead accessing a field of a class. It enforces encapsulation, but doesn't impose the assumption a large memory allocation might occur.

    Something like a Mesh, or EdgeCollider could easily return a very large array (especially a mesh). Having it a property (which looks a lot like a field, the entire point of properties) doesn't openly convey the large allocation that's about to happen.

    Hence OP's problem. They see property, they think "oh, I'll just access this array", and they never notice that they're allocating a whole new array every tick of the loop. Imagine if this were a mesh of say 2000 verts (a reasonable number of verts for a mesh to have), that's a 24KB array being allocated 2000 times every read. That's 48MB of memory created and then needing GC in a matter of a millisecond.

    Where as a function, that looks and reads like a function, implies that work is being done. If the code above read:

    Code (csharp):
    1.  
    2. for(int m = 0; m < amC; m++)
    3. {
    4.     EdgeCollider mf = allMeshes[m];
    5.     for(int i = 0; i < mf.pointCount; i++)
    6.     {
    7.         point = mf.GetPoints()[i];
    8.     }
    9. }
    10.  
    You could see how that reads very quickly that "oh snap, a bunch of work is happening" (in C# terms... in Java not so obvious since the 'Get' name is so common... but not in .net).


    Precisely!
     
    Last edited: Nov 22, 2015
    Kiwasi likes this.
  10. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Yup. Its the same principle as replacing .rigidbody with GetComponent<Rigidbody>. Underneath they compile to identical code. But the second makes it obvious there is some expense to the operation.
     
  11. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    Note, I'm referencing MSDN standards:
    https://msdn.microsoft.com/library/ms229054(v=vs.100).aspx

    4 of the 6...
     
    Kiwasi likes this.
  12. btft

    btft

    Joined:
    Aug 11, 2015
    Posts:
    14
    I understand how it breaks MS guidelines on when not to use properties (especially access to resources in getter, blah!). :)
    My point was that you were quite unclear on your point, having @hippocoder say it in one sentence what took you 3 long posts. ;) However, I think GetPoints() might be misinterpreted in a same way as property's getter itself.
     
  13. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    You didn't make it clear that you understood MS guidelines.

    So I went into full swing detail.

    ;)


    I also don't really see the difference here:
    I just say it in the opposite order, and with slightly different words.
     
  14. btft

    btft

    Joined:
    Aug 11, 2015
    Posts:
    14
    Fair enough. ;)
    Do we even have any guidelines from devs where do all these externs refer to in Unity?
    In standard .NET or C# I might think of internal CLR calls but here I guess it just calls underlying hardware access libraries through Unity low level API?
    Or is that something else?
     
  15. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    We don't get access to the Unity side with out special enterprise licensing that isn't even priced and more a you call them and negotiate a license/contract.

    I bet that comes with some sort of documentation... and it probably also comes with a "you can't tell anyone this" agreement.