【问题标题】:Performance of reflection method call vs delegate call反射方法调用与委托调用的性能
【发布时间】:2016-06-21 09:27:11
【问题描述】:

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

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

实现将调用提供的type 参数的TryParse 方法(如果类型为int,将调用int.TryPase,并且输出值将作为对象返回)。

我已经通过反射实现了它,但是有一个重大的性能损失(如我所料)

反射实现:

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 次 reties 上)。 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. 在这种情况下有什么方法可以提高性能吗?

【问题讨论】:

  • 如果您将此代码的性能与其他代码进行比较,参考其他代码会很有用。当我们不知道更快的代码是什么时,真的很难说“为什么它执行得更慢”。性能差异可能有多种原因。我正在查看您的代码并思考“它使用反射,当然它比不使用反射的版本慢”..
  • 我有两个小提琴附加代表的链接:dotnetfiddle.net/RYx0Zt反射:dotnetfiddle.net/LcVqMy

标签: c# performance reflection delegates


【解决方案1】:

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

目前,您的代码运行时间为 0.5 毫秒。那是远远的噪音范围。修复后我得到:

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

本次基准测试使用 1e7 次迭代,而前一次使用的是 1e2。 还要确保在发布模式下进行测试,而无需在您关心的位数上附加调试器。

【讨论】:

  • 首先感谢您的回复。将迭代次数增加到1e7,确实证明了您的观点。看起来反射调用比直接调用慢大约 10 倍。所以我现在确实看到了区别。这里有趣的一点是,如果预期的迭代次数比反射调用和委托调用之间的差异相对较小,则相对可以忽略不计。
  • 您不能得出这样的结论,因为您不仅在测量调用样式,还测量了一次性开销。反射可能只是启动成本很高。您可以先尝试运行一次预热迭代。这消除了一次性开销,您可以减少迭代次数。
【解决方案2】:

有什么问题:

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

【讨论】:

  • 在过程中尝试和学习有什么问题?
  • @Stralos:虽然它没有回答第一个问题,但它似乎回答了第二个问题,即如何获得性能更好的代码版本。
【解决方案3】:

如前所述。对于 100 次迭代,您很可能只测量开销。

我进一步进行了测试。 我将您的代码合二为一并运行 450 次迭代以获取一些统计数据。 我将其设置为解析 1000 万次 (10^7)

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

这是最后几次迭代的结果:
m-方法,d-委托,r-反射 总结一下:
- 授权是aprox。直接调用慢 1.195 倍
- 反射是aprox。直接调用慢 6.105 倍

希望对您有所帮助!它确实帮助我说服了我从反思转向委托。

【讨论】:

    猜你喜欢
    • 2011-01-06
    • 1970-01-01
    • 2015-05-01
    • 1970-01-01
    • 2011-07-23
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 2010-10-01
    相关资源
    最近更新 更多