Есть ли у метода System.Activator.CreateInstance(T)
проблемы с производительностью (поскольку я подозреваю, что он использует отражение), достаточно большие, чтобы отговорить нас от его случайного использования?
Имеет ли System.Activator.CreateInstance (T) проблемы с производительностью, достаточно большие, чтобы отговорить нас от случайного использования?
Ответы (5)
Как всегда, единственный правильный способ ответить на вопрос о производительности - это измерить код.
Вот пример программы LINQPad, которая тестирует:
- Activator.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 раза больше. Обратите внимание, что конструктор здесь ничего не делает, поэтому я только попытался измерить накладные расходы различных методов.
Изменить: я добавил базовый вызов, который не создает объект, но выполняет все остальное, а также рассчитал время для этого. Исходя из этого, похоже, что делегат занимает на 75% больше времени, чем простой new (), а Activator.CreateInstance занимает примерно на 1100% больше времени.
Однако это микрооптимизация. Если вам действительно нужно это сделать и вытащить последнюю каплю производительности какого-то критичного по времени кода, я бы либо вручную запрограммировал делегата для использования вместо него, либо, если это невозможно, т. Е. вам нужно указать тип во время выполнения, я бы использовал Reflection.Emit для динамического создания этого делегата.
Во всяком случае, вот и мой настоящий ответ:
Если у вас проблемы с производительностью, сначала измерьте, где находится ваше узкое место. Да, указанное выше время может указывать на то, что Activator.CreateInstance имеет больше накладных расходов, чем динамически созданный делегат, но в вашей кодовой базе может быть гораздо больше рыбы, которую нужно поджарить, прежде чем вы дойдете (или даже должны будете добраться) до этого уровня оптимизации. сильный>
И просто чтобы убедиться, что я действительно отвечу на ваш конкретный вопрос: нет, я бы не стал препятствовать использованию Activator.CreateInstance. Вы должны знать, что он использует отражение, чтобы вы знали, что если он превосходит ваши списки узких мест профилирования, тогда вы можете что-то с этим сделать, но тот факт, что он использует отражение, не означает, что он em> узкое место.
Программа:
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, которая тестирует:
- Activator.CreateInstance
- новый T ()
- вызов делегата, который вызывает new T ()
- общий новый ()
- Activator.CreateInstance с использованием универсального
- Activator.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
Это заимствовано из ответа Лассе В. Карлсена, но, что важно, включает в себя дженерики. Обратите внимание, что указание привязок замедляет Activator с использованием дженериков более чем в 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);
}
}
}
Expression
дерево для создания метода с аргументами. Это очевидно, если тип и количество аргументов фиксированы.
- person xanatos; 03.07.2015
Это зависит от вашего варианта использования. Если вам нужна очень высокая производительность и вы создаете много объектов, использование Activator.CreateInstance
может стать проблемой.
Но в большинстве случаев это будет достаточно быстро, и это очень мощный метод создания объектов.
Фактически, большинство контейнеров IoC / локаторов служб / как бы вы их ни называли, используют этот метод для создания объекта того типа, который вы запрашиваете.
Если вас беспокоит, что производительность недостаточно высока, вам следует выполнить профилирование своего приложения и определить, есть ли у вас узкое место и где оно находится. Я предполагаю, что звонок в Activator.CreateInstance
не будет вашей проблемой.
Да, есть разница в производительности между вызовами
(MyClass)Activator.CreateInstance(typeof(MyClass));
и
new MyClass();
где последний быстрее. Но определение того, достаточно ли велико падение скорости, зависит от вашего домена. В 90% случаев это не проблема. Также обратите внимание, что для типов значений Activator.CreateInstance
снова работает медленнее из-за распаковка.
Но вот загвоздка: для универсальных типов они похожи. new T()
внутренне вызывает Activator.CreateInstance<T>()
, который, в свою очередь, вызывает .CreateInstanceDefaultCtor (...). Поэтому, если у вас есть общий метод для создания нового экземпляра T
, это не имеет значения, хотя наличие ограничения new()
и вызов new T()
гораздо удобнее для чтения. Здесь соответствующая ссылка по теме Джон Скит.
Да, на самом деле у него есть проблема с производительностью (по сравнению с new()
), поскольку он использует Reflection
и проверки статического компилятора специально, когда вы передаете ему параметры (отправка параметров в конструктор класса) вместо использования конструктора по умолчанию (как показано ниже)
//Too bad!!!
T someResult = (T)Activator.CreateInstance(
typeof(T),
//parameter
new object[] {id}
);
Использовать это или нет, на мой взгляд, зависит от двух вещей:
Сначала тип вашего приложения и, конечно же, его масштаб (и это типичный трафик).
И, во-вторых (и что более важно), как и где вы используете метод Activator.CreateInstance
, например, если вы используете его в каждом запросе с одним или несколькими параметрами конструктора (как я уже упоминал, использование с параметрами конструктора почти на одну десятую медленнее, чем использование без параметров (конструктор по умолчанию )), производительность вашего приложения ухудшается почти в значительной степени, но в другом случае, если вы используете его один раз (например, в application_start) и без параметра конструктора, оно почти действует как new
ключевое слово
вот подробное сравнение результатов между _6 _, _ 7_ и Type.GetInstance()
новый vs Func vs Activator.CreateInstance ()
new T
, оно фактически используетSystem.Activator
? - person Pacerier   schedule 20.05.2011