简体   繁体   中英

Why is using a Func<> so much faster than using the new() constraint on a generic sequence creator

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();
            }
        }
    }
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM