转鹤冲天兄反射性能比较

替代反射调用的几种方式及性能测试
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\n");

        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("\r\n\r\n测试完成,任意键退出...");
        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 的一些“新”特性。

-------------------

思想火花,照亮世界
时间: 2024-10-14 20:13:59

转鹤冲天兄反射性能比较的相关文章

[转] 利用表达式树构建委托改善反射性能

最近搞一个系统时由于在比较关键地方用到反射了,所以要关注了一下反射的性能问题.搜索一下,不难搜到老赵的这篇文章,下面是一些杂乱的笔记.(建议先看老赵的文章) .Net4.0反射性能改善 看老赵的文章,老赵得到的结果是这样的: 1 00:00:00.0125539 (Directly invoke) 2 00:00:04.5349626 (Reflection invoke) 3 00:00:00.0322555 (Dynamic executor) 而我把代码搞下来自己运行得到这样的结果: 1

[转] 优化反射性能的总结(上)

反射是一种很重要的技术,然而它与直接调用相比性能要慢很多,因此如何优化反射性能也就成为一个不得不面对的问题. 目前最常见的优化反射性能的方法就是采用委托:用委托的方式调用需要反射调用的方法(或者属性.字段). 那么如何得到委托呢? 目前最常见也就是二种方法:Emit, ExpressionTree .其中ExpressionTree可认为是Emit方法的简化版本, 所以Emit是最根本的方法,它采用在运行时动态构造一段IL代码来包装需要反射调用的代码, 这段动态生成的代码满足某个委托的签名,因此

C# 之 反射性能优化

反射是一种很重要的技术,然而它与直接调用相比性能要慢很多,因此如何优化反射性能也就成为一个不得不面对的问题. 目前最常见的优化反射性能的方法就是采用委托:用委托的方式调用需要反射调用的方法(或者属性.字段). 目前最常见也就是二种方法:Emit, ExpressionTree .其中ExpressionTree可认为是Emit方法的简化版本, 所以Emit是最根本的方法,它采用在运行时动态构造一段IL代码来包装需要反射调用的代码, 这段动态生成的代码满足某个委托的签名,因此最后可以采用委托的方式

Java各种反射性能对比

对各种方法实现get方法的性能进行了一个测试. 总共有5个测试,,每个测试都是执行1亿次 1. 直接通过Java的get方法 2.通过高性能的ReflectAsm库进行测试 3.通过Java Class类自带的反射获得Method测试 4.使用Java自带的Property类获取Method测试 5.BeanUtils的getProperty测试 1 测试用Bean类 测试定义了如下一个bean类. public class SimpleBean { private String name; p

如何利用缓存机制实现JAVA类反射性能提升30倍

一次性能提高30倍的JAVA类反射性能优化实践 文章来源:宜信技术学院 & 宜信支付结算团队技术分享第4期-支付结算部支付研发团队高级工程师陶红<JAVA类反射技术&优化> 分享者:宜信支付结算部支付研发团队高级工程师陶红 原文首发于宜信支付结算技术团队公号:野指针 在实际工作中的一些特定应用场景下,JAVA类反射是经常用到.必不可少的技术,在项目研发过程中,我们也遇到了不得不运用JAVA类反射技术的业务需求,并且不可避免地面临这个技术固有的性能瓶颈问题. 通过近两年的研究.尝

C# 之 反射性能优化3

阅读目录 开始 用Delegate优化反射的缺点 用Delegate优化反射的优点 用CodeDOM优化反射的优点 如何用好CodeDOM? 用CodeDOM优化反射的缺点 能不能不使用委托? 根据反射密集程度选择优化方法 CodeDOM优化的误区 反射优化的总结 在前二篇博客中,我分别介绍了二种优化反射的方法: 1. Delegate:委托. 2. CodeDOM:动态代码生成. 这是二种截然不同的方法,性能的差距也很大. 今天的博客将着重比较它们的优缺点,以及给出它们的使用建议. 回到顶部

[转] 优化反射性能的总结(下)

阅读目录 开始 用Delegate优化反射的缺点 用Delegate优化反射的优点 用CodeDOM优化反射的优点 如何用好CodeDOM? 用CodeDOM优化反射的缺点 能不能不使用委托? 根据反射密集程度选择优化方法 CodeDOM优化的误区 反射优化的总结 在前二篇博客中,我分别介绍了二种优化反射的方法:1. Delegate:委托.2. CodeDOM:动态代码生成.这是二种截然不同的方法,性能的差距也很大.今天的博客将着重比较它们的优缺点,以及给出它们的使用建议. 用Delegate

java反射性能与优化

在最近的计划中,打算看看在不使用google protobuf的情况下,在原有的采用jackson作为json序列化工具的基础上,是否可以实现进一步的性能优化.主要是针对list的情况. 测试的时候选择了一个50个字段的对象,采用50条记录的list作为例子.因为大部分还都是可控的系统rpc交互,所以测试的时候选择了将字段用逗号分隔的方式. 在反射机制中,Reflection和BeanInfo两种均作了测试,method/field都做了缓存的前提,结果中与原生jackson序列化.反序列化性能

java动态表达式、原生调用、反射性能对比

package net.hs.cw.bomp.utils; import java.io.Serializable; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import org.mvel2.MVEL; /** * Id 模型 * * @author Gavin Hu * @create 2015/8/26 */ public class Id implements Seria