Generics vs. Array Lists - PullRequest
       52

Generics vs. Array Lists

15 голосов
/ 18 сентября 2008

Система, над которой я работаю, была написана до .net 2.0 и не имела преимуществ обобщений. В конечном итоге он был обновлен до 2.0, но ни один из кодов не был реорганизован из-за нехватки времени. Есть ряд мест, где код использует ArraysLists и т. Д., Которые хранят вещи как объекты.

Насколько важно изменить код на использование обобщений с точки зрения производительности? Я знаю, с точки зрения производительности, упаковки и распаковки и т. Д., Это неэффективно, но сколько выигрыша в производительности вы получите от его изменения? Являются ли дженерики чем-то, что можно использовать для дальнейшего развития, или достаточно изменений производительности, чтобы приложить усилия для обновления старого кода?

Ответы [ 12 ]

14 голосов
/ 18 сентября 2008

Технически производительность дженериков, как вы говорите, лучше. Однако, если производительность не является чрезвычайно важной и вы уже оптимизировали в других областях, вы, вероятно, получите НАМНОГО лучших улучшений, потратив свое время в другом месте.

Я бы предложил:

  • используйте дженерики в будущем.
  • если у вас есть солидные юнит-тесты, то при изменении кода рефакторинг к дженерикам
  • тратить другое время на рефакторинг / измерение, которые значительно улучшат производительность (вызовы базы данных, изменение структуры данных и т. Д.), А не несколько миллисекунд здесь и там.

Конечно, есть и другие причины, кроме производительности, чтобы перейти на дженерики:

  • меньше ошибок, так как у вас есть проверка типов во время компиляции
  • более читабельно, вам не нужно приводить повсюду, и очевидно, какой тип хранится в коллекции
  • если вы будете использовать дженерики в будущем, тогда чище использовать их повсюду
8 голосов
/ 18 сентября 2008

Вот результаты, которые я получил от простого разбора строки из файла размером 100 КБ 100 000 раз. Общий список (Char) занял 612,293 секунды, чтобы просмотреть файл 100 000 раз. ArrayList потребовалось 2880,415 секунд, чтобы просмотреть файл 100 000 раз. Это означает, что в этом сценарии (поскольку ваш пробег будет меняться ), общий список (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 голосов
/ 18 сентября 2008

Единственный способ узнать наверняка - это профилировать ваш код с помощью такого инструмента, как dotTrace.

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

Вполне возможно, что бокс / распаковка тривиальны в вашем конкретном приложении и не стоит рефакторинга. В дальнейшем вам все же следует подумать об использовании обобщений из-за безопасности типов во время компиляции.

3 голосов
/ 18 сентября 2008

Обобщения, будь то Java или .NET, должны использоваться для обеспечения безопасности проектирования и типов, а не для повышения производительности. Автобокс отличается от универсальных (по сути неявных преобразований объектов в примитивы), и, как вы упомянули, вы НЕ должны использовать их вместо примитивов, если будет много арифметических или других операций, которые приведут к снижению производительности от создание / уничтожение неявных объектов.

В целом, я бы предложил использовать движение вперед и обновлять существующий код только в том случае, если его необходимо очистить в целях безопасности / проектирования типов, а не производительности.

1 голос
/ 18 сентября 2008

Зависит, лучший ответ - профилировать свой код и посмотреть. Мне нравится AQTime, но для этого существует несколько пакетов.

В общем, если ArrayList используется LOT, возможно, стоит переключить его на универсальную версию. На самом деле, скорее всего, вы даже не сможете измерить разницу в производительности. Бокс и распаковка - это дополнительные шаги, но современные компьютеры настолько быстры, что это почти ничего не меняет. Поскольку ArrayList на самом деле является обычным массивом с хорошей оберткой, вы, вероятно, увидите гораздо большую производительность, полученную благодаря лучшему выбору структуры данных (ArrayList.Remove равно O (n)!), Чем при преобразовании в универсальные.

Edit: Outlaw Programmer имеет хорошую точку зрения, вы все равно будете боксировать и распаковывать с помощью дженериков, это просто происходит неявно. Весь код, связанный с проверкой исключений и нулей из приведения и ключевых слов "is / as", немного помог бы.

0 голосов
/ 22 января 2009

Использование обобщений также должно означать, что ваш код будет проще и проще в использовании, если вы захотите использовать такие вещи, как linq, в более поздних версиях c #.

0 голосов
/ 20 сентября 2008

Generics имеет гораздо лучшую производительность, особенно если вы будете использовать тип значения (int, bool, struct и т. Д.), Где вы получите заметный прирост производительности.

  1. Использование Arraylist с типами-значениями вызывает коробку / распаковку, которая, если она выполняется несколько сотен раз, значительно медленнее, чем использование универсального List.

  2. при хранении значений-типов в качестве объекта вы можете использовать до четырех раз память для каждого элемента. Хотя этот объем не истощает вашу оперативную память, кэш-память меньшего размера может содержать меньше элементов. Это означает, что при переборе длинных коллекций будет много копий из основной памяти в кэш, что замедлит работу вашего приложения.

Я писал о здесь .

0 голосов
/ 18 сентября 2008

Если сущности в ArrayLists имеют типы объектов, вы немного выиграете, если не приведете их к правильному типу. Если они являются типами Value (структуры или примитивы, такие как Int32), то процесс упаковки / распаковки добавляет много накладных расходов, и универсальные коллекции должны быть намного быстрее.

Вот статья MSDN на эту тему

0 голосов
/ 18 сентября 2008

Зависит от того, сколько там в вашем коде. Если вы связываете или отображаете большие списки в пользовательском интерфейсе, вы, вероятно, увидите значительный прирост производительности.

Если ваш ArrayList просто разбросан по местам, то, вероятно, не будет большой проблемой просто очистить его, но он также не сильно повлияет на общую производительность.

Если вы используете множество ArrayLists в своем коде, и было бы неприлично заменить их (что может повлиять на ваши графики), тогда вы могли бы принять подход «если ты коснешься, то изменишь» .

Главное, однако, что Generics намного проще для чтения и более стабильны во всем приложении благодаря строгой типизации, которую вы получаете от них. Вы увидите выгоды не только от производительности, но и от стабильности и устойчивости кода. Если вы можете сделать это быстро, я бы сказал, сделать это.

Если вы можете получить вступительный взнос от Владельца продукта, я бы порекомендовал очистить его. Вы любите свой код больше потом.

0 голосов
/ 18 сентября 2008

Моя старая компания действительно рассматривала эту проблему. Подход, который мы выбрали, заключался в следующем: если это легко изменить, сделайте это; если нет (то есть это затронет слишком много классов), оставьте это на более позднее время. Это действительно зависит от того, есть ли у вас время для этого или есть ли более важные элементы для кодирования (т.е. функции, которые вы должны реализовать для клиентов).

Опять же, если вы не работаете над чем-то для клиента, продолжайте проводить рефакторинг. Это улучшит читабельность кода для себя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...