Система.Активатор.У CreateInstance (T) есть проблемы с производительностью, достаточно большие, чтобы препятствовать нам использовать его случайно?

тут System.Activator.CreateInstance(T) У метода есть проблемы с производительностью (поскольку я подозреваю, что он использует отражение), достаточно большие, чтобы препятствовать нам использовать его случайно?

5 ответов


как всегда, единственный правильный способ ответить на вопрос о производительности-это фактически измерить код.

вот пример помощью linqpad программа, которая проверяет:

  • активатор.Метод createinstance
  • новый T()
  • вызов делегата, который вызывает new T ()

как всегда, возьмите программу производительности с солью, здесь могут быть ошибки, которые искажают результаты.

выход (значения синхронизации в миллисекундах):

Test1 - Activator.CreateInstance<T>() 
12342 

Test2 - new T() 
1119 

Test3 - Delegate 
1530 

Baseline 
578 

обратите внимание, что вышеуказанные тайминги предназначены для 100.000.000 (100 миллионов) сооружений объекта. Накладные расходы не могут быть реальной проблемой для вашей программы.

поучительная вывод Activator.CreateInstance<T> занимает примерно в 11 раз больше времени, чтобы сделать ту же работу, что и new T() делает, и делегат занимает примерно в 1,5 раза. Обратите внимание, что конструктор здесь ничего не делает, поэтому я только попытался измерить накладные расходы разные методы.

Edit: я добавил базовый вызов, который не создает объект, но делает все остальное, и приурочил это. С этим в качестве базовой линии, похоже, делегат занимает на 75% больше времени, чем простой new () и активатор.CreateInstance занимает около 1100% больше.

однако, это микро-оптимизация. Если вам действительно нужно это сделать и извлечь последнюю унцию производительности некоторого критического по времени кода, я бы либо ручной код делегата для использования вместо этого, или если это невозможно, т. е. вам нужно предоставить тип во время выполнения, я бы использовал отражение.Emit для создания этого делегата динамически.

в любом случае, а вот мой настоящий ответ:

если у вас есть проблема с производительностью, сначала измерьте, где находится ваше узкое место. Да, вышеуказанные тайминги могут указывать на этот активатор.CreateInstance имеет больше накладных расходов, чем динамически построенный делегат, но может быть много большая рыба для жарки в вашей кодовой базе, прежде чем вы доберетесь (или даже должны добраться) до этого уровня оптимизации.

и просто чтобы убедиться, что я действительно отвечу на ваш конкретный вопрос: Нет, я не буду препятствовать использованию активатора.Метод createinstance. Вы должны знать, что он использует отражение, чтобы вы знали, что если это возглавляет ваши профилирующие списки узких мест, то вы можете что-то сделать с этим, но тот факт, что он использует отражение, не означает этого is узкое место.

программа:

void Main()
{
    const int IterationCount = 100000000;

    // warmup
    Test1();
    Test2();
    Test3();
    Test4();

    // profile Activator.CreateInstance<T>()
    Stopwatch sw = Stopwatch.StartNew();
    for (int index = 0; index < IterationCount; index++)
        Test1();
    sw.Stop();
    sw.ElapsedMilliseconds.Dump("Test1 - Activator.CreateInstance<T>()");

    // profile new T()
    sw.Restart();
    for (int index = 0; index < IterationCount; index++)
        Test2();
    sw.Stop();
    sw.ElapsedMilliseconds.Dump("Test2 - new T()");

    // profile Delegate
    sw.Restart();
    for (int index = 0; index < IterationCount; index++)
        Test3();
    sw.Stop();
    sw.ElapsedMilliseconds.Dump("Test3 - Delegate");

    // profile Baseline
    sw.Restart();
    for (int index = 0; index < IterationCount; index++)
        Test4();
    sw.Stop();
    sw.ElapsedMilliseconds.Dump("Baseline");
}

public void Test1()
{
    var obj = Activator.CreateInstance<TestClass>();
    GC.KeepAlive(obj);
}

public void Test2()
{
    var obj = new TestClass();
    GC.KeepAlive(obj);
}

static Func<TestClass> Create = delegate
{
    return new TestClass();
};

public void Test3()
{
    var obj = Create();
    GC.KeepAlive(obj);
}

TestClass x = new TestClass();
public void Test4()
{
    GC.KeepAlive(x);
}

public class TestClass
{
}

вот пример программы C# .NET 4.0, которая проверяет:

  • активатор.Метод createinstance
  • новый T()
  • вызов делегата, который вызывает new T ()
  • generic new ()
  • активатор.CreateInstance с использованием универсального
  • активатор.CreateInstance с использованием универсальных и нестандартных Привязок (например, для вызова внутреннего конструктора)

выход (значения синхронизации в миллисекундах от 2014 мясистой машины с x86 выпуск):

Test1 - Activator.CreateInstance<T>(): 8542
Test2 - new T() 1082
Test3 - Delegate 1214
Test4 - Generic new() 8759
Test5 - Generic activator 9166
Test6 - Generic activator with bindings 60772
Baseline 322

это принято из ответа Лассе В. Карлсена, но главное включает в себя дженерики. Обратите внимание, что указание Привязок замедляет активатор с использованием дженериков более чем в 6 раз!

using System;
using System.Reflection;
using System.Diagnostics;

namespace ConsoleApplication1
{
    public class TestClass
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            const int IterationCount = 100000000;

            // warmup
            Test1();
            Test2();
            Test3();
            Test4<TestClass>();
            Test5<TestClass>();
            Test6<TestClass>();

            // profile Activator.CreateInstance<T>()
            Stopwatch sw = Stopwatch.StartNew();
            for (int index = 0; index < IterationCount; index++)
                Test1();
            sw.Stop();
            Console.WriteLine("Test1 - Activator.CreateInstance<T>(): {0}", sw.ElapsedMilliseconds);

            // profile new T()
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test2();
            sw.Stop();
            Console.WriteLine("Test2 - new T() {0}", sw.ElapsedMilliseconds);

            // profile Delegate
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test3();
            sw.Stop();
            Console.WriteLine("Test3 - Delegate {0}", sw.ElapsedMilliseconds);

            // profile generic new()
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test4<TestClass>();
            sw.Stop();
            Console.WriteLine("Test4 - Generic new() {0}", sw.ElapsedMilliseconds);

            // generic Activator without bindings
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test5<TestClass>();
            sw.Stop();
            Console.WriteLine("Test5 - Generic activator {0}", sw.ElapsedMilliseconds);

            // profile Activator with bindings
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test6<TestClass>();
            sw.Stop();
            Console.WriteLine("Test6 - Generic activator with bindings {0}", sw.ElapsedMilliseconds);


            // profile Baseline
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                TestBaseline();
            sw.Stop();
            Console.WriteLine("Baseline {0}", sw.ElapsedMilliseconds);
        }

        public static void Test1()
        {
            var obj = Activator.CreateInstance<TestClass>();
            GC.KeepAlive(obj);
        }

        public static void Test2()
        {
            var obj = new TestClass();
            GC.KeepAlive(obj);
        }

        static Func<TestClass> Create = delegate
        {
            return new TestClass();
        };

        public static void Test3()
        {
            var obj = Create();
            GC.KeepAlive(obj);
        }

        public static void Test4<T>() where T : new()
        {
            var obj = new T();
            GC.KeepAlive(obj);
        }

        public static void Test5<T>()
        {
            var obj = ((T)Activator.CreateInstance(typeof(T)));
            GC.KeepAlive(obj);
        }

        private const BindingFlags anyAccess = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

        public static void Test6<T>()
        {
            var obj = ((T)Activator.CreateInstance(typeof(T), anyAccess, null, null, null));
            GC.KeepAlive(obj);
        }

        static TestClass x = new TestClass();
        public static void TestBaseline()
        {
            GC.KeepAlive(x);
        }
    }
}

Это зависит от вашего варианта использования. Если вам нужна очень высокая производительность и вы создаете много объектов, используйте Activator.CreateInstance может быть проблема.

но в большинстве случаев это будет достаточно быстро, и это очень мощный способ создания объектов.

фактически, большинство контейнеров IoC / локаторов служб / как бы вы их ни называли, используют этот метод для создания объекта запрашиваемого типа.

Если вы беспокоитесь, что производительность недостаточно хороша, то вы следует выполнить профилирование вашего приложения и измерить, если у вас есть узкое место и где оно находится. Я предполагаю, что вызов Activator.CreateInstance не будет вашей проблемой.


Да есть разница в производительности между вызовом

(MyClass)Activator.CreateInstance(typeof(MyClass));

и

new MyClass();

, где последняя быстрее. Но определение того, является ли падение скорости достаточно большим, зависит от вашего домена. В 90% случаев это не проблема. Также обратите внимание, что для типов значений, Activator.CreateInstance снова медленнее из-за анбоксинг участвует.

но вот подвох: для общих типов они похожи. new T() внутренние вызовы Activator.CreateInstance<T>(), который, в свою очередь, называет объектом типа runtimetype.CreateInstanceDefaultCtor(...). Поэтому, если у вас есть общий метод для создания нового экземпляра T, тогда это не имеет значения, хотя и имеет new() ограничения и вызов new T() гораздо более читабельно. Вот!--21-->соответствующей ссылке на эту тему Джон Скит.


да, на самом деле у него проблема с производительностью (по сравнению с new()) так как он использует Reflection и статический компилятор проверяет специально, когда вы передаете ему параметры (отправляя параметры в конструктор класса) вместо использования конструктора по умолчанию (как показано ниже)

//Too bad!!!
T someResult = (T)Activator.CreateInstance(
 typeof(T),   
 //parameter
 new object[] {id}
);

использовать его или нет, на мой взгляд, зависит от двух вещей:

сначала ваш тип приложения и, конечно, это масштаб (и это типичный трафик)

и второй (и что более важно) как и где вы используете Activator.CreateInstance метод, например, если вы используете его для каждого запроса с одним или несколькими параметрами конструктора (как я уже упоминал, использование с параметрами конструктора почти на одну десятую медленнее, чем использование без параметров (конструктор по умолчанию)), производительность вашего приложения ухудшается почти в значительной степени, но для другого экземпляра, если вы использовать его один раз (например, на application_start) и без параметр конструктора он почти действует как new ключевое слово

вот подробное сравнение бенчмарка между new(),Activator.CreateInstance и Type.GetInstance()

новый vs Func vs активатор.CreateInstance ()