点击这里查看原文

  1. 输出结果:
  • 执行环境:windows开发机,release模式
  • 时间单位:毫秒,越小,速度越快。
  • rps是每秒调用次数
direct   ret : 10000000,         time : 8,       RPS : 1250000000
lamd     ret : 10000000,         time : 31,      RPS : 322580645
expr     ret : 10000000,         time : 492,     RPS : 20325203
refl     ret : 10000000,         time : 3758,    RPS : 2660989
  1. 输出解释:
  • 直接调用函数,速度非常快。可能编译器做了优化。
  • 用lambda包装,调用函数的速度也很快。
  • 用编译并缓存后的expression,速度会慢很多,是正常情况下的5-10%不到
  • 用反射的方式调用方法,速度最慢,调用瓶颈就是在invoke本身,和创建的那个参数数组基本没关系。
  1. 结论
  • 如果追求极致效率,应该直接调用,或者用生成代码的方式批量生成调用封装代码。
  • 如果整个服务器每秒调用次数在几十万,可以用缓存的 Compiled Expression,浪费的开销基本可以忽略。
  • 反射Invoke去调用函数,效率非常低,根据以上测试看,单线程260万次/秒,如果自己代码每秒使用几万次,也是可以的。如果是做UI开发,可以放心使用,瓶颈一般不会在这里。游戏开发需要慎重考虑,如果整体调用次数很高,可能会导致掉帧。
  • 以上数据是单核情况下,服务器都有多少个核心N,以上数据可以乘以N。
  1. 测试代码:
    对expression代码不熟悉,部分代码抄自网络。
using commcs;
using commcs.wsrpc.msg;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace TestMain.TestReflectionPerformance
{
    public class ClientMsgController
    {
        public int handle1(int a, int b)
        {
            return a + b;
        }
    }
    public class TestReflectionPerformance
    {
        private const int N = 10000000;
        public static void Test()
        {
            testDirectCall();
            testLambdaCall();
            testDelegate();
            testReflect();
        }

        static void print(string label,int ret,long startTime, long endTime)
        {
            double rps =  N *1.0 / (endTime - startTime) * 1000;
            Console.WriteLine("{0} \t ret : {1},\t time : {2},\t RPS : {3}", label, ret, endTime - startTime, (long)rps);
        }

        private static void testDirectCall()
        {
            var obj = new ClientMsgController();
            var n = 0;
            long startTime = MyTime.Now();
            Func<ClientMsgController, int, int, int> lamd = (t, a, b) => t.handle1(a, b);
            for (int i = 0; i < N; i++)
            {
                n = obj.handle1(n, 1);
            }
            long endTime = MyTime.Now();
            print("direct", n, startTime, endTime);
        }

        private static void testLambdaCall()
        {
            var obj = new ClientMsgController();
            var n = 0;
            long startTime = MyTime.Now();
            Func<ClientMsgController,int,int,int> lamd = (t, a,  b) =>  t.handle1(a, b);
            for (int i = 0; i < N; i++)
            {
                n = lamd(obj,n, 1);
            }
            long endTime = MyTime.Now();
            print("lamd", n, startTime, endTime);
        }

        private static void testDelegate()
        {
            var obj = new ClientMsgController();
            var method = obj.GetType().GetMethod("handle1");
            var n = 0;
            long startTime = MyTime.Now();
            var mcall = CreateDelegate(method);

            for (int i = 0; i < N; i++)
            {
                n = (int)mcall.Invoke(obj, n, 1);
            }
            long endTime = MyTime.Now();
            print("expr", n, startTime, endTime);            
        }

        private static void testReflect()
        {
            var obj = new ClientMsgController();
            var method = obj.GetType().GetMethod("handle1");
            var n = 0;
            long startTime = MyTime.Now();
            var mcall = CreateDelegate(method);

            for (int i = 0; i < N; i++)
            {
                n = (int)method.Invoke(obj, new object[] { n, 1 });
            }
            long endTime = MyTime.Now();
            print("refl", n, startTime, endTime);
        }

        public delegate object MethodInvoker(object instance, params object[] parameters);


        public static MethodInvoker CreateDelegate(MethodInfo method)
        {
            //ExceptionHelper.CheckArgumentNull(method, "method");
            //if (method.IsGenericMethodDefinition)
            //{
            //    // 不对开放的泛型方法执行绑定。
            //    throw ExceptionHelper.BindTargetMethod("method");
            //}
            // 要执行方法的实例。
            ParameterExpression instanceParam = Expression.Parameter(typeof(object));
            // 方法的参数。
            ParameterExpression parametersParam = Expression.Parameter(typeof(object[]));
            // 构造参数列表。
            ParameterInfo[] methodParams = method.GetParameters();
            Expression[] paramExps = new Expression[methodParams.Length];
            for (int i = 0; i < methodParams.Length; i++)
            {
                paramExps[i] = Expression.Convert(
                    Expression.ArrayIndex(parametersParam, Expression.Constant(i)),
                    methodParams[i].ParameterType);
            }
            // 静态方法不需要实例,实例方法需要 (TInstance)instance
            Expression instanceCast = method.IsStatic ? null :
                Expression.Convert(instanceParam, method.DeclaringType);
            // 调用方法。
            Expression methodCall = Expression.Call(instanceCast, method, paramExps);
            // 添加参数数量检测。
            //methodCall = Expression.Block(GetCheckParameterExp(parametersParam, methodParams.Length), methodCall);
            // return Expression.Lambda<MethodInvoker>(GetReturn(methodCall, typeof(object)),
            //    instanceParam, parametersParam).Compile();
            return Expression.Lambda<MethodInvoker>(Expression.Convert(methodCall, typeof(object)), instanceParam, parametersParam).Compile();
        }
    }

}