zoukankan      html  css  js  c++  java
  • 使用动态生成的委托提高调用动态程序集的性能

    前言

    在一些时候,我们需要动态生成一个函数,例如最近银河发表的一篇随笔当中提及到的《画函数图形的C#程序(改进版)》。不久之前,我们伟大的老赵也发过一篇《方法的直接调用,反射调用与……Lambda表达式调用》,他也推荐了《Dynamic Reflection Library》,但我不懂怎么应用在这里,因为这里的输入是字符串。自己用 CodeDom 实现了一下(其实这个我在没有来博客园之前就想实现了,当时刚学 .Net 不久,走了很多弯路才勉强可行,博客园真是个学习和提高的好地方^_^),发现性能提高了非常多,故在这里跟大家分享一下。

    思想和目标

    首先,普遍知识,MethodInfo.Invoke 需要查找元数据,很慢,然而通过一个委托调用某方法跟调用这个方法的速度差不多。因此,在需要经常重复调用一个方法的时候,不应该使用 MethodInfo.Invoke 这个方法。

    然而,偏偏这个方法是由用户动态输入的,那么,解决的思路是把用户输入编译成动态程序集中的一个方法,却不直接 MethodInfo.Invoke 它,而是在这个动态程序集中另外编写一个方法,返回一个委托,指向目标方法,然后 MethodInfo.Invoke 这个另外编写的方法,获得一个委托,之后就可以重复调用我们动态生成的目标方法却不用 MethodInfo.Invoke 了,而且是 TypeSafe 的,还有 IDE 的智能提示支持,多好!

    在没有 Lambda 表达式甚至没有匿名方法的时代(就是我刚有这个想法的时代),要实现这个很麻烦,这是因为委托的类型问题。(见过“XYZ 类型不能强制转换为 XYZ 类型”这种错误吗? XYZ = XYZ)

    自从有了 Linq 以后,BCL 中多了一系列形如 Func<T, TResult> 这样的委托类型,这种思想的实现就更容易了,Lambda 表达式还能免去我们多写一个方法的痛苦,实在太好了。

    那还等什么?编程实现!

    首先,编写核心的动态程序集代码以及编译。

    private static Assembly getDynamicAssembly()
    {
        string source = @"
        public class DynamicClass
        {
            public static System.Func<double, double> GetFunc()
            {
                return ( double x ) =>
                {
                    return x + x;
                };
            }
    
            public static double Add( double x )
            {
                return x + x;
            }
        }";
        var providerOptions = new Dictionary<string, string> { { "CompilerVersion", "v3.5" } };
        var cp = new CSharpCodeProvider(providerOptions);
    
        CompilerParameters cps = new CompilerParameters();
        cps.GenerateExecutable = false;
        cps.GenerateInMemory = true;
        cps.IncludeDebugInformation = false;
        cps.CompilerOptions += "/optimize /reference:System.Core.dll";
    
        return cp.CompileAssemblyFromSource(cps, source).CompiledAssembly;
    }

    说明一下,private static 是因为我还没有封装,只是写了一个 ConsoleApp 来证明想法。OK,source 里的明星方法是 GetFunc(),它返回一个 Func<double, double> 委托,委托指向的方法由 Lambda 表达式构造,这个 Lambda 表达式的内容就是我们要动态生成的内容。另外,在使用 CSharpCodeProvider 的时候默认是用 2.0 的,所以需要配置一下,具体可以查阅 MSDN Library,这里就直接 hard code 进去了。实际应用的时候,动态编译可能会出错,这里也忽略了。Add 方法是等一下要 MethodInfo.Invoke 的对比方法。

    接着,就是编写个方法测试一下效果,很简单:

    private static Stopwatch measureDynamicDelegate( Assembly asm, int length )
    {
        var func = asm.GetType("DynamicClass").GetMethod("GetFunc").Invoke(null, null) as Func<double, double>;
        var sw = Stopwatch.StartNew();
        for ( int i = 0; i < length; i++ )
        {
            func(1d);
        }
        sw.Stop();
        return sw;
    }

    可以看到,要使用很简单,只需写 func(1d),而且还有 IDE 的 Intellisense 支持:

    Intellisense Support

    然后就是其他测试了。这里就不贴出来了,代码等下提供下载。

    测试结果

    我分别在 Debug 和 Release 模式中测试了一下,发现相对差别不大,都是首次调用慢一点,以后的调用就快一点。左图为 Debug 模式,右图为 Release 模式,循环 65536 次。(点击放大)

    Debug Release

    测试代码下载

    最后

    这个貌似比老赵提供的数据快,但我相信原理是一样的。还有怎么在这个场景使用老赵的 Fast Reflection Library 和他推荐的 Dynamic Reflection Library 呢?欢迎大家指教。

    [EDIT]

    睡了一觉,想一想老赵的回复是对的,应用的场景是不同的,那么 Dynamic Reflection Library 呢?继续欢迎大家讨论,赐教!

  • 相关阅读:
    一些java的基础知识
    android基础AlertDialog使用
    Js+XML 操作 [ZT]
    [ASP.NET2.0] asp.net在ie7中使用FileUpload上传前预览图片 [ZT]
    C#对图片的几种简单处理 [ZT]
    使用 Bulk Copy 将大量数据复制到数据库 [ZT]
    html中name和id的区别 [ZT]
    两个分页存储过程
    C#常用的文件操作 (转)
    JSON
  • 原文地址:https://www.cnblogs.com/Diryboy/p/UsingDynamicDelegateToImproveReflectionPerf.html
Copyright © 2011-2022 走看看