Субж приводит к вызовам
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 На самом деле ожидал большего ускорения...)
Здравствуйте, artelk, Вы писали:
A>Субж приводит к вызовам Activator.CreateInstance, что есть относительно медленно.
Похожая темаАвтор:
Дата: 13.02.07
A> var ctor = typeof(T).GetConstructor(new Type[0]);
Кстати, это не сработает, если T — структура.
Здравствуйте, 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);
}
|
| |
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, nikov, Вы писали:
N>>Кстати, это не сработает, если T — структура.
Doc>Можно в начале проверить тип T, и для структуры в newT подставить () => new T();
Можно, но для структур это приведет к замедлению работы, т.к. new MyStruct() вырождается до default(MyStruct). По крайней мере, до тех пор пока не введут
Parameterless constructors in structs.
По идее, Activator.CreateInstance<T>() должен бы быть (еще более) оптимизирован и, в идеале, заинлайнен по самые помидоры.
Здравствуйте, nikov, Вы писали:
N>Похожая темаАвтор:
Дата: 13.02.07
Запустил
примерАвтор: nikov
Дата: 14.02.07
на новом фреймворке (предварительно увеличив 500000 в 10 раз). Результат:
testReflection0: 40
testReflection1: 42
testReflection2: 47
testGeneric: 520
Ну, примерно в 2 раза Activator.CreateInstance<T>() был таки ускорен с 2007 года.