简体   繁体   English

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

[英]Performance of reflection method call vs delegate call

My goal is to write a weakly typed TryParse method that is going support basically all the available struct types (int, long, float...) 我的目标是编写一个弱类型的 TryParse方法,该方法基本上支持所有可用的struct类型(int,long,float ...)

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

The implementation is going to invoke the TryParse method of the provided type argument (if type is int , int.TryPase will be invoked and the out value is going to be returned as object). 该实现将调用所提供type参数的TryParse方法(如果type为int ,则将调用int.TryPase并将out值作为对象返回)。

I've implemented it via reflection, but there is a major performance penalty (as I expected) 我通过反射实现了它,但是有一个主要的性能损失(正如我预期的那样)

The Reflection Implementation: 反思实施:

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;
                };
        }
}

And here is the performance test: 这是性能测试:

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);
    }
}

The average result is that reflection invocation is a bout 200 times slower than direct invocation (on 100 reties). 平均结果是反射调用比直接调用慢了200倍(在100次重复上)。 Fiddle that demonstrates the reflection test 演示反射测试的小提琴

I tried to improve performance by using a cached delegates: 我试图通过使用缓存的委托来提高性能:

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;
    }           
}

I assumed that using delegates will drastically improve the performance so it will be close to the direct invocation, but I was wrong it is still about 100 times slower (on 100 reties) Here is the fiddle 我假设使用委托将大大提高性能,因此它将接近直接调用,但我错了它仍然慢约100倍(在100 reties) 这里是小提琴

My questions are: 我的问题是:

  1. While I saw multiple examples where converting reflection calls into delegate calls makes the difference, it doesn't do the job in this case. 虽然我看到了多个示例,其中将反射调用转换为委托调用会产生差异,但在这种情况下它不起作用。 Why? 为什么?
  2. Is there any way to improve the performance in this case? 在这种情况下,有什么办法可以改善性能吗?

You're just testing 100 iterations. 你只是测试100次迭代。 You're mostly testing one-time startup overhead. 您主要测试一次性启动开销。 Increase the iteration count until each test takes 1 second. 增加迭代次数,直到每次测试需要1秒钟。 That way the overhead disappears in the noise. 这样,开销就会消失在噪音中。

Currently, your code runs for .5 milliseconds. 目前,您的代码运行时间为.5毫秒。 That is far in the noise range. 这远远噪音范围内。 After fixing that I get: 修好后我得到:

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

This benchmark uses 1e7 iterations whereas the previous one used 1e2. 该基准使用1e7次迭代,而前一次使用1e2。 Also make sure to test in Release mode without debugger attached on the bitness that you care about. 另外,请确保在Release模式下进行测试,而不需要调试器附加您关心的位数。

What's wrong with: 有什么不对:

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

As it was stated before. 如前所述。 With 100 iterations you are most probably measuring only the overhead. 通过100次迭代,您最有可能只测量开销。

I took the testing a little bit further. 我进一步测试了一下。 I joined your codes into one and ran 450 iteration to get some statistical data. 我将您的代码加入到一个并运行450次迭代以获得一些统计数据。 I set it to parse 10 milion times (10^7) 我把它设置为解析10万次(10 ^ 7)

Here is the code: http://pastebin.com/65dhdX9t 这是代码: http//pastebin.com/65dhdX9t

Here is the result of few last iterations: 这是最后几次迭代的结果:
m-method, d-delegate, r-reflection m-method,d-delegate,r-reflection 统计 To sum it up: 把它们加起来:
- Delegation is aprox. - 代表团是aprox。 1.195x slower against direct call 直接呼叫速度慢1.195倍
- Reflection is aprox. - 反思是aprox。 6.105x slower against direct call 比直接呼叫慢6.105倍

Hope it helps! 希望能帮助到你! It sure helped me to convince me to transfer from reflection to delegation. 它确实帮助我说服我从反思转移到代表团。

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

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