繁体   English   中英

反射方法调用与委托调用的性能

[英]Performance of reflection method call vs delegate call

我的目标是编写一个弱类型的 TryParse方法,该方法基本上支持所有可用的struct类型(int,long,float ...)

public static bool TryParse(Type type, string s, out object obj)

该实现将调用所提供type参数的TryParse方法(如果type为int ,则将调用int.TryPase并将out值作为对象返回)。

我通过反射实现了它,但是有一个主要的性能损失(正如我预期的那样)

反思实施:

public static class ParserHelper
{
    public delegate bool TryParseDl(string str, out object obj);

    private static readonly HashSet<Type> ParsableStructs = new HashSet<Type>
    {
    typeof(int),
    typeof(uint),
    typeof(decimal),
    typeof(short),
    typeof(ushort),
    typeof(double),
    typeof(long),
    typeof(ulong),
    typeof(float),
    typeof(byte),
    typeof(sbyte)
    };

    public static readonly ReadOnlyDictionary<Type, TryParseDl> StructParsers;

    static ParserHelper()
    {
        StructParsers = new ReadOnlyDictionary<Type, TryParseDl>(CreateParsersForStructs());
    }

    /// Creates parsers for structs
            private static Dictionary<Type, TryParseDl> CreateParsersForStructs()
        {
            var parsers = new Dictionary<Type, TryParseDl>();
            foreach (var t in ParsableStructs)
            {
                parsers[t] = GetParserForStruct(t);
            }
            return parsers;
        }

    private static TryParseDl GetParserForStruct(Type targetType)
        {
            var methodInfo = targetType.GetMethod(
                "TryParse",
                BindingFlags.Public | BindingFlags.Static,
                Type.DefaultBinder,
                new[] { typeof(string), targetType.MakeByRefType() },
                null);

            return (string str, out object obj) =>
                {
                    if (string.IsNullOrEmpty(str))
                    {
                        obj = targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
                        return true;
                    }
                    var inputParameters = new object[] { str, null };
                    var tryParseResult = (bool)methodInfo.Invoke(null, inputParameters);
                    obj = inputParameters[1];
                    return tryParseResult;
                };
        }
}

这是性能测试:

public class Program
{
    public static void Main()
    {
        Stopwatch s = new Stopwatch();
        string str = "100";     
        s.Start();
        for(int j = 0;j<100;j++)
        {
            int i;
            int.TryParse(str,out i);

        }
        s.Stop();
        Console.WriteLine(s.Elapsed);
        s.Reset();
        s.Start();
        var parser = ParserHelper.StructParsers[typeof(int)];   
        for(int j = 0;j<100;j++)
        {                           
            object o;
            parser(str, out o);
        }

        s.Stop();
        Console.WriteLine(s.Elapsed);
    }
}

平均结果是反射调用比直接调用慢了200倍(在100次重复上)。 演示反射测试的小提琴

我试图通过使用缓存的委托来提高性能:

public static class StructParserExtensions
{
    public static bool IntToObjParse(string str, out object obj)
    {
        int i;
        var result = int.TryParse(str, out i);
        obj = result ? (object)i : null;
        return result;
    }

    public static bool LongToObjParse(string str, out object obj)
    {
        long i;
        var result = long.TryParse(str, out i);
        obj = result ? (object)i : null;
        return result;
    }

    //implementations for other types goes here

}


public static class ParserHelper
{
    public delegate bool TryParseDl(string str, out object obj);

    public static readonly ReadOnlyDictionary<Type, TryParseDl> StructParsers;

    static ParserHelper()
    {
        StructParsers = new ReadOnlyDictionary<Type, TryParseDl>(CreateParsersForStructs());
    }

    /// Creates parsers for structs
    /// </summary>
    /// <returns>Dictionary</returns>
    private static Dictionary<Type, TryParseDl> CreateParsersForStructs()
    {

        var parsers = new Dictionary<Type, TryParseDl>();
        parsers[typeof(int)] = StructParserExtensions.IntToObjParse;                      
        parsers[typeof(long)] = StructParserExtensions.LongToObjParse;           
        return parsers;
    }           
}

我假设使用委托将大大提高性能,因此它将接近直接调用,但我错了它仍然慢约100倍(在100 reties) 这里是小提琴

我的问题是:

  1. 虽然我看到了多个示例,其中将反射调用转换为委托调用会产生差异,但在这种情况下它不起作用。 为什么?
  2. 在这种情况下,有什么办法可以改善性能吗?

你只是测试100次迭代。 您主要测试一次性启动开销。 增加迭代次数,直到每次测试需要1秒钟。 这样,开销就会消失在噪音中。

目前,您的代码运行时间为.5毫秒。 这远远噪音范围内。 修好后我得到:

00:00:00.9711365
00:00:01.0958751 //Slightly slower

该基准使用1e7次迭代,而前一次使用1e2。 另外,请确保在Release模式下进行测试,而不需要调试器附加您关心的位数。

有什么不对:

        private object Converter(object inVal, Type t)
    {
        return Convert.ChangeType(inVal, t);
    }

如前所述。 通过100次迭代,您最有可能只测量开销。

我进一步测试了一下。 我将您的代码加入到一个并运行450次迭代以获得一些统计数据。 我把它设置为解析10万次(10 ^ 7)

这是代码: http//pastebin.com/65dhdX9t

这是最后几次迭代的结果:
m-method,d-delegate,r-reflection 统计 把它们加起来:
- 代表团是aprox。 直接呼叫速度慢1.195倍
- 反思是aprox。 比直接呼叫慢6.105倍

希望能帮助到你! 它确实帮助我说服我从反思转移到代表团。

暂无
暂无

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

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