Есть ли у C # Generics преимущество в производительности? - PullRequest
36 голосов
/ 22 сентября 2008

У меня есть несколько классов данных, представляющих различные объекты.

Что лучше: написание универсального класса (скажем, для печати или вывода XML) с использованием дженериков и интерфейсов или написание отдельного класса для работы с каждым классом данных?

Есть ли повышение производительности или какое-либо другое преимущество (кроме того, что оно экономит мне время на написание отдельных классов)?

Ответы [ 12 ]

58 голосов
/ 22 сентября 2008

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

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

Не только да, но и ДА. Я не верил, насколько они могут измениться. Мы провели тестирование в VistaDB после переписывания небольшого процента кода ядра, который использовал ArrayLists и HashTables для обобщений. 250% или больше улучшений скорости.

Прочтите мой блог о тестировании , которое мы провели на дженериках против коллекций слабых типов Результаты поразили нас.

Я начал переписывать много старого кода, который использовал слабо типизированные коллекции в строго типизированные. Одна из моих самых больших проблем с интерфейсом ADO.NET - это то, что они не предоставляют более строго типизированных способов ввода и вывода данных. Время каста от объекта и обратно является абсолютным убийцей в приложениях большого объема.

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

Иногда вам приходится использовать слабо типизированные интерфейсы в среде исполнения dot net. Хотя, когда это возможно, ищите способы оставаться строго типизированными. Это действительно имеет огромное значение в производительности для нетривиальных приложений.

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

Обобщения в C # - это действительно общие типы с точки зрения CLR. Не должно быть принципиальной разницы между производительностью универсального класса и конкретного класса, который делает то же самое. Это отличается от Java Generics, который в большей степени представляет собой автоматизированное приведение типов, когда это необходимо, или шаблоны C ++, которые расширяются во время компиляции.

Вот хорошая статья, несколько старая, которая объясняет основной дизайн: «Разработка и внедрение дженериков для .NET Common Language Runtime ".

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

Таким образом, может быть выигрыш в производительности, но я бы сначала порекомендовал универсальное решение, а затем при необходимости оптимизировал. Это особенно верно, если вы ожидаете создать экземпляр универсального типа с множеством различных типов.

7 голосов
/ 22 сентября 2008

Я сделал несколько простых сравнений ArrayList и Generic Lists для другого вопроса: Generics vs. Array Lists , ваш пробег будет отличаться, но Generic List был в 4,7 раза быстрее, чем ArrayList.

Так что да, бокс / распаковка очень важны , если вы выполняете много операций. Если вы делаете простые вещи CRUD, я бы об этом не беспокоился.

6 голосов
/ 22 сентября 2008

Обобщения являются одним из способов параметризации кода и предотвращения повторения. Глядя на описание вашей программы и вашу мысль о написании отдельного класса для работы с каждым объектом данных, я бы склонялся к обобщению. Наличие одного класса, заботящегося о многих объектах данных, вместо множества классов, которые делают одно и то же, повышает вашу производительность. И, конечно, ваша производительность, измеряемая способностью изменять ваш код, как правило, важнее, чем производительность компьютера. : -)

4 голосов
/ 23 сентября 2008

Согласно Microsoft, Generics быстрее, чем приведение (примитивы для упаковки / распаковки) , что верно . Они также утверждают, что дженерики обеспечивают лучшую производительность, чем приведение между ссылочными типами, , что кажется неверным (никто не может доказать это).

Тони Нортруп - соавтор MCTS 70-536: Фонд разработки приложений - утверждает в той же книге следующее:

Я не смог воспроизвести преимущества производительности дженериков; однако, по словам Microsoft, генерики быстрее, чем при использовании Кастинг. На практике кастинг оказался быть в несколько раз быстрее, чем при использовании универсальный. Тем не менее, вы, вероятно, не будете заметить разницу в производительности в вашем Приложения. (Мои тесты более 100 000 итерации заняли всего несколько секунд.) Так что вы все равно должны использовать дженерики потому что они безопасны от типа.

Мне не удалось воспроизвести такие преимущества в производительности с помощью обобщений по сравнению с преобразованием между ссылочными типами - поэтому я бы сказал, что выигрыш в производительности «предполагается» больше, чем «значительный».

3 голосов
/ 23 января 2010

Дженерики быстрее!

Я также обнаружил, что Тони Нортруп в своей книге неправильно писал о производительности дженериков и не дженериков.

Я написал об этом в своем блоге: http://andriybuday.blogspot.com/2010/01/generics-performance-vs-non-generics.html

Вот отличная статья, в которой автор сравнивает производительность дженериков и не дженериков:

nayyeri.net / использование-генерики-к-повышения производительности

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

Если вы сравните общий список (например) с конкретным списком для точно используемого вами типа, тогда разница будет минимальной, результаты компилятора JIT почти одинаковы.

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

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

2 голосов
/ 24 сентября 2008

См. Также блог Рико Мариани на MSDN:

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

Q1: что быстрее?

Версия Generics значительно быстрее, см. ниже.

Статья немного старая, но дает подробности.

2 голосов
/ 23 сентября 2008

В случае универсальных коллекций по сравнению с боксом и др. С более старыми коллекциями, такими как ArrayList, генерики выигрывают в производительности. Но в подавляющем большинстве случаев это не самое важное преимущество дженериков. Я думаю, что есть две вещи, которые имеют гораздо большую выгоду:

  1. Тип безопасности.
  2. Самодокументируемый, более читабельный.

Дженерики способствуют обеспечению безопасности типов, заставляя более однородную коллекцию. Представьте, что вы наткнетесь на веревку, когда ожидаете int. Уч.

Общие коллекции также более самодокументированы. Рассмотрим две коллекции ниже:

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

Читая первую строку, вы можете подумать, что listOfNames - это список строк. Неправильно! На самом деле хранятся объекты типа NameType. Во втором примере не только утверждается, что типом должен быть NameType (или его потомок), но и код более читабелен. Я сразу понял, что мне нужно найти TypeName и научиться использовать его, просто взглянув на код.

Я видел много таких вопросов, «лучше ли х работает лучше, чем у» в StackOverflow. Вопрос здесь был очень справедливым, и, как выяснилось, дженерики - это победа в любом случае. Но, в конце концов, главное - предоставить пользователю что-то полезное. Конечно, ваше приложение должно иметь возможность работать, но оно также не должно аварийно завершать работу, и вы должны иметь возможность быстро реагировать на ошибки и запросы функций. Я думаю, вы можете увидеть, как эти два последних пункта связаны с безопасностью типов и читабельностью кода в общих коллекциях. Если бы это был обратный случай, если бы ArrayList превзошел List <>, я бы, вероятно, все равно взял бы реализацию List <>, если разница в производительности не была значительной.

Что касается производительности (в целом), я был бы готов поспорить, что вы найдете большинство своих узких мест производительности в этих областях в течение вашей карьеры:

  1. Плохое оформление базы данных или запросов к базе данных (включая индексацию и т. Д.),
  2. Плохое управление памятью (забыл вызвать dispose, глубокие стеки, слишком долго держатся за объекты и т. Д.),
  3. Неправильное управление потоками (слишком много потоков, не вызывая IO для фонового потока в настольных приложениях и т. Д.),
  4. Плохая конструкция ввода-вывода.

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

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