C#泛型有性能优势吗?
我有许多表示各种实体的数据类。
哪个更好:使用泛型和接口编写泛型类(例如,打印或者输出XML),或者编写单独的类来处理每个数据类?
是否有性能上的好处或者其他好处(除了它节省了我编写单独的类的时间)?
解决方案
使用泛型具有显着的性能优势-我们无需装箱和拆箱。与开发自己的课程相比,这是一个抛硬币的过程(硬币的一侧比另一侧的权重更大)。仅在认为自己可以胜过该框架的作者时才提出自己的建议。
我针对一个不同的问题在ArrayList的vs通用列表上做了一些简单的基准测试:通用列表与数组列表,里程会有所不同,但是通用列表比ArrayList快4.7倍。
因此,是的,如果我们要进行很多操作,装箱/拆箱至关重要。如果我们正在做简单的CRUD程序,那我就不用担心。
如果我们将泛型列表(例如)与特定列表精确地比较为我们使用的类型,则差异很小,JIT编译器的结果几乎相同。
如果将通用列表与对象列表进行比较,则通用列表将具有显着的优势,即无需对值类型进行装箱/拆箱,也无需对引用类型进行类型检查。
.net库中的通用集合类也进行了重大优化,我们自己不太可能做得更好。
如果我们想使用在某个接口上调用方法来完成其工作的泛型类,那将比使用已知类型的特定类慢,因为调用接口方法比(非虚拟)函数调用慢。
当然,除非代码是关键性能过程中的缓慢部分,否则我们应该关注清晰性。
泛型是参数化代码和避免重复的一种方法。查看程序说明和编写单独的类以处理每个数据对象的想法,我将倾向于泛型。拥有一个可以处理许多数据对象的类,而不是处理相同数据的许多类,可以提高性能。当然,以更改代码的能力来衡量的性能通常比计算机性能更重要。 :-)
从CLR角度来看,Generics in Care是真正的泛型类型。通用类和执行完全相同的操作的特定类的性能之间不应有任何根本区别。这不同于Java泛型,后者更多是在需要时进行自动类型转换或者在编译时扩展的C ++模板。
这是一篇不错的论文,有些陈旧,它解释了基本设计:
"针对以下对象的泛型的设计和实现:
.NET公共语言运行时"。
如果我们为特定任务手写类,则可以优化某些方面,这些方面需要通过通用类型的接口进行其他绕行。
总而言之,这可能会带来性能上的好处,但是我会首先推荐通用解决方案,然后根据需要进行优化。如果我们希望使用许多不同的类型实例化泛型,则尤其如此。
根据Microsoft的说法,泛型比强制转换(装箱/拆箱原语)更快。
他们还声称,泛型提供了比引用类型之间的强制转换更好的性能,这似乎是不正确的(没人能证明这一点)。
Tony Northrup是MCTS 70-536:应用程序开发基金会的合著者,在同一本书中指出以下内容:
I haven’t been able to reproduce the performance benefits of generics; however, according to Microsoft, generics are faster than using casting. In practice, casting proved to be several times faster than using a generic. However, you probably won’t notice performance differences in your applications. (My tests over 100,000 iterations took only a few seconds.) So you should still use generics because they are type-safe.
与在引用类型之间进行强制转换相比,我无法使用泛型重现这种性能优势,因此我想说性能提高比"有意义"要"重要"得多。
对于常规集合与boxing等相比,对于诸如ArrayList之类的较早的集合,泛型是性能上的胜利。但是在绝大多数情况下,这并不是仿制药的最重要好处。我认为有两个好处更大的事情:
- 类型安全。
- 自我记录也更易读。
泛型提高了类型安全性,从而强制了更均匀的集合。想象一下,当我们期望一个整数时,会绊在字符串上。哎哟。
通用馆藏也更能自我记录。考虑下面的两个集合:
ArrayList listOfNames = new ArrayList(); List<NameType> listOfNames = new List<NameType>();
阅读第一行,我们可能会认为listOfNames是一个字符串列表。错误的!它实际上存储的是NameType类型的对象。第二个示例不仅强制类型必须为NameType(或者后代),而且代码更易读。我立即知道,我需要查找TypeName并仅通过查看代码来学习如何使用它。
我在StackOverflow上看到了很多这样的问题:" x的性能优于y"。这里的问题非常公平,事实证明,仿制药无论如何都是猫的赢家。但总而言之,关键是要为用户提供有用的东西。确保应用程序需要能够执行,但也需要不崩溃,并且我们需要能够快速响应错误和功能请求。我想我们可以看到这最后两点如何与泛型集合的类型安全性和代码可读性结合在一起。如果情况相反,如果ArrayList的表现优于List <>,则除非性能差异显着,否则我可能仍会采用List <>实现。
就绩效而言(一般而言),我愿意打赌,在职业生涯中,我们会发现这些领域的大部分绩效瓶颈:
- 数据库或者数据库查询的设计不良(包括索引编制等),
- 糟糕的内存管理(忘记调用处置,深层堆栈,对对象的保持时间过长等等),
- 线程管理不当(线程太多,在桌面应用程序中未在后台线程上调用IO等),
- IO设计不良。
这些都不是单行解决方案所解决的。作为程序员,工程师和极客,我们希望了解所有出色的小性能技巧。但是重要的是,我们必须保持警惕。我相信,在我上面提到的四个领域中将重点放在良好的设计和编程实践上,这将进一步引起更多的担忧,而不仅仅是担心性能的提高。
不仅可以,还可以。我不相信他们能带来多大的改变。在将使用ArrayLists和HashTables的一小部分核心代码重写为泛型之后,我们在VistaDB中进行了测试。速度提高了250%或者更多。
阅读我的博客,了解我们对泛型与弱类型集合进行的测试。结果令人震惊。
我已经开始重写许多旧代码,这些旧代码将弱类型集合用于强类型集合。我对ADO.NET接口的最大了解之一是,它们没有公开更强类型的数据输入和输出方式。在大批量应用中,从物体到背面的投射时间绝对是杀手。
强类型化的另一个副作用是,我们通常会在代码中发现弱类型引用问题。我们发现通过在某些情况下实现结构以避免对GC施加压力,我们可以进一步加快代码的速度。将此功能与强键入功能结合使用,可以最大程度地提高速度。
有时,我们必须在点网运行时中使用弱类型的接口。只要有可能,就寻找保持强类型输入的方法。对于非平凡的应用程序,确实确实会在性能上产生巨大的差异。
也请参阅MSDN上的Rico Mariani的博客:
http://blogs.msdn.com/ricom/archive/2005/08/26/456879.aspx
Q1: Which is faster? The Generics version is considerably faster, see below.
这篇文章有点旧,但是提供了详细信息。
我们不仅可以取消装箱,而且由于基础实现的更改,通用实现比带引用类型的非通用实现要快一些。
原稿在设计时考虑了特定的扩展模型。该模型从未真正使用过(无论如何都是一个坏主意),但是设计决定迫使一些方法是虚拟的,因此是不可联机的(基于这方面当前和过去的JIT优化)。
该决定在较新的类中已得到纠正,但在较旧的类中不能更改,除非它可能是潜在的二进制突破性更改。
另外,由于ArrayList的Enumerator需要堆分配,因此通过List <>(而不是IList <>)上的foreach进行迭代的速度更快。诚然,这确实导致了一个晦涩的错误
泛型更快!
我还发现Tony Northrup在他的书中就泛型和非泛型的性能写了错误的话。
我在博客上写道:
http://andriybuday.blogspot.com/2010/01/generics-performance-vs-non-generics.html
这是一篇很棒的文章,作者比较了泛型和非泛型的性能:
nayyeri.net/使用泛型来提高性能