zoukankan      html  css  js  c++  java
  • 转鹤冲天兄反射性能比较

    替代反射调用的几种方式及性能测试
    2013-03-31 17:25 by 鹤冲天, 6065 阅读, 21 评论, 收藏, 编辑
    
    
    园子里和这个话题的相关文章比较多,本文是旧话重提,外加个小的总结。主要因为近期看到很多同事、朋友都已经使用 VS2012 进行 .NET 4.5 开发了,却还在大量使用反射,不知道用新的方式。或有所了解,但又害怕性能不好不敢大胆去用。
    
    本文以如下类为例:
    
    
    public class MyMath
    {
        public int Add(int a, int b)
        {
            return a + b;
        }
    }
     
    
    替代反射的几种方式
    
    倒序说吧,从最先进最简单的开始。
    
    1. dynamic 调用
    
    .NET 4 引入了 dynamic 类型,可以使用如下方式来完成对 MyMath.Add 方法的动态调用:
    
    1
    2
     dynamic math = new MyMath();
    int result = math.Add(1, 2);
     
    
    非常简单,效率也不错,可以看后面的性能对比测试结果。
    
    但有一点要注意, dynamic 遵守 .NET 的访问级别限定,会对成员进行可见性检查。也就是说,只能 dynamic 调用 public 成员;当然,如果是同一程序集内部,internal 成员也是可以访问的。
    
    2. Expression Tree 编译调用
    
    Expression Tree 是 .NET 3.5 引入的。简单地,我们可以使用 lambda 构建一颗 Expression Tree: 
    
    
     var math = new MyMath();
    Expression<Func<int, int, int>> add = (a, b) => math.Add(a, b);
     
    
    这种方法适合手工编码构建,还有另外一种方式可以动态构建:
    
    
    var add = typeof(MyMath).GetMethod("Add");
    var math = Expression.Parameter(typeof(MyMath));
    var a = Expression.Parameter(typeof(int), "a");
    var b = Expression.Parameter(typeof(int), "b");
    var body = Expression.Call(myMath, add, a, b);
    var lambda = Expression.Lambda<Func<MyMath, int, int, int>>(body, math, a, b);
     
    
    两种方式构建出的 Tree 是相同的。
    
    话归正题,构建出表达式树后,调用其 Compile 方法便可编译成一个委托,如下代码第 3 行:
    
    
     var math = new MyMath();
    Expression<Func<int, int, int>> addExpTree = (a, b) => math.Add(a, b);  // ExressionTree
    Func<int, int, int> add = addExpTree.Compile();                         //  编译成委托
    var result = add(1, 2);                                                 // 相加,结果为3
    dynamic 调用方法同,Expression Tree 编译出的委托方法也遵守 .NET 的访问级别限定,会对成员进行可见性检查,不能访问私有成员。
    
    3. 反射发出调用
    
    这里只介绍反射发出的一项技术 DynamicMethod,.NET 2.0 新增此类。
    
    使用 DynamicMethod 类在运行时定义轻量全局方法,然后使用委托执行这些方法。
    
    针对 MyMath.Add 方法,调用比前面两种方式复杂些:
    
    
    using System;
    using System.Linq.Expressions;
    using System.Reflection.Emit;
    
    public class MyMath {
        public int Add(int a, int b) { return a + b; }
    }
    
    class Program {
        static void Main(string[] args) {
            int result;
            var math = new MyMath();
            var count = 1000000;
    
            Console.WriteLine("数据量:" + count);
            Console.WriteLine("-----------------------------r
    ");
    
            using (Profiler.Step("循环:{0} ms")) {
                for (int i = 0; i < count; i++)
                    result = 1;
            }
            using (Profiler.Step("直接调用 :{0} ms")) {
                for (int i = 0; i < count; i++)
                    result = math.Add(i, i);
            }
            using (Profiler.Step("反射发出:{0} ms")) {
                var emitAdd = BuildEmitAddFunc();
                for (int i = 0; i < count; i++)
                    result = emitAdd(math, i, i);
            }
            using (Profiler.Step("表达式树:{0} ms")) {
                var expressionAdd = BuildExpressionAddFunc();
                for (int i = 0; i < count; i++)
                    result = expressionAdd(math, i, i);
            }
            using (Profiler.Step("dynamic 调用:{0} ms")) {
                dynamic d = math;
                for (int i = 0; i < count; i++)
                    result = d.Add(i, i);
            }
            using (Profiler.Step("反射调用:{0} ms")) {
                var add = typeof(MyMath).GetMethod("Add");
                for (int i = 0; i < count; i++)
                    result = (int)add.Invoke(math, new object[] { i, i });
            }
    
            Console.WriteLine("
    
    测试完成,任意键退出...");
            Console.ReadKey();
        }
        static Func<MyMath, int, int, int> BuildExpressionAddFunc() {
            var add = typeof(MyMath).GetMethod("Add");
            var math = Expression.Parameter(typeof(MyMath));
            var a = Expression.Parameter(typeof(int), "a");
            var b = Expression.Parameter(typeof(int), "b");
            var body = Expression.Call(math, add, a, b);
            var lambda = Expression.Lambda<Func<MyMath, int, int, int>>(body, math, a, b);
            return lambda.Compile();
        }
        static Func<MyMath, int, int, int> BuildEmitAddFunc() {
            var add = typeof(MyMath).GetMethod("Add");
            var dynamicMethod = new DynamicMethod("", typeof(int), new[] { typeof(MyMath), typeof(int), typeof(int) });
            var il = dynamicMethod.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Ldarg_2);
            il.Emit(OpCodes.Callvirt, add);
            il.Emit(OpCodes.Ret);
            return (Func<MyMath, int, int, int>)dynamicMethod.CreateDelegate(typeof(Func<MyMath, int, int, int>));
        }
    }
     
    
    Profiler 是我写的一个类,用于简化测试:
    
    using System;
    using System.Diagnostics;
    
    public class Profiler : IDisposable {
        private Stopwatch watch;
        private string message;
    
        private Profiler(string message) {
            this.watch = new Stopwatch();
            this.watch.Start();
            this.message = message;
        }
    
        public void Dispose() {
            watch.Stop();
            Console.WriteLine(message, watch.ElapsedMilliseconds);
            Console.WriteLine();
        }
    
        public static IDisposable Step(string message) {
            return new Profiler(message);
        }
    
        public static T Inline<T>(string message, Func<T> func) {
            using (new Profiler(message))
                return func();
        }
    }
     
    
    总结
    
    综上所述,我们可以使用 .NET 2.0 的 DynamicMethod,.NET 3.5 引入的 Expression Tree、.NET 4 新增的 dynamic 来替换反射调用,带来更好的性能。
    
    希望本文对大家有所帮助,也希望整天忙于项目、疲于工作的朋友抽点时间学习下 .NET Framework 的一些“新”特性。
    
    
    -------------------
    
    思想火花,照亮世界
  • 相关阅读:
    大数据开发速查表
    静态图像介绍
    get请求如何传递数组参数
    Redis 6.0 新增功能
    JVM 出现 StackOverflowError和OutOfMemoryError的可能
    golang超级mapper包
    dotnet vs java vs go
    [翻译]Jupyter notebook .NET Core 内核预览1
    .NET Core到底有多强?
    .net core 运行时事件(Runtime Events)
  • 原文地址:https://www.cnblogs.com/feige/p/6352241.html
Copyright © 2011-2022 走看看