【发布时间】:2009-07-22 01:32:25
【问题描述】:
我很想知道技术原因:为什么反射在 .NET 中表现不佳?
【问题讨论】:
标签: .net performance reflection
我很想知道技术原因:为什么反射在 .NET 中表现不佳?
【问题讨论】:
标签: .net performance reflection
反射效果不好
这是一个非常重要的声明。 “表现好”是相对的。与静态代码相比,反射调用的性能相当好。但是,几乎在所有情况下,.NET 中的反射都非常快。我不能低估这一点。反射在 .NET 1.x 时代和其他语言中名声不佳,但 .NET 2.0+ 中的反射非常快。
在 99% 的情况下,“反射太慢”是无关紧要的问题。我怀疑您是否需要费心衡量反射调用与静态调用对性能的影响。
【讨论】:
简单地说“反射”执行缓慢就是在一个非常宽泛的毯子下混杂了一大堆功能。 .NET 中的反射分为几个类,每个类都有不同的“性能”级别。一方面,typeof() 运算符的使用实际上是一种反射形式……它查询 CLR 元数据以查找类型。但是,typeof() 执行速度非常快(在接近空闲的时间内)。使用其他与类型相关的“反射”,例如 is 运算符、sizeof() 运算符等也几乎是免费的(它们基本上就像它们一样执行是静态代码。)
用于检索类型信息的反射虽然比typeof() 慢,但考虑到指针遍历和元数据探测的数量,它也非常非常快。元数据探测是 .NET 代码的一种相当普遍的做法,尤其是在使用自定义属性时。
关于反射的最大性能问题与调用有关。访问类型信息和读取元数据非常轻量级。当您涉及属性、索引器、方法的动态调用或通过反射动态构造新类型时,您会受到数量级的性能影响。
但是,反射仍然是进程内执行,因此在您担心一点动态调用会影响性能之前,请确保没有任何明显更大的性能瓶颈,例如进程间执行、网络调用(即数据库、Web 服务等)在性能方面,从最大的性能影响开始,然后从那里开始。从性能的角度来看,反射(包括动态调用)通常是您最不应该担心的事情之一。
附录:
有点事后的想法,但如果您需要对后期绑定类型成员进行高度动态调用,您应该研究轻量级代码生成。使用 System.Reflection.Emit 命名空间,您可以使用 DynamicMethod 等实用程序在运行时生成可以执行早期绑定调用的轻量级代码。缓存此生成的代码可降低生成它的初始成本,让您获得后期绑定调用和早期绑定性能的好处。
【讨论】:
MSDN 上关于此的精彩文章:Dodge Common Performance Pitfalls to Craft Speedy Applications。基本上,这是一个晚期和早期绑定的问题。也就是说,什么可以在编译时决定,什么必须在运行时决定。从那篇文章...
后期绑定案例是 MethodBase.Invoke,DynamicMethod 通过 调用、Type.InvokeMember 和 后期绑定的委托调用(调用 通过 Delegate.DynamicInvoke 代表)。 所有这些方法都附带 明显更消极 性能影响比 早期绑定的案例。即使在最好的 情况下,它们通常是 比最慢的幅度慢 早期绑定案例。
他打破了我们对进行早期和晚期绑定调用的各种方式的相当多的性能测试。值得一读。
【讨论】:
反射性能很好,它只是做比静态代码更多。
假设你有这个代码sn-p:
typeof(SomeClass).GetMethod("SomeStaticMethod").
Invoke(null, new object[] { 1, 2, 3 });
这个和这个是一样的:
SomeClass.SomeStaticMethod(1, 2, 3);
但很明显,第一个有很多工作要做。它必须获取类型信息,遍历它以查看是否存在 SomeStaticMethod 方法,检查它是什么类型的方法,在实例上调用该方法,如果它是静态的并传递对象数组是参数,装箱/拆箱整数在这种情况下也是如此。
这可能是一个非常广泛的总结,毫无疑问还有更多内容。然而尽管如此,反射仍然非常快并且被用于很多领域,从 WinForms 上的数据绑定到 ASP.NET MVC 中的模型绑定(你对这个基于 MVC 构建的站点发出的每个请求都涉及到一大堆反射,然而, 站点非常快,MVC 被认为是一个非常快的框架)。
【讨论】:
因为它涉及运行时的字符串查找(类型和成员名称),以及值类型的额外装箱/拆箱。
【讨论】:
反射涉及到元数据以将名称解析为标记,然后使用此标记查找和检索您想要的数据(例如,方法标记用于获取有关方法、参数...等)。
这个过程很昂贵,原因有两个。 1-正在进行大量查找。 2- 接触通常不接触的页面(冷页面),其中包含元数据表。
直接访问元数据是昂贵的,CLR 维护缓存以加快此过程并避免在您以非反射方式访问时接触元数据表,但是一旦您进行反射,我们会绕过这些缓存并直接转到来源。
【讨论】:
Reflection 在 .NET 中确实表现良好 - 就其作用而言。
然而,反射,因为它是对已编译类型的运行时分析,需要相当多的开销。一般来说,诸如静态类型信息的字符串查找、类元数据的遍历等操作会花费一些时间。
这并不是说它真的很慢 - 它只是比完全编译的、严格的方法调用和查找慢很多。应该是——但我真的认为它更多是因为在任何系统中编译的代码比动态查找信息要快。
【讨论】: