13

Consider the following code...

In my tests for a RELEASE (not debug!) x86 build on a Windows 7 x64 PC (Intel i7 3GHz) I obtained the following results:

CreateSequence() with new() took 00:00:00.9158071
CreateSequence() with creator() took 00:00:00.1383482

CreateSequence() with new() took 00:00:00.9198317
CreateSequence() with creator() took 00:00:00.1372920

CreateSequence() with new() took 00:00:00.9340462
CreateSequence() with creator() took 00:00:00.1447375

CreateSequence() with new() took 00:00:00.9344077
CreateSequence() with creator() took 00:00:00.1365162

It seems that using a Func<> to define a delegate to create new objects is more than 6 times faster than calling "new T()" directly.

I find this slightly unexpected... I guess it's because of some inlining done by the Jitter, but I'd have thought that it would have been able to optimize the "new T()" just as well.

Does anyone have an explanation for this?

Maybe I'm making some mistake. (I've considered the effect the garbage collector might have, but rearranging the code and adding GC.Collect() and so on doesn't change the results significantly).

Anyway, here's the code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ConsoleApplication6
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();

            int repeats =    100;
            int count   = 100000;

            for (int outer = 0; outer < 4; ++outer)
            {
                sw.Restart();

                for (int inner = 0; inner < repeats; ++inner)
                {
                    CreateSequence<object>(count).Count();
                }

                Console.WriteLine("CreateSequence() with new() took " + sw.Elapsed);
                sw.Restart();

                for (int inner = 0; inner < repeats; ++inner)
                {
                    CreateSequence(count, () => new object()).Count();
                }

                Console.WriteLine("CreateSequence() with creator() took " + sw.Elapsed);
                Console.WriteLine();
            }
        }

        public static IEnumerable<T> CreateSequence<T>(int n) where T: new()
        {
            for (int i = 0; i < n; ++i)
            {
                yield return new T();
            }
        }

        public static IEnumerable<T> CreateSequence<T>(int n, Func<T> creator)
        {
            for (int i = 0; i < n; ++i)
            {
                yield return creator();
            }
        }
    }
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 10
    See https://msmvps.com/blogs/jon_skeet/archive/2011/08/22/optimization-and-generics-part-1-the-new-constraint.aspx – Jon Skeet Apr 16 '12 at 12:34
  • related: http://stackoverflow.com/questions/367577/why-does-the-c-sharp-compiler-emit-activator-createinstance-when-calling-new-in – nawfal May 10 '13 at 06:51

1 Answers1

14

The new() constraint only ensures that the type passed in has a parameterless constructor. If you actually call new T() (or whatever your type argument's name is), it actually does this:

Activator.CreateInstance<T>();

Which, at its core, uses reflection.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • 1
    Thanks (and to Jon too, for the interesting article). It seems like the new() constraint should be avoided when you need very fast code and you can use the "Func<> creator" approach instead! – Matthew Watson Apr 16 '12 at 12:40
  • 1
    @MatthewWatson: That's what I'd suggest – Adam Robinson Apr 16 '12 at 12:41
  • 1
    As to why that is, see http://stackoverflow.com/questions/367577/why-does-the-c-sharp-compiler-emit-activator-createinstance-when-calling-new-in – nawfal May 10 '13 at 06:50