简体   繁体   English

Autofac:使用DynamicProxy时提高性能的提示?

[英]Autofac: Tips for increasing performance when using DynamicProxy?

I just start using DynamicProxy2 today. 我今天才开始使用DynamicProxy2。 And found it caused significant performance drop. 并且发现它导致了显着的性能下降。

See the code below. 请参阅下面的代码。 Test1 is 10 times slower than Test2. Test1比Test2慢10倍。

Any tips for increasing performance when using DynamicProxy? 使用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. 在我的机器上,Test1大约需要45秒,Test2需要大约4.5秒。 After read Krzysztof Koźmic 's answer, I tried to put NotifyPropertyChangedInterceptor into singleton scope: 在阅读KrzysztofKoźmic的回答后,我尝试将NotifyPropertyChangedInterceptor放入单例范围:

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

that saved me about 4 seconds. 这节省了我大约4秒钟。 Now Test1 takes about 41 seconds. 现在Test1大约需要41秒。

Update 2: 更新2:

Test3 takes about 8.3 seconds on my machine. Test3在我的机器上大约需要8.3秒。 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. 因此,似乎单独使用Autofac或DynamicProxy性能不是一个非常大的问题(在我的项目中),但将它们组合在一起会导致性能下降。

    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. 我不熟悉Autofac如何在内部使用DP,但您不应该注意到性能影响很大。

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. 容器必须做更多工作来代理VM,实例化拦截器(因此你创建两个对象而不是一个)并将拦截器与代理连接起来。

If the caching is used right you will get a one-time performance hit when DP is actually generating the proxy type. 如果正确使用缓存,则当DP实际生成代理类型时,您将获得一次性性能。 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. 如果它是Castle.Proxies.TestViewModelProxy ,这意味着缓存工作正常。

If it's Castle.Proxies.TestViewModelProxy_1000000 then you're generating a new proxy type each time which understandably decreases your performance. 如果它是Castle.Proxies.TestViewModelProxy_1000000那么每次生成一个新的代理类型,这可以理解地降低您的性能。

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: 我尝试设置容器来手动构建代理,而不是使用AutofacContrib.DynamicProxy2扩展,因此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>();
        }
    }

This seems to run in about 13.5 seconds on my machine (for reference, your original test also takes about 45 seconds for me). 这似乎在我的机器上运行大约13.5秒(作为参考,你的原始测试对我来说也需要大约45秒)。

I was wondering if, as Krzysztof suggested, the AutofacContrib.DynamicProxy2 was doing something naive like trying to create a new ProxyGenerator each time. 我想知道,正如Krzysztof建议的那样,AutofacContrib.DynamicProxy2是否正在做一些天真的事情,比如每次尝试创建一个新的ProxyGenerator。 But when I tried to manually emulate this I got a OOM exception (I only have 2 gig in this machine however). 但是当我尝试手动模拟这个时,我得到了一个OOM异常(但是我在这台机器上只有2个演出)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM