Znajomy i ja testowaliśmy używając kompilowanych wyrażeń do tworzenia obiektów zamiast Activator.CreateInstance<T>
i uzyskaliśmy interesujące wyniki. Okazało się, że gdy uruchomiliśmy ten sam kod na każdym z naszych maszyn, zobaczyliśmy zupełnie przeciwne wyniki. Otrzymał oczekiwany wynik, znacznie lepszą wydajność ze skompilowanego wyrażenia, podczas gdy ja byłem zaskoczony, widząc, że Activator.CreateInstance<T>
wykonuje się 2x.Activator.CreateInstance <T> vs Compiled Expression. Odwrotna wydajność na dwóch różnych maszynach
Oba komputery prowadził kompilowany w .NET 4,0
Komputer 1 ma .NET 4.5 zainstalowany. Komputer 2 nie.
komputerowy 1 ponad 100.000 obiektów:
45ms - Type<Test>.New()
19ms - System.Activator.CreateInstance<Test>();
komputerowe 2 ponad 100.000 obiektów:
13ms - Type<Test>.New()
86ms - System.Activator.CreateInstance<Test>();
A oto kod:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
namespace NewNew
{
class Program
{
static void Main(string[] args)
{
Stopwatch benchmark = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
var result = Type<Test>.New();
}
benchmark.Stop();
Console.WriteLine(benchmark.ElapsedMilliseconds + " Type<Test>.New()");
benchmark = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
System.Activator.CreateInstance<Test>();
}
benchmark.Stop();
Console.WriteLine(benchmark.ElapsedMilliseconds + " System.Activator.CreateInstance<Test>();");
Console.Read();
}
static T Create<T>(params object[] args)
{
var types = args.Select(p => p.GetType()).ToArray();
var ctor = typeof(T).GetConstructor(types);
var exnew = Expression.New(ctor);
var lambda = Expression.Lambda<T>(exnew);
var compiled = lambda.Compile();
return compiled;
}
}
public delegate object ObjectActivator(params object[] args);
public static class TypeExtensions
{
public static object New(this Type input, params object[] args)
{
if (TypeCache.Cache.ContainsKey(input))
return TypeCache.Cache[input](args);
var types = args.Select(p => p.GetType());
var constructor = input.GetConstructor(types.ToArray());
var paraminfo = constructor.GetParameters();
var paramex = Expression.Parameter(typeof(object[]), "args");
var argex = new Expression[paraminfo.Length];
for (int i = 0; i < paraminfo.Length; i++)
{
var index = Expression.Constant(i);
var paramType = paraminfo[i].ParameterType;
var accessor = Expression.ArrayIndex(paramex, index);
var cast = Expression.Convert(accessor, paramType);
argex[i] = cast;
}
var newex = Expression.New(constructor, argex);
var lambda = Expression.Lambda(typeof(ObjectActivator), newex, paramex);
var result = (ObjectActivator)lambda.Compile();
TypeCache.Cache.Add(input, result);
return result(args);
}
}
public class TypeCache
{
internal static IDictionary<Type, ObjectActivator> Cache;
static TypeCache()
{
Cache = new Dictionary<Type, ObjectActivator>();
}
}
public class Type<T>
{
public static T New(params object[] args)
{
return (T)typeof(T).New(args);
}
}
public class Test
{
public Test()
{
}
public Test(string name)
{
Name = name;
}
public string Name { get; set; }
}
}
Nawiasem mówiąc, [tutaj] (http://stackoverflow.com/a/969327/217219) jest lepszym sposobem użycia 'Stopwatch'. – kprobst
Nigdy o tym nie myślałem, o –
Nie jest to odpowiedź, ale nie potrzebujesz do tego pamięci podręcznej słownika. C# static robi to za Ciebie. Zobacz http://stackoverflow.com/a/16162475/661933 – nawfal