2011-03-10 1 views
4

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()); 
     } 
    } 

Répondre

0

Quels types de numéros obtenez-vous? La performance diminue-t-elle dans l'utilisation réelle? Je ne suis pas familier avec la façon dont Autofac utilise DP en interne, mais vous ne devriez pas remarquer un impact important sur les performances.

Le conteneur doit faire plus de travail pour le proxy de la machine virtuelle, instancier l'intercepteur (de sorte que vous créez deux objets au lieu d'un seul) et attacher l'intercepteur au proxy.

Si la mise en cache est utilisée correctement, vous obtiendrez un résultat ponctuel lorsque DP générera le type de proxy. Le type devrait ensuite être réutilisé.

Vous pouvez facilement vérifier cela en inspectant le type du dernier proxy renvoyé.

Si c'est Castle.Proxies.TestViewModelProxy cela signifie que la mise en cache fonctionne correctement. Si c'est Castle.Proxies.TestViewModelProxy_1000000, alors vous générez un nouveau type de proxy à chaque fois ce qui, naturellement, diminue vos performances.

En général, l'impact sur les performances devrait être négligé par les normes de la vie réelle.

+0

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. –

+0

Hmm, ça ne va pas. Peut-être que Nick a un peu plus de perspicacité ... –

0

Pas une réponse mais j'ai pensé que j'ajouterais mon entrée.

Au lieu d'utiliser les extensions de AutofacContrib.DynamicProxy2 essayant d'installer le conteneur pour construire le proxy manuellement, donc ressemble Test1:

void Test1(int count) 
    { 
     var builder = new ContainerBuilder(); 

     ProxyGenerator pg = new ProxyGenerator(); 
     builder.Register(c => 
     { 
      var obj = pg.CreateClassProxyWithTarget(new TestViewModel(), c.Resolve < NotifyPropertyChangedInterceptor>()); 
      return (TestViewModel)obj; 
     }); 
     builder.RegisterType<NotifyPropertyChangedInterceptor>().SingleInstance(); 


     var container = builder.Build(); 
     for (int i = 0; i < count; i++) 
     { 
      container.Resolve<TestViewModel>(); 
     } 
    } 

Cela semble fonctionner en environ 13,5 secondes sur ma machine (pour référence , votre test original prend également environ 45 secondes pour moi).

Je me demandais si, comme Krzysztof l'a suggéré, l'AutofacContrib.DynamicProxy2 faisait quelque chose de naïf comme essayer de créer un nouveau ProxyGenerator à chaque fois. Mais quand j'ai essayé de l'émuler manuellement, j'ai eu une exception de MOO (je n'ai que 2 Go dans cette machine).

Questions connexes