泛型与数组列表

时间:2008-09-18 17:48:12

标签: c# performance generics

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

从性能角度来看,将代码更改为使用泛型有多重要?我从性能的角度来看,拳击和拆箱等等,这是低效的,但是改变它会带来多大的性能提升?仿制药是否可以在前进的基础上使用,或者是否有足够的性能改变以便更新旧代码?

12 个答案:

答案 0 :(得分:14)

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

我建议:

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

当然除了性能之外还有其他原因可以改为泛型:

  • 不易出错,因为您有类型的编译时检查
  • 更具可读性,你不需要在所有地方进行投射,而且很明显哪种类型存储在集合中
  • 如果你正在使用仿制药,那么到处使用它们会更清洁

答案 1 :(得分:8)

这是我从100KB文件中简单解析一个字符串100,000次得到的结果。通用列表(Of char)在文件中花费了612.293秒来完成100,000次。 ArrayList花了2,880.415秒来完成文件的100,000次。这意味着在这种情况下(因为您的里程变化),通用列表(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

答案 2 :(得分:4)

唯一可以确定的方法是使用dotTrace等工具分析您的代码。

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

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

答案 3 :(得分:3)

泛型,无论是Java还是.NET,都应该用于设计和类型安全,而不是性能。自动装箱与通用装置(基本上是隐式对象到原始转换)不同,正如您所提到的,如果存在大量算术或其他操作会导致重复性能损失,则不应使用它们代替基元。隐式对象创建/销毁。

总的来说,我建议使用前进,只更新现有代码,如果需要为了类型安全/设计而不是性能而进行清理。

答案 4 :(得分:1)

这取决于,最好的答案是分析您的代码并查看。我喜欢AQTime,但是存在许多包。

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

编辑:Outlaw程序员有一个好点,你仍然会使用泛型装箱和拆箱,它只是隐含地发生。检查异常的所有代码和来自转换的“null / as”关键字的空值会有所帮助。

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

我的老公司实际上考虑过这个问题。我们采取的方法是:如果它很容易重构,那就去做;如果没有(即它会触及太多的课程),请留待以后。这实际上取决于你是否有时间去做,或者是否有更重要的项目要编码(即你应该为客户实现的功能)。

然后,如果你没有为客户工作,请继续花时间重构。它将为您自己提高代码的可读性。

答案 8 :(得分:0)

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

如果您的ArrayList只是随处可见,那么清理它可能不是什么大问题,但也不会对整体性能造成太大影响。

如果您在整个代码中使用了大量的ArrayLists,那么替换它们(可能会影响您的日程安排)会有很大的不足之处,那么您可以采用if-you-touch-it-change-it方法

但主要的是,Generics更易于阅读,并且由于您从中获得了强大的输入,因此在整个应用程序中更加稳定。您不仅可以从性能上获得收益,还可以从代码维护性和稳定性中获益。如果你能快点做到,我会说这样做。

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

答案 9 :(得分:0)

如果ArrayLists中的实体是Object类型,那么从未将它们转换为正确的类型可以获得一点点。如果它们是值类型(结构或原始数据,如Int32),那么装箱/拆箱过程会增加很多开销,而通用集合应该快得多。

Here's an MSDN article on the subject

答案 10 :(得分:0)

泛型具有更好的性能,特别是如果你将使用值类型(int,bool,struct等),你将获得显着的性能提升。

  1. 使用带有值类型的Arraylist会导致装箱/拆箱,如果完成几百次,则使用通用列表会慢一些。

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

  3. 我写了here

答案 11 :(得分:0)

使用泛型也意味着如果你想在后来的c#版本中利用linq之类的东西,你的代码将变得简单易用。