Reputation: 2258
I'm working on some applications that require very low latency and push a lot of memory and was doing some testing of how e.g. allocating a list ad-hoc vs. pre-allocating and clearing a list performs. I was expecting the test runs that pre-allocate the memory to perform a lot faster but to my surprise they're actually slightly slower (when I let the test run for 10 minutes, the avg. difference is about 400ms).
Here is the test code that I used:
class Program
{
private static byte[] buffer = new byte[50];
private static List<byte[]> preAlloctedList = new List<byte[]>(500);
static void Main(string[] args)
{
for (int k = 0; k < 5; k++)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000000; i++)
{
List<byte[]> list = new List<byte[]>(300);
for (int j = 0; j < 300; j++)
{
list.Add(buffer);
}
}
sw.Stop();
Console.WriteLine("#1: " + sw.Elapsed);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000; i++)
{
for (int j = 0; j < 300; j++)
{
preAlloctedList.Add(buffer);
}
preAlloctedList.Clear();
}
sw.Stop();
Console.WriteLine("#2: " + sw.Elapsed);
}
Console.ReadLine();
}
}
Now, what's really interesting, I was running perfmon side by side and saw the following pattern which looks like I expected:
Green = Gen 0 collections
Blue = Allocated Bytes/sec
Red = %Time in GC
The console application below shows the test runtimes for #1 and #2
So, my question is, why is Test #1 faster than #2?
Obviously, I'd rather have the perfmon statistics of Test #2 in my app as there is basically no memory pressure, no GC collections, etc. yet #1 seems to be slightly faster?
Does List.Clear() carry that much overhead?
Thanks,
Tom
EDIT
I did another test, with the same setup but running the app with server GC enabled, now #2 becomes slightly faster
Upvotes: 3
Views: 1092
Reputation: 273274
Does List.Clear() carry that much overhead?
Yes, compared to a (single) GC.Collect(0)
, doing a few thousand calls to Clear()
might very well be slower.
For what I have learned, the dotNet memory system is very fast in allocating/deallocating shortlived memory blocks.
But be careful to carry conclusions from this simple test over to your real app.
Upvotes: 3
Reputation: 564433
I suspect the reason Test #1 is faster is that the garbage collection is happening on a separate thread, and the overhead of allocation is lower than the extra List<T>.Clear
call. Since none of these lists are large (just 300 references each), and they're all being created and unrooted in a tight loop, they'll all typically stay in Gen 0.
I've noticed this during profiling in the past - reusing a List<T>
and calling Clear on it is often slower than just reallocating. Clear()
actually clears the internal array as well as resets the parameters of the list, which I believe has (slightly) more overhead than the initial allocation of the list.
However, this example, in my opinion, really just shows that the GC in .NET is very, very efficient.
Upvotes: 4