TJF
TJF

Reputation: 2258

.NET pre-allocating memory vs ad-hoc allocation

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
alt text

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 alt text

Upvotes: 3

Views: 1092

Answers (2)

Henk Holterman
Henk Holterman

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

Reed Copsey
Reed Copsey

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

Related Questions