反射方法调用与委托调用

时间:2016-06-21 09:27:11

标签: c# performance reflection delegates

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

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

实现将调用提供的TryParse参数的type方法(如果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);
    }
}

平均结果是反射调用比直接调用慢100倍(在100次重复上)。 Fiddle that demonstrates the reflection test

我尝试使用缓存的委托来提高性能:

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次重复) Here is the fiddle

我的问题是:

  1. 虽然我看到了多个示例,其中将反射调用转换为委托调用会产生差异,但在这种情况下它并没有完成工作。为什么?
  2. 在这种情况下,有没有办法改善表现?

3 个答案:

答案 0 :(得分:5)

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

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

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

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

答案 1 :(得分:2)

出了什么问题:

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

答案 2 :(得分:1)

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

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

以下是代码:http://pastebin.com/65dhdX9t

这是最后几次迭代的结果:
m-method,d-delegate,r-reflection Statistics 总结一下:
- 代表团是aprox。比直接呼叫慢1.195倍 - 反思是aprox。比直接呼叫慢6.105倍

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