where T: new()
От: artelk  
Дата: 30.10.14 23:34
Оценка:
Субж приводит к вызовам Activator.CreateInstance, что есть относительно медленно.
Код:

public class Foo<T> where T : new()
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public void DoSomething()
    {
        var obj = new T();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public void DoNothing(){}
}

public class Bar{}


Тест:
public class Program
{
    private static void Main()
    {
        const int N = 1000000;

        Thread.CurrentThread.Priority = ThreadPriority.Highest;
        var foo = new Foo<Bar>();
        var sw = Stopwatch.StartNew();
        for (int i = 0; i < N; i++)
        {
            foo.DoSomething();
            foo.DoNothing();
        }
        sw.Restart();
        GC.Collect();
        sw.Stop();

        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            foo.DoSomething();
        }
        sw.Stop();
        var time = sw.ElapsedTicks;

        GC.Collect();
        sw.Restart();
        for (int i = 0; i < N; i++)
        {
            foo.DoNothing();
        }
        sw.Stop();
        time -= sw.ElapsedTicks;

        Console.WriteLine(time);
        Console.ReadKey();
    }
}

Минимальное значение после серии запусков — ~288 тысяч тиков.

Немножко меняем:
public class Foo<T> where T : new()
{
    private static readonly Func<T> newT;

    static Foo()
    {
        var ctor = typeof(T).GetConstructor(new Type[0]);
        var dm = new DynamicMethod("Create", typeof(T), new Type[0], typeof(T), true);
        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Newobj, ctor);
        il.Emit(OpCodes.Ret);
        newT = (Func<T>)dm.CreateDelegate(typeof(Func<T>));
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public void DoSomething()
    {
        var obj = newT();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public void DoNothing(){}
}


Минимальное значение после серии запусков — ~42 тысячи тиков.
Итого, ускорение почти в 7 раз.

PS Теоретически, такое преобразование могло бы быть сделано компилятором.
PSS На самом деле ожидал большего ускорения...)
Re: where T: new()
От: nikov США http://www.linkedin.com/in/nikov
Дата: 31.10.14 02:15
Оценка: +1
Здравствуйте, artelk, Вы писали:

A>Субж приводит к вызовам Activator.CreateInstance, что есть относительно медленно.


Похожая тема
Автор:
Дата: 13.02.07


A> var ctor = typeof(T).GetConstructor(new Type[0]);


Кстати, это не сработает, если T — структура.
Re[2]: where T: new()
От: Sinix  
Дата: 31.10.14 06:08
Оценка: +1
Здравствуйте, nikov, Вы писали:

N>Кстати, это не сработает, если T — структура.

Ещё гаже: может сработать, может нет.

Чтоб два раза не вставать, для структур new T() не опасен (но это топикстартеру уже)
  пруф
                  Normal Int32:    28ms, ips: 3 450 834 239,18 : 100000000
                 Normal Object:  6569ms, ips:    15 221 036,08 : 100000000
                  Struct Int32:    28ms, ips: 3 491 169 087,79 : 100000000
                       TestInt:    28ms, ips: 3 490 730 365,51 : 100000000
                       TestObj:   485ms, ips:   205 813 278,37 : 100000000


        static void Main(string[] args)
        {
            Test<int>();
            Test<object>();
            TestStruct<int>();
            TestInt();
            TestObj();

            Console.ReadKey();
        }
        static void Test<T>() where T: new()
        {
            T x;
            int count = 100 * 1000 * 1000;
            Measure("Normal " + typeof(T).Name, () =>
            {
                for (int i = 0; i < count; i++)
                {
                    x = new T();
                }
                return count;
            });
        }
        
        static void TestStruct<T>() where T: struct
        {
            T x;
            int count = 100 * 1000 * 1000;
            Measure("Struct " + typeof(T).Name, () =>
            {
                for (int i = 0; i < count; i++)
                {
                    x = new T();
                }
                return count;
            });
        }        
        
        static void TestInt()
        {
            int x;
            int count = 100 * 1000 * 1000;
            Measure("TestInt", () =>
            {
                for (int i = 0; i < count; i++)
                {
                    x = new int();
                }
                return count;
            });
        }
            
        static void TestObj()
        {
            object x;
            int count = 100 * 1000 * 1000;
            Measure("TestObj", () =>
            {
                for (int i = 0; i < count; i++)
                {
                    x = new object();
                }
                return count;
            });
        }

        static void Measure(string name, Func<long> callback)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            
            var sw = Stopwatch.StartNew();
            var tmp = callback();
            sw.Stop();

            Console.WriteLine("{0,30}: {1,5}ms, ips: {2,16:N} : {3,9}", name, sw.ElapsedMilliseconds, tmp / sw.Elapsed.TotalSeconds, tmp);
        }
Re[2]: where T: new()
От: Doc Россия http://andrey.moveax.ru
Дата: 31.10.14 08:38
Оценка:
Здравствуйте, nikov, Вы писали:

N>Кстати, это не сработает, если T — структура.


Можно в начале проверить тип T, и для структуры в newT подставить () => new T();
Re[3]: where T: new()
От: artelk  
Дата: 31.10.14 09:20
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Здравствуйте, nikov, Вы писали:


N>>Кстати, это не сработает, если T — структура.


Doc>Можно в начале проверить тип T, и для структуры в newT подставить () => new T();

Можно, но для структур это приведет к замедлению работы, т.к. new MyStruct() вырождается до default(MyStruct). По крайней мере, до тех пор пока не введут Parameterless constructors in structs.

По идее, Activator.CreateInstance<T>() должен бы быть (еще более) оптимизирован и, в идеале, заинлайнен по самые помидоры.
Re[2]: where T: new()
От: artelk  
Дата: 31.10.14 09:34
Оценка:
Здравствуйте, nikov, Вы писали:

N>Похожая тема
Автор:
Дата: 13.02.07


Запустил пример
Автор: nikov
Дата: 14.02.07
на новом фреймворке (предварительно увеличив 500000 в 10 раз). Результат:

testReflection0: 40
testReflection1: 42
testReflection2: 47
testGeneric: 520


Ну, примерно в 2 раза Activator.CreateInstance<T>() был таки ускорен с 2007 года.
Отредактировано 31.10.2014 9:35 artelk . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.