【问题标题】:Do C# Generics Have a Performance Benefit?C# 泛型有性能优势吗?
【发布时间】:2010-09-12 03:10:47
【问题描述】:

我有许多代表各种实体的数据类。

哪个更好:使用泛型和接口编写一个泛型类(例如,打印或输出 XML),还是编写一个单独的类来处理每个数据类?

是否有性能优势或任何其他优势(除了节省我编写单独类的时间)?

【问题讨论】:

    标签: c# .net performance generics


    【解决方案1】:

    使用泛型具有显着的性能优势——您可以取消 boxing and unboxing。与开发自己的课程相比,这是一次抛硬币(硬币的一侧比另一侧更重)。仅当您认为自己可以胜过框架的作者时,才可以自己动手。

    【讨论】:

    • 我已经完成了,但我的手卷也是通用的,所以去看看。
    • 经验证据告诉我,使用泛型的假定性能优势在很大程度上是理论上的。然而,强类型是选择泛型而不是非泛型对应项的一个非常有说服力的论据。
    【解决方案2】:

    不仅是的,而且是的。我不相信他们能带来多大的改变。在将一小部分使用 ArrayLists 和 HashTables 的核心代码改写为泛型之后,我们在 VistaDB 中进行了测试。速度提高了 250% 或更多。

    Read my blog about the testing 我们在泛型与弱类型集合上做过。结果让我们大吃一惊。

    我已经开始将许多使用弱类型集合的旧代码重写为强类型集合。我对 ADO.NET 接口最大的了解之一是它们不公开更强类型的数据输入和输出方法。在大批量应用中,从物体到背面的投射时间绝对是一个杀手。

    强类型的另一个副作用是您经常会在代码中发现弱类型的引用问题。我们发现通过implementing structs 在某些情况下可以避免对GC 施加压力,我们可以进一步加快我们的代码速度。将此与强输入相结合,以最大程度地提高速度。

    有时您必须在 dot net 运行时中使用弱类型接口。尽管尽可能寻找保持强类型的方法。对于非平凡的应用程序,它确实对性能产生了巨大的影响。

    【讨论】:

      【解决方案3】:

      从 CLR 的角度来看,C# 中的泛型是真正的泛型类型。泛型类的性能与执行完全相同操作的特定类之间不应该有任何根本区别。这与 Java 泛型不同,后者更多是在需要时自动进行类型转换或在编译时扩展的 C++ 模板。

      这是一篇很好的论文,有点旧,它解释了基本设计: "Design and Implementation of Generics for the .NET Common Language Runtime".

      如果您为特定任务手动编写类,您可以通过泛型类型的接口优化某些需要额外绕道的方面。

      总而言之,这可能会带来性能优势,但我会先推荐通用解决方案,然后根据需要进行优化。如果您希望实例化具有许多不同类型的泛型,则尤其如此。

      【讨论】:

        【解决方案4】:

        我针对不同的问题对 ArrayList 与通用列表进行了一些简单的基准测试:Generics vs. Array Lists,您的里程会有所不同,但通用列表比 ArrayList 快 4.7 倍。

        所以是的,装箱/拆箱是至关重要的如果你正在做很多操作。如果您正在做简单的 CRUD 操作,我不会担心。

        【讨论】:

          【解决方案5】:

          泛型是参数化代码和避免重复的方法之一。查看您的程序描述和您编写一个单独的类来处理每个数据对象的想法,我会倾向于泛型。让一个类来处理许多数据对象,而不是许多类做同样的事情,可以提高你的性能。当然,以更改代码的能力衡量的性能通常比计算机性能更重要。 :-)

          【讨论】:

            【解决方案6】:

            根据 Microsoft 的说法,泛型比强制转换(装箱/拆箱基元)更快这是真的。 他们还声称泛型提供了比引用类型之间的强制转换更好的性能,这似乎是不正确的(没有人能完全证明这一点)。

            Tony Northrup - MCTS 70-536:应用程序开发基金会的合著者 - 在同一本书中陈述如下:

            我无法重现 仿制药的性能优势; 然而,根据微软的说法, 泛型比使用更快 铸件。在实践中,铸造证明 比使用快几倍 一个通用的。但是,您可能不会 注意你的性能差异 应用程序。 (我的测试超过 100,000 迭代只用了几秒钟。) 所以你仍然应该使用泛型 因为它们是类型安全的。

            与引用类型之间的强制转换相比,我无法通过泛型重现这种性能优势 - 所以我会说性能提升是“假定的”而不是“显着的”。

            【讨论】:

            • 我正在阅读 Tony Northrup 的书,确切的页面,并在谷歌上搜索了泛型的性能优势后最终来到了这里。这个问题的答案似乎大相径庭——您和托尼都发现性能优势不大,而其他人(例如上面的 Jason Short)则发现了巨大的性能优势。我想知道为什么这里有这么大的区别?
            • 请记住,我不是在谈论基元(装箱/拆箱),这显然比泛型更快 - 微软声称泛型提供了比引用类型之间的转换更好的性能,但我得到了完全相反的结果!看看这里的 cmets dotnetbutchering.blogspot.com/2008/09/…(我的博客),你会发现我的结果
            • 没有看到 Tony 用来测试的实际代码,很难对此争论不休。有可能 Tony 将内置泛型类型(例如 List<string>)与非泛型等效类型(例如 StringCollection)进行了比较,这种比较并没有说明泛型本身的性能,而是比较了性能这两个实现。强制转换是一项相当昂贵的操作,因此在使用泛型类型(可以避免强制转换)而不是非泛型类型并且必须不断地强制转换时,通常会看到性能提升。
            • 自己测试这个相当容易。针对这个问题的其他答案之一,我编写了一个测试程序(循环将一个项目添加到列表中,然后读取刚刚添加的项目),我发现 List 始终比 40% 快ArrayList,不管加了多少东西。我尝试了 10、100、1000、1000000 和 1 亿,对于仿制药来说,速度优势始终相同......
            【解决方案7】:

            如果您将通用列表(例如)与您使用的类型的特定列表进行比较,则差异很小,JIT 编译器的结果几乎相同。

            如果您将泛型列表与对象列表进行比较,那么泛型列表有很大的好处 - 没有值类型的装箱/拆箱,也没有引用类型的类型检查。

            .net 库中的通用集合类也经过大量优化,您自己不太可能做得更好。

            【讨论】:

              【解决方案8】:

              泛型更快!

              我还发现 Tony Northrup 在他的书中写了关于泛型和非泛型性能的错误内容。

              我在我的博客上写过这个: http://andriybuday.blogspot.com/2010/01/generics-performance-vs-non-generics.html

              这是一篇很棒的文章,作者比较了泛型和非泛型的性能:

              nayyeri.net/use-generics-to-improve-performance

              【讨论】:

              • 您指的是 MCTS 70-536 不是吗?这是迄今为止我在职业生涯中读过的最糟糕的计算机相关书籍(而且我读过不少)。它不仅充满了愚蠢的错误,而且包含许多明显错误的陈述。即使是公布的勘误表也只涵盖了书中实际错误的 10% 左右。
              【解决方案9】:

              如果您正在考虑调用某个接口上的方法来完成其工作的泛型类,那将比使用已知类型的特定类慢,因为调用接口方法比(非虚拟)函数调用慢.

              当然,除非代码是性能关键过程的缓慢部分,否则您应该关注清晰。

              【讨论】:

                【解决方案10】:

                在泛型集合与拳击等的情况下,对于 ArrayList 等较旧的集合,泛型是性能上的胜利。但在绝大多数情况下,这并不是泛型最重要的好处。我认为有两件事会带来更大的好处:

                1. 类型安全。
                2. 自我记录也更易读。

                泛型促进类型安全,强制实现更同质的集合。想象一下当您期望一个 int 时偶然发现一个字符串。哎哟。

                通用集合也更能自我记录。考虑以下两个集合:

                ArrayList listOfNames = new ArrayList();
                List<NameType> listOfNames = new List<NameType>();
                

                阅读第一行你可能会认为 listOfNames 是一个字符串列表。错误的!它实际上是存储 NameType 类型的对象。第二个示例不仅强制类型必须是 NameType(或后代),而且代码更具可读性。我马上就知道我需要去找 TypeName 并通过查看代码来学习如何使用它。

                我在 StackOverflow 上看到了很多“x 的性能是否比 y 好”的问题。这里的问题非常公平,事实证明,无论您以何种方式剥猫皮,仿制药都是一种胜利。但归根结底,重点是为用户提供有用的东西。当然,您的应用程序需要能够执行,但它也需要不崩溃,并且您需要能够快速响应错误和功能请求。我认为您可以看到最后两点如何与泛型集合的类型安全和代码可读性相关联。如果是相反的情况,如果 ArrayList 的性能优于 List,除非性能差异显着,否则我可能仍会采用 List 实现。

                就性能而言(总体而言),我敢打赌,在您的职业生涯中,您会发现这些领域的大部分性能瓶颈:

                1. 数据库或数据库查询(包括索引等)设计不佳,
                2. 内存管理不善(忘记调用 dispose、深堆栈、持有对象时间过长等),
                3. 线程管理不当(线程过多,桌面应用程序的后台线程未调用 IO 等),
                4. 糟糕的 IO 设计。

                单行解决方案无法解决这些问题。作为程序员、工程师和极客,我们想知道所有很酷的性能小技巧。但重要的是我们要密切关注球。我相信专注于我上面提到的四个领域的良好设计和编程实践将进一步导致远远超过担心小的性能提升。

                【讨论】:

                  【解决方案11】:

                  也可以在 MSDN 上查看 Rico Mariani 的博客:

                  http://blogs.msdn.com/ricom/archive/2005/08/26/456879.aspx

                  Q1:哪个更快?

                  泛型版本相当 更快,见下文。

                  文章有点旧,但给出了细节。

                  【讨论】:

                    【解决方案12】:

                    您不仅可以取消装箱,而且由于底层实现的变化,泛型实现比具有引用类型的非泛型对应物要快一些。

                    原件的设计考虑了特定的扩展模型。这个模型从未真正使用过(无论如何这都是一个坏主意),但设计决定迫使一些方法是虚拟的,因此不可内联(基于这方面当前和过去的 JIT 优化)。

                    此决定已在较新的类中得到纠正,但不能在旧类中更改,除非它是潜在的二进制破坏性更改。

                    此外,由于 ArrayList 的 Enumerator 需要堆分配,因此通过 List(而不是 IList)上的 foreach 进行迭代更快。诚然,这确实导致了obscure bug

                    【讨论】:

                    • 对于晦涩的飞碟参考的奖励 ;)
                    猜你喜欢
                    • 2015-01-06
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-01-10
                    • 1970-01-01
                    • 2020-03-17
                    • 2010-09-06
                    • 2011-12-18
                    • 2012-05-12
                    相关资源
                    最近更新 更多