Почему новый тип Tuple в .Net 4.0 является ссылочным типом (классом), а не типом значения (структурой) - PullRequest
86 голосов
/ 09 марта 2010

Кто-нибудь знает ответ и / или имеет какое-то мнение по этому поводу?

Поскольку кортежи обычно бывают не очень большими, я бы предположил, что для них было бы разумнее использовать структуры, чем классы.Что скажете вы?

Ответы [ 5 ]

90 голосов
/ 02 мая 2011

Microsoft сделала ссылки на все типы кортежей в интересах простоты.

Лично я считаю, что это было ошибкой. Кортежи с более чем 4 полями очень необычны и должны быть заменены более типичной альтернативой (например, тип записи в F #), так что только маленькие кортежи представляют практический интерес. Мои собственные тесты показали, что распакованные кортежи размером до 512 байт все еще могут быть быстрее, чем коробочные.

Хотя эффективность использования памяти является одной из проблем, я считаю, что доминирующей проблемой являются издержки сборщика мусора .NET. Выделение и сбор очень дорогие в .NET, потому что сборщик мусора не был сильно оптимизирован (например, по сравнению с JVM). Более того, по умолчанию .NET GC (рабочая станция) еще не распараллелена. Следовательно, параллельные программы, использующие кортежи, останавливаются, поскольку все ядра борются за общий сборщик мусора, разрушая масштабируемость. Это не только главная проблема, но, AFAIK, Microsoft полностью пренебрегла, когда исследовала эту проблему.

Еще одна проблема - виртуальная отправка. Ссылочные типы поддерживают подтипы и, следовательно, их члены обычно вызываются посредством виртуальной диспетчеризации. Напротив, типы значений не могут поддерживать подтипы, поэтому вызов члена полностью однозначен и всегда может быть выполнен как прямой вызов функции. Виртуальная диспетчеризация очень дорогостоящая на современном оборудовании, потому что ЦП не может предсказать, где остановится счетчик программ. JVM делает все возможное, чтобы оптимизировать виртуальную диспетчеризацию, а .NET - нет. Однако .NET обеспечивает выход из виртуальной отправки в форме типов значений. Таким образом, представление кортежей в качестве типов значений может снова значительно повысить производительность. Например, вызов GetHashCode для двухкратного кортежа миллион раз занимает 0,17 с, но вызов эквивалентной структуры занимает всего 0,008 с, то есть тип значения в 20 раз быстрее, чем ссылочный тип.

Реальная ситуация, когда эти проблемы с производительностью обычно возникают при использовании кортежей, заключается в использовании кортежей в качестве ключей в словарях. На самом деле я наткнулся на эту тему, перейдя по ссылке из вопроса переполнения стека F # запускает мой алгоритм медленнее, чем Python! , где авторская программа на F # оказалась медленнее, чем его Python именно потому, что он использовал кортежи в штучной упаковке. Распаковка вручную с использованием рукописного типа struct делает его программу на F # в несколько раз быстрее и быстрее, чем на Python. Эти проблемы никогда бы не возникли, если бы кортежи были представлены типами значений, а не ссылочными типами для начала ...

44 голосов
/ 09 марта 2010

Причина наиболее вероятна, потому что только меньшие кортежи будут иметь смысл в качестве типов значений, поскольку они будут иметь небольшой объем памяти. Большие кортежи (т. Е. Те, у которых больше свойств) фактически пострадают в производительности, так как они будут больше 16 байтов.

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

Ах, подозрения подтвердились! Пожалуйста, смотрите Building Tuple :

Первым важным решением было рассматривать кортежи либо как ссылку или тип значения. Так как они неизменным в любое время вы хотите изменить Значения кортежа, вы должны создать новый. Если они ссылочные типы, это означает, что может будет много мусора, если вы меняются элементы в кортеже в тугая петля. Кортежи F # были эталонными типы, но было ощущение от команда, чтобы они могли реализовать улучшение производительности, если два, и возможно, три кортежа были типы значений вместо. Некоторые команды, которые создали внутренние кортежи использовали значение вместо ссылочных типов, потому что их сценарии были очень чувствительны к созданию множества управляемых объекты. Они обнаружили, что используя значение Тип дал им лучшую производительность. В наш первый набросок кортежа спецификация, мы сохранили два, трех- и четырехэлементные кортежи как типы значений, остальное время ссылочные типы. Однако во время встреча дизайна, которая включала представители других языков было решено, что это «раскол» дизайн будет сбивать с толку, из-за немного другая семантика между два типа. Последовательность в поведении и дизайн был определен как более высокий приоритет, чем потенциальный производительность увеличивается. Основываясь на этом вход, мы изменили дизайн так, чтобы все кортежи являются ссылочными типами, хотя мы попросили команду F # сделать некоторое исследование производительности, чтобы увидеть если он испытал ускорение при использовании тип значения для некоторых размеров кортежей. Это был хороший способ проверить это, так как его компилятор, написанный на F #, был хороший пример большой программы, которая кортежи используются в различных сценариях. В конце концов, команда F # обнаружила, что это не получил улучшения производительности когда некоторые кортежи были типами значений вместо ссылочных типов. Это сделано мы лучше относимся к нашему решению используйте ссылочные типы для кортежа.

7 голосов
/ 25 октября 2012

Если типы .NET System.Tuple <...> определены как структуры, они не будут масштабируемыми. Например, троичный кортеж из длинных целых чисел в настоящее время масштабируется следующим образом:

type Tuple3 = System.Tuple<int64, int64, int64>
type Tuple33 = System.Tuple<Tuple3, Tuple3, Tuple3>
sizeof<Tuple3> // Gets 4
sizeof<Tuple33> // Gets 4

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

sizeof<Tuple3> // Would get 32
sizeof<Tuple33> // Would get 104

Поскольку у кортежей есть встроенная поддержка синтаксиса в F #, и они очень часто используются на этом языке, кортежи "struct" могут поставить программистов F # на риск написания неэффективных программ, даже не подозревая об этом. Это случилось бы так легко:

let t3 = 1L, 2L, 3L
let t33 = t3, t3, t3

По моему мнению, "структурированные" кортежи могут привести к значительной неэффективности в повседневном программировании. С другой стороны, существующие в настоящее время «классовые» кортежи также приводят к некоторой неэффективности, как упомянуто @Jon. Тем не менее, я думаю, что произведение «вероятности возникновения» на «потенциальный ущерб» будет намного выше для структур, чем для классов. Поэтому текущая реализация является меньшим злом.

В идеале, должны быть как кортежи "class", так и кортежи "struct", оба с синтаксической поддержкой в ​​F #!

Редактировать (2017-10-07)

Кортежи Struct теперь полностью поддерживаются следующим образом:

  • Встроено в mscorlib (.NET> = 4.7) как System.ValueTuple
  • Доступно как NuGet для других версий
  • Синтаксическая поддержка в C #> = 7
  • Синтаксическая поддержка в F #> = 4.1
4 голосов
/ 26 апреля 2010

Для двух кортежей вы все равно всегда можете использовать KeyValuePair из более ранних версий Common Type System. Это тип значения.

Небольшое уточнение к статье Мэтта Эллиса заключается в том, что различие в семантике использования между ссылочным типом и типом значения является лишь «небольшим», когда действует неизменяемость (что, конечно, будет иметь место в данном случае). Тем не менее, я думаю, что было бы лучше в проекте BCL не вводить путаницу, когда Tuple переходит к эталонному типу при некотором пороге.

0 голосов
/ 06 мая 2010

Не знаю, но использовали ли вы когда-нибудь F # Кортежи являются частью языка. Если я сделал .dll и возвратил тип кортежей, было бы неплохо иметь тип для его вставки. Теперь я подозреваю, что F # является частью языка (.Net 4), были внесены некоторые изменения в CLR, чтобы приспособить некоторые общие структуры в F #

С http://en.wikibooks.org/wiki/F_Sharp_Programming/Tuples_and_Records

let scalarMultiply (s : float) (a, b, c) = (a * s, b * s, c * s);;

val scalarMultiply : float -> float * float * float -> float * float * float

scalarMultiply 5.0 (6.0, 10.0, 20.0);;
val it : float * float * float = (30.0, 50.0, 100.0)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...