Je commence juste à utiliser DynamicProxy2 aujourd'hui. Et constaté qu'il a causé une baisse significative des performances.Autofac: Conseils pour augmenter les performances lors de l'utilisation de DynamicProxy?
Voir le code ci-dessous. Test1 est 10 fois plus lent que Test2.
Des conseils pour augmenter les performances lors de l'utilisation de DynamicProxy?
class Program
{
public void Main()
{
for (int i = 0; i < 3; i++)
{
var stopWatch = Stopwatch.StartNew();
int count = 1 * 1000 * 1000;
Test1(count);
//Test2(count);
long t = stopWatch.ElapsedMilliseconds;
Console.WriteLine(t.ToString() + " milliseconds");
Console.WriteLine(((double)count/(t/1000)).ToString() + " records/1 seconds");
}
}
void Test1(int count)
{
var builder = new ContainerBuilder();
builder.RegisterType<TestViewModel>()
.EnableClassInterceptors()
.InterceptedBy(typeof(NotifyPropertyChangedInterceptor));
builder.RegisterType<NotifyPropertyChangedInterceptor>();
var container = builder.Build();
for (int i = 0; i < count; i++)
{
container.Resolve<TestViewModel>();
}
}
void Test2(int count)
{
var builder = new ContainerBuilder();
builder.RegisterType<TestViewModel>();
var container = builder.Build();
for (int i = 0; i < count; i++)
{
container.Resolve<TestViewModel>();
}
}
}
public class TestViewModel : INotifyPropertyChanged
{
[Notify]
public virtual string Value { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
/// <summary>
/// Copied from: http://serialseb.blogspot.com/2008/05/implementing-inotifypropertychanged.html
/// </summary>
public class NotifyPropertyChangedInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
// let the original call go through first, so we can notify *after*
invocation.Proceed();
if (invocation.Method.Name.StartsWith("set_"))
{
string propertyName = invocation.Method.Name.Substring(4);
var pi = invocation.TargetType.GetProperty(propertyName);
// check that we have the attribute defined
if (Attribute.GetCustomAttribute(pi, typeof(NotifyAttribute)) == null)
return;
// get the field storing the delegate list that are stored by the event.
FieldInfo info = invocation.TargetType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(f => f.FieldType == typeof(PropertyChangedEventHandler))
.FirstOrDefault();
if (info != null)
{
// get the value of the field
PropertyChangedEventHandler evHandler = info.GetValue(invocation.InvocationTarget) as PropertyChangedEventHandler;
// invoke the delegate if it's not null (aka empty)
if (evHandler != null)
evHandler.Invoke(invocation.TargetType, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Mise à jour:
Sur ma machine, prend environ 45 Test1 secondes Test2 prend environ 4,5 secondes. Après avoir lu la réponse de Krzysztof Koźmic, j'ai essayé de mettre NotifyPropertyChangedInterceptor dans le périmètre singleton:
builder.RegisterType<NotifyPropertyChangedInterceptor>().SingleInstance();
qui m'a sauvé environ 4 secondes. Maintenant Test1 prend environ 41 secondes.
Mise à jour 2: sur ma machine
Test3 prend environ 8,3 secondes. Donc, il semble que l'utilisation d'Autofac ou de DynamicProxy ne soit pas un problème majeur (dans mon projet), mais les combiner ensemble entraînerait une baisse des performances.
public void Test3(int count)
{
var generator = new Castle.DynamicProxy.ProxyGenerator();
for (int i = 0; i < count; i++)
{
generator.CreateClassProxy(typeof(TestViewModel),
new NotifyPropertyChangedInterceptor());
}
}
Merci pour votre réponse détaillée. Sur ma machine, Test1 prend environ 45 secondes, soit environ 20 000 objets/seconde. Cela va certainement causer des baisses de performances notables dans mon vrai projet. J'ai vérifié le type du dernier proxy, c'est exactement "Castle.Proxies.TestViewModelProxy". Donc, il semble que la mise en cache fonctionne bien. J'ai essayé de mettre NotifyPropertyChangedInterceptor dans la portée de Singleton, cela m'a sauvé environ 4 secondes. Mais encore trop lent. –
Hmm, ça ne va pas. Peut-être que Nick a un peu plus de perspicacité ... –