简体   繁体   中英

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...)

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).

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). 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

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. You're mostly testing one-time startup overhead. Increase the iteration count until each test takes 1 second. That way the overhead disappears in the noise.

Currently, your code runs for .5 milliseconds. 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. Also make sure to test in Release mode without debugger attached on the bitness that you care about.

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.

I took the testing a little bit further. I joined your codes into one and ran 450 iteration to get some statistical data. I set it to parse 10 milion times (10^7)

Here is the code: http://pastebin.com/65dhdX9t

Here is the result of few last iterations:
m-method, d-delegate, r-reflection 统计 To sum it up:
- Delegation is aprox. 1.195x slower against direct call
- Reflection is aprox. 6.105x slower against direct call

Hope it helps! It sure helped me to convince me to transfer from reflection to delegation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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