开发者

Autofac: Tips for increasing performance when using DynamicProxy?

I just start using DynamicProxy2 today. And found it caused significant performance drop.

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

Any tips for increasing 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's 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 read Krzysztof Koźmic's answer, I tried to put NotifyPropertyChangedInterceptor into singleton scope:

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

that saved me about 4 seconds. Now Test1 takes about 41 seconds.

Update 2:

Test3 takes about 8.3 seconds on my machine. So it seems using Autofac or DynamicProxy alone performance is not a very big problem (in my project), but combining them together开发者_开发问答 would cause great performance drop.

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


What sorts of numbers are you getting? Is the performance drop noticeable in real life usage?

I'm not familiar with how Autofac uses DP internally but you shouldn't notice big performance impact.

The container has to do more work to proxy the VM, instantiate the interceptor (so you're creating two objects instead of just one) and attach the interceptor with the proxy.

If the caching is used right you will get a one-time performance hit when DP is actually generating the proxy type. The type should then be reused.

You can easily check that by inspecting the type of the last proxy returned.

If it's Castle.Proxies.TestViewModelProxy that means caching works fine.

If it's Castle.Proxies.TestViewModelProxy_1000000 then you're generating a new proxy type each time which understandably decreases your performance.

In general the performance impact should be neglectable by real life standards.


Not an answer but thought I'd add my input.

Instead of using the AutofacContrib.DynamicProxy2 extensions I tried setting up the container to build the proxy manually, so Test1 looks like:

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

This seems to run in 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, the AutofacContrib.DynamicProxy2 was doing something naive like trying to create a new ProxyGenerator each time. But when I tried to manually emulate this I got a OOM exception (I only have 2 gig in this machine however).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜