Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?
like image 519
Alex Art. Avatar asked Jun 21 '16 09:06

Alex Art.


1 Answers

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.

like image 57
usr Avatar answered Oct 26 '22 23:10

usr