Подтвердить что ты не робот

У С# Generics есть преимущество в производительности?

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

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

Я не смог воспроизвести такие преимущества производительности с помощью дженериков по сравнению с литьем между ссылочными типами, поэтому я бы сказал, что усиление производительности "предполагается" более чем "значительным".

Ответ 7

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

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

также были обобщены общие классы классов в библиотеке .net, и вы вряд ли будете лучше себя чувствовать.

Ответ 8

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

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

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

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

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

Ответ 9

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

Конечно, если код не является медленной частью критически важного процесса, вам следует сосредоточиться на ясности.

Ответ 10

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

  • Введите безопасность.
  • Собственная документация, более читаемая.

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

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

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

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

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

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

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

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

Ответ 12

Не только вы можете покончить с боксом, но общие реализации несколько быстрее, чем не общие экземпляры с ссылочными типами из-за изменения базовой реализации.

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

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

Кроме того, итерация через foreach в List < > (а не IList < > ) выполняется быстрее из-за перечислителя ArrayList, требующего выделения кучи. По общему признанию, это привело к непонятной ошибке