【问题标题】:How costly is .NET reflection?.NET 反射的成本是多少?
【发布时间】:2010-09-06 17:30:44
【问题描述】:

我经常听到反射的使用有多糟糕。虽然我通常避免反思并且很少发现没有它就无法解决我的问题的情况,但我想知道......

对于那些在应用程序中使用过反射的人,您是否测量过性能影响,它真的那么糟糕吗?

【问题讨论】:

  • 您可能还想查看这个问题。 stackoverflow.com/questions/224232/…
  • 使用 fastflect.codeplex.com 上的 api。对于 getter/setter/invokers 和其他一些东西,它将加快反射速度 500 倍。如果您需要扩展它,也有关于它如何工作的源代码和信息。
  • 此信息在 2014 年如何检查?这 4 年有什么变化吗?
  • 使用反射 (PropertyInfo.SetValue(instance, value)) 为实例属性赋值的简单任务比直接编码 (instance.property = value) 慢大约 150 倍。在 .NET 4.0 中

标签: c# .net performance reflection


【解决方案1】:

在他的演讲The Performance of Everyday Things 中,Jeff Richter 表明通过反射调用方法比正常调用要慢 1000 倍

Jeff 的提示:如果需要多次调用该方法,使用一次反射找到它,然后将其分配给一个delegate,然后调用该delegate。

【讨论】:

  • 我也参加了 Devscovery,并同意 .NET 3.5 的这些结果。为 .NET 4 重新编译 Devscovery 性能基准程序显示了巨大的改进!成本下降到慢 100 倍。在 .NET 3.5 和 .NET 4 之间,使用反射进行 typeof() 查找没有变化。
【解决方案2】:

是的。但这取决于你想要做什么。

我使用反射来动态加载程序集(插件),它的性能“惩罚”不是问题,因为该操作是我在应用程序启动期间执行的操作。

但是,如果您在一系列嵌套循环中进行反射,每个循环都带有反射调用,我会说您应该重新访问您的代码:)

对于“几次”操作,反射是完全可以接受的,您不会注意到它有任何延迟或问题。这是一个非常强大的机制,甚至被 .NET 使用,所以我不明白你为什么不应该尝试一下。

【讨论】:

  • 我一直在使用反射来获取方法,当前方法的类名来记录 try-catch 中的错误。基本上是为了避免在记录错误时对函数名称进行硬编码。我需要担心吗?
  • @Sangram 不,没关系
  • @Sangram 不,除非你有很多需要不断捕获的错误,否则这应该是一个不同的问题:)
  • @Sangram 虽然反射性能不应该是您的问题,但您似乎正在尝试以一种开箱即用的更优雅的方式重新实现普通旧异常提供的功能...
【解决方案3】:

反射性能取决于实现(重复调用应该被缓存,例如:entity.GetType().GetProperty("PropName"))。由于我每天看到的大部分反射都用于填充来自数据读取器或其他存储库类型结构的实体,因此我决定在反射用于获取或设置对象属性时专门对性能进行基准测试。

我设计了一个我认为是公平的测试,因为它缓存了所有重复调用,并且只对实际的 SetValue 或 GetValue 调用进行计时。性能测试的所有源代码都在 bitbucket 中:https://bitbucket.org/grenade/accessortest。欢迎并鼓励审查。

我得出的结论是,当反射实现完成时,在一次返回少于 100,000 行的数据访问层中移除反射是不切实际的,并且不会提供显着的性能改进。

上图展示了我的小基准测试的输出,并显示了优于反射的机制,只有在 100,000 次循环标记之后才会显着提高。大多数 DAL 一次只返回数百甚至数千行,在这些级别上,反射表现得很好。

【讨论】:

  • 不一定。您的 DAL 转换可能仅针对几千个项目,但将其乘以使用您的应用程序(如果是 Web)的并发用户,它可能加起来就像您要转换数百万个项目一样。如果特定方法慢 100 倍,那么在小型和大型装置上都会慢得多。越慢越慢。
  • @RobertKoritnik 假设您服务器上的 Web 方法不是异步的
  • @kurren 异步不会影响反射,但会影响服务器资源。异步 Web 方法当然能够为更多用户服务,但反射仍然会很慢。无论如何,AFAIK的反射本身就是一个同步过程。另一方面,数据获取将是唯一能很好地与异步设计配合使用的部分。
  • 图表上的Hyper方法是什么?它与 Reflector 有什么不同?
【解决方案4】:

如果您不在循环中,请不要担心。

【讨论】:

    【解决方案5】:

    我最相关的经验是编写代码来比较大型对象模型中任何两个相同类型的数据实体的属性。让它工作,尝试它,显然像狗一样跑。

    我很沮丧,然后一夜之间意识到,在不改变逻辑的情况下,我可以使用相同的算法自动生成方法来进行比较,但静态访问属性。完全没有时间为此目的调整代码,而且我能够对实体与静态代码进行深入的属性比较,只要对象模型发生变化,只需单击按钮即可更新。

    我的观点是:在与同事的对话中,我多次指出他们使用反射可能是自动生成代码以进行编译而不是执行运行时操作,这通常值得考虑。

    【讨论】:

    • 考虑到Visual Studio有这么优秀的模板支持,使用代码生成是一种实用的方法
    【解决方案6】:

    不是很大。我在桌面开发中从来没有遇到过它的问题,除非正如 Martin 所说,你在一个愚蠢的地方使用它。我听说很多人完全不合理地担心它在桌面开发中的性能。

    Compact Framework(我通常在其中)中,它几乎是anathema,在大多数情况下应该像瘟疫一样避免。我仍然可以不经常使用它,但我必须非常小心它的应用,因为它不那么有趣。 :(

    【讨论】:

    • +1 教我一个新词:anathema。还提到了非理性的恐惧。我害怕那些非理性地害怕的程序员——这表明他们并不真正知道自己在做什么,只是根据别人告诉他们的事情来做。 咳货邪咳
    • 啊啊啊货物崇拜。现在有一个很好的人类好奇行为的例子。
    【解决方案7】:

    你不得不担心 .NET 库内部为性能关键代码执行的反射,这已经够糟糕的了。

    以下示例已过时 - 在当时(2008 年)是正确的,但很久以前已在更新的 CLR 版本中修复。不过,总的来说,反射仍然是一件有点昂贵的事情!

    恰当的例子:您不应该在高性能代码的 lock (C#) / SyncLock (VB.NET) 语句中使用声明为“Object”的成员。为什么?因为 CLR 无法锁定值类型,这意味着它必须进行运行时反射类型检查以查看您的 Object 是否实际上是值类型而不是引用类型。

    【讨论】:

    • 公平地说,反射类型检查很快。
    • 对于这样的“性能关键代码”,您真的应该从使用 .NET 开始吗?
    • @Seph:.NET 的动态/反射部分,没有。但是通常的 C#/.NET,为什么不呢? C++ 与 C# 的加速在应用层是微不足道的(C++ 在密集的数学例程上仍然快几个%)。而且我猜你不是在建议组装......
    • 可以锁定一个装箱的值类型(即对象)。 @BryceWagner 是正确的。
    • 公平地说(对我来说),更准确地说答案是“过时的”,而不是“普通的废话”。我对 lock(obj) 行为的评论在编写它们时是准确的,但 CLR 的特定于实现的行为早已不复存在。
    【解决方案8】:

    与编程中的所有事情一样,您必须平衡性能成本与获得的任何收益。小心使用时,反射是一种非常宝贵的工具。我在 C# 中创建了一个 O/R 映射库,它使用反射来进行绑定。这工作得非常好。大多数反射代码只执行一次,因此对性能的影响很小,但好处很大。如果我正在编写一个新的扇形排序算法,我可能不会使用反射,因为它的扩展性可能很差。

    感谢我在这里没有准确回答您的问题。我的观点是,这并不重要。在适当的地方使用反射。这只是您需要了解如何以及何时使用的另一种语言功能。

    【讨论】:

      【解决方案9】:

      如果您将反射用于频繁的对象创建,它会对性能产生显着影响。我开发了基于Composite UI Application Block 的应用程序,它严重依赖反射。与通过反射创建对象相关的性能明显下降。

      但是,在大多数情况下,使用反射没有问题。如果您只需要检查一些组件,我会推荐Mono.Cecil,这非常lightweight and fast

      【讨论】:

        【解决方案10】:

        反射代价高昂,因为每当您请求与参数列表匹配的方法时,运行时必须进行许多检查。在内部的某个地方,存在循环遍历类型的所有方法、验证其可见性、检查返回类型以及检查每个参数的类型的代码。所有这些都需要时间。

        当您在内部执行该方法时,有一些代码会在执行实际目标方法之前检查您是否传递了兼容的参数列表。

        如果可能的话,如果将来要不断地重用它,总是建议缓存方法句柄。像所有好的编程技巧一样,避免重复自己通常是有意义的。在这种情况下,不断地查找带有某些参数的方法然后每次都执行它是很浪费的。

        探索源代码并查看正在执行的操作。

        【讨论】:

          【解决方案11】:

          与所有事情一样,一切都是为了评估情况。在DotNetNuke 中有一个相当核心的组件,称为FillObject,它使用反射来填充数据行中的对象。

          这是一个相当常见的场景,MSDN 上有一篇文章,Using Reflection to Bind Business Objects to ASP.NET Form Controls 涵盖了性能问题。

          抛开性能不谈,我不喜欢在特定场景中使用反射的一件事是它往往会降低快速理解代码的能力与强类型数据集或 LINQ to SQL 之类的东西相比,也会失去编译时安全性。

          【讨论】:

            【解决方案12】:

            反射不会显着降低应用的性能。通过不使用反射,您可能能够更快地完成某些事情,但如果反射是实现某些功能的最简单方法,那么使用它。如果它成为一个性能问题,你总是可以重构你的代码远离反射。

            【讨论】:

              【解决方案13】:

              我想你会发现答案是,这取决于。如果你想把它放在你的任务列表应用程序中,这没什么大不了的。如果您想将其放入 Facebook 的持久性库中,这将是一件大事。

              【讨论】:

                猜你喜欢
                • 2010-09-18
                • 2011-05-17
                • 2012-05-13
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-09-20
                • 2013-02-11
                相关资源
                最近更新 更多