【问题标题】:Generics vs. Array Lists泛型与数组列表
【发布时间】:2010-09-10 19:51:56
【问题描述】:

我在这里工作的系统是在 .net 2.0 之前编写的,没有泛型的好处。它最终更新到 2.0,但由于时间限制,没有任何代码被重构。有许多地方的代码使用 ArraysLists 等将事物存储为对象。

从性能的角度来看,更改代码对使用泛型有多重要?我知道从性能的角度来看,装箱和拆箱等,它是低效的,但是改变它真的会有多少性能提升呢?泛型是可以在未来使用的东西,还是有足够的性能变化,应该努力更新旧代码?

【问题讨论】:

    标签: c# performance generics


    【解决方案1】:

    使用泛型还意味着如果您想在以后的 c# 版本中利用 linq 之类的东西,您的代码将更加简单易用。

    【讨论】:

      【解决方案2】:

      泛型具有更好的性能,尤其是在您使用值类型(int、bool、struct 等)时,您将获得显着的性能提升。

      1. 将 Arraylist 与 value-types 一起使用会导致装箱/拆箱,如果完成数百次,则比使用泛型 List 要慢得多。

      2. 当将值类型存储为对象时,每个项目最多可以使用四次内存。虽然这个数量不会耗尽您的 RAM,但较小的缓存内存可能包含较少的项目,这意味着在迭代长集合时,从主内存到缓存会有很多副本,这会减慢您的应用程序。

      我写了关于here

      【讨论】:

      • 您的应用程序逻辑有缺陷。具体来说, InsertItemsToList 的 stopper.Start() 放错了位置。它需要在循环内。此外,使用 List 对象与 ArrayList 对象时的性能差异很小 - 大约提高了 10% 的速度(我的数字是 1.982 / 1.81 / 1.233 / 1.089)。诚然,List 与 ArrayList 的性能提升对于 int 来说是非常有形的,它只是不适用于类。
      • @JustLoren - 即使没有轻微的“错误”,计算也大致相同。我在我的回答(上面)和博客文章中都写了性能差异主要是因为值类型的装箱/拆箱
      【解决方案3】:

      这是我对 100KB 文件中的字符串进行 100,000 次简单解析得到的结果。 Generic List(Of char) 花了 612.293 秒来遍历文件 100,000 次。 ArrayList 花了 2,880.415 秒来遍历文件 100,000 次。这意味着在这种情况下(因为您的里程有所不同)Generic List(Of char) 快 4.7 倍。

      这是我运行了 100,000 次的代码:

      Public Sub Run(ByVal strToProcess As String) Implements IPerfStub.Run
          Dim genList As New ArrayList
      
          For Each ch As Char In strToProcess.ToCharArray
              genList.Add(ch)
          Next
      
          Dim dummy As New System.Text.StringBuilder()
          For i As Integer = 0 To genList.Count - 1
              dummy.Append(genList(i))
          Next
      
      End Sub
      
       Public Sub Run(ByVal strToProcess As String) Implements IPerfStub.Run
           Dim genList As New List(Of Char)
      
           For Each ch As Char In strToProcess.ToCharArray
               genList.Add(ch)
           Next
      
           Dim dummy As New System.Text.StringBuilder()
           For i As Integer = 0 To genList.Count - 1
               dummy.Append(genList(i))
           Next
       End Sub
      

      【讨论】:

        【解决方案4】:

        如果 ArrayLists 中的实体是 Object 类型,你会从不将它们转换为正确的类型中获得一点好处。如果它们是值类型(结构体或 Int32 之类的原语),那么装箱/拆箱过程会增加很多开销,并且泛型集合应该快得多。

        Here's an MSDN article on the subject

        【讨论】:

          【解决方案5】:

          取决于您的代码中有多少。如果您在 UI 中绑定或显示大型列表,您可能会看到性能上的巨大提升。

          如果您的 ArrayList 只是到处散布,那么清理它可能没什么大不了的,但也不会对整体性能产生太大影响。

          如果您在整个代码中使用了很多 ArrayLists,并且替换它们会很麻烦(这可能会影响您的日程安排),那么您可以采用 if-you-touch-it-change-it 方法.

          不过,主要的一点是,泛型更易于阅读,并且由于您从它们那里获得的强类型化在整个应用程序中更加稳定。您不仅会看到性能的提升,还会看到代码的可维护性和稳定性。如果你能很快做,我会说做。

          如果您能从产品负责人那里获得支持,我建议您将其清理干净。之后你会更喜欢你的代码。

          【讨论】:

            【解决方案6】:

            正如您所说,从技术上讲,泛型的性能更好。但是,除非性能非常重要并且您已经在其他领域进行了优化,否则您可能会通过将时间花在其他方面来获得更好的改进。

            我建议:

            • 继续使用泛型。
            • 如果您有可靠的单元测试,那么在您接触代码时重构为泛型
            • 花其他时间进行重构/测量,以显着提高性能(数据库调用、更改数据结构等),而不是到处花几毫秒。

            当然改成泛型还有性能以外的原因:

            • 不易出错,因为您可以在编译时检查类型
            • 更具可读性,您无需到处进行强制转换,并且很明显集合中存储了什么类型
            • 如果您以后要使用泛型,那么在任何地方都使用它们会更干净

            【讨论】:

              【解决方案7】:

              泛型,无论是 Java 还是 .NET,都应该用于设计和类型安全,而不是用于性能。自动装箱不同于泛型(本质上是隐式对象到基元的转换),并且正如您所提到的,如果有很多算术或其他操作会导致重复的性能下降,则不应使用它们代替基元隐式对象创建/销毁。

              总体而言,我建议继续使用,并且仅在出于类型安全/设计目的而不是性能目的而需要清理现有代码时才更新现有代码。

              【讨论】:

                【解决方案8】:

                我的老公司其实也考虑过这个问题。我们采取的方法是:如果重构容易,就去做;如果没有(即它会涉及太多类),请稍后再处理。这实际上取决于您是否有时间去做,或者是否有更重要的项目需要编码(即您应该为客户实现的功能)。

                再说一次,如果您不是在为客户做某事,请继续花时间进行重构。它会提高你自己代码的可读性。

                【讨论】:

                  【解决方案9】:

                  自动装箱/拆箱与泛型有什么关系?这只是一个类型安全问题。对于非泛型集合,您需要显式转换回对象的实际类型。使用泛型,您可以跳过此步骤。我认为这两种方式没有性能差异。

                  【讨论】:

                  • 查看有关泛型的文档及其在使用结构时的用处,您将了解装箱与它的关系。 (提示:你无法获得指向堆栈变量的对象指针)
                  【解决方案10】:

                  这取决于,最好的答案是分析您的代码并查看。我喜欢 AQTime,但有很多包可以解决这个问题。

                  一般来说,如果 ArrayList 被大量使用,则可能值得将其切换到通用版本。但实际上,您很可能甚至无法衡量性能差异。装箱和拆箱是额外的步骤,但现代计算机速度如此之快,几乎没有区别。由于 ArrayList 实际上只是一个带有漂亮包装器的普通数组,因此您可能会看到更好的数据结构选择(ArrayList.Remove 是 O(n)!)比转换为泛型获得了更多的性能。

                  编辑:Outlaw Programmer 有一个很好的观点,你仍然会使用泛型进行装箱和拆箱,它只是隐含地发生。不过,所有有关检查强制转换和“is/as”关键字的异常和空值的代码都会有所帮助。

                  【讨论】:

                    【解决方案11】:

                    最大的收获是在维护阶段。泛型更容易处理和更新,无需处理转换和转换问题。如果这是您不断访问的代码,那么一定要努力。如果这是多年未触及的代码,我真的不会打扰。

                    【讨论】:

                      【解决方案12】:

                      确定的唯一方法是使用 dotTrace 之类的工具分析您的代码。

                      http://www.jetbrains.com/profiler/

                      装箱/拆箱可能在您的特定应用程序中是微不足道的,不值得重构。展望未来,由于编译时类型安全,您仍应考虑使用泛型。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2023-03-06
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-08-21
                        • 2013-06-27
                        • 1970-01-01
                        相关资源
                        最近更新 更多