Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List.Clear(); vs List<T> = new List<T>(); Garbage Collection Performance

I was working on a game with a lot of AI and noticed that my garbage collector was collecting objects 3-4 times a second which was really hurting performance and limiting the enemy count to 120 before the frame rate dropped. I profiled the code and found the culprit to be my collision detection code where I had a function being called multiple times a frame that was generating a list of projections onto axes. The code was like

public List<Vector2> foo()
{
    List<Vector2> projections = new List<Vector2>();

    // Calculate and return projections
}

There were a few other functions creating new lists every call as well and I switched out the generation of new lists each function call in favour of having a list of vector2s stored as a field of the class so that I am not calling new list every function call. This reduced the the garbage collections calls from 3-4 times a second to once every 10-20 seconds, allowing me to get 400-500 enemies on the map before frame rate dropped.

The new functions would have looked like:

public List<Vector2> foo()
{
    // projections is now a field of this class so we just clear it each function call
    projections.Clear();

    // Calculate and return projections
}

My question is why does the List.Clear method incur way less garbage collections than List = new List()? I have done some research into what happens to items in a list when you call clear and it appears as if they should be garbage collected as well when they have no references anymore, so my items are being created inside the function so when that function exits and the list is cleared next call, shouldn't the previous items be garbage collected as well?

My understanding is that the items cleared should be garbage collected just like when I call List = new List. Clearly something else is going on as my garbage collections drastically reduced when I switched to List.Clear(). Here are the links I used for trying to understand whats happening to my list items.

  • list.clear() vs list = new ArrayList<Integer>();
  • https://msdn.microsoft.com/en-us/library/dwb5h52a(v=vs.110).aspx
  • Does a list clear delete the objects previously contained
like image 710
Darcy Avatar asked Jan 27 '23 22:01

Darcy


1 Answers

When you clear a list, you're just setting the count to 0 and wiping the existing contents (which makes the old objects possibly collectable, if they are reference-types). The existing buffer (T[]) is retained, and can be reused for the next set. I'm guessing you usually have roughly the same number of projections each frame, so that means the same array can be used pretty much forever

However: if you new it, then a new default-sized buffer is allocated - it starts at something like 10 or 20, IIRC (edit: it looks like it is 4 now). As you add (Add) items, whenever it runs out of room, it will have to resize that buffer (it uses a doubling algorithm, so when you add the 21st item it'll double to 40, etc). If you have a lot of projections, this could mean allocating (and re-copying) a lot of times - so you could allocate a bunch of arrays that end up getting discarded... and of course the single list object.

It is these discarded "double it when it gets full" buffers of size 4, 8, 16, 32, 64, 128, 256, 512, 1024 etc - plus the actual list objects themselves - that you are seeing being collected; not your game objects.

In particlar: Vector2 is a struct, I believe - so: there is nothing to collect!

That can really add up!

like image 138
Marc Gravell Avatar answered Jan 30 '23 15:01

Marc Gravell