Autofac: performance tips for using DynamicProxy?

I am just starting to use DynamicProxy2 today. And I found that this led to a significant decrease in performance.

See code below. Test1 is 10 times slower than Test2.

Any tips on improving performance when using 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 not null (aka empty) if (evHandler != null) evHandler.Invoke(invocation.TargetType, new PropertyChangedEventArgs(propertyName)); } } } } 

Update:

On my machine, Test1 takes about 45 seconds, Test2 takes about 4.5 seconds. After reading Krzysztof KoΕΊmic , I tried setting NotifyPropertyChangedInterceptor to the Singleton scope:

 builder.RegisterType<NotifyPropertyChangedInterceptor>().SingleInstance(); 

which saved me about 4 seconds. Test1 now takes about 41 seconds.

Update 2:

Test3 takes about 8.3 seconds on my machine. Thus, it seems that using only the Autofac or DynamicProxy function is not a very big problem (in my project), but combining them with each other will lead to a significant decrease in performance.

  public void Test3(int count) { var generator = new Castle.DynamicProxy.ProxyGenerator(); for (int i = 0; i < count; i++) { generator.CreateClassProxy(typeof(TestViewModel), new NotifyPropertyChangedInterceptor()); } } 
+4
source share
2 answers

What numbers do you get? Is performance loss noticeable when used in real life?

I am not familiar with how Autofac uses DP internally, but you should not notice a big impact on performance.

The container should do more work for the VM proxy server, create an instance of the interceptor (so that you create two objects instead of one) and attach the interceptor to the proxy server.

If caching is used correctly, you will get a one-time performance boost when DP actually generates a proxy type. Then the type must be reused.

You can easily verify this by checking the type of the last proxy returned.

If this is Castle.Proxies.TestViewModelProxy , it means caching is working fine.

If it is Castle.Proxies.TestViewModelProxy_1000000 , then each time you generate a new type of proxy, which, for obvious reasons, reduces your performance.

In general, the impact on performance should be negligible for real life standards.

0
source

Not an answer, but I thought I would add my contribution.

Instead of using the AutofacContrib.DynamicProxy2 extensions, I tried to configure the container to create a proxy manually, so Test1 looks like this:

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

It seems like it takes about 13.5 seconds on my machine (for reference, your original test also takes about 45 seconds for me).

I was wondering if, as Krzysztof suggested, AutofacContrib.DynamicProxy2 does something naive as every time it tries to create a new ProxyGenerator. But when I tried to manually imitate this, I got an OOM exception (I only have 2 gigabytes on this machine).

0
source

Source: https://habr.com/ru/post/1343129/


All Articles