Кто-нибудь знает ответ и / или имеет мнение по этому поводу?
Поскольку кортежи обычно не очень большие, я предполагаю, что для них имеет смысл использовать структуры, а не классы. Что скажешь?
performance
class
struct
.net-4.0
Бент Расмуссен
источник
источник
ValueTuple<...>
. См. Ссылку на типы кортежей C #Ответы:
Microsoft сделала все типы кортежей ссылочными типами в интересах простоты.
Я лично считаю это ошибкой. Кортежи с более чем 4 полями очень необычны и в любом случае должны быть заменены более типизированной альтернативой (например, тип записи в F #), поэтому практический интерес представляют только небольшие кортежи. Мои собственные тесты показали, что неупакованные кортежи размером до 512 байт все еще могут быть быстрее, чем упакованные кортежи.
Хотя эффективность памяти является одной из проблем, я считаю, что основной проблемой являются накладные расходы сборщика мусора .NET. Размещение и сборка в .NET обходятся очень дорого, потому что его сборщик мусора не был оптимизирован очень сильно (например, по сравнению с JVM). Более того, стандартный .NET GC (рабочая станция) еще не распараллеливался. Следовательно, параллельные программы, использующие кортежи, перестают работать, поскольку все ядра борются за общий сборщик мусора, разрушая масштабируемость. Это не только основная проблема, но, AFAIK, Microsoft полностью проигнорировала эту проблему.
Еще одна проблема - виртуальная отправка. Ссылочные типы поддерживают подтипы, поэтому их члены обычно вызываются через виртуальную диспетчеризацию. Напротив, типы значений не могут поддерживать подтипы, поэтому вызов члена полностью однозначен и всегда может выполняться как прямой вызов функции. Виртуальная диспетчеризация обходится очень дорого на современном оборудовании, потому что ЦП не может предсказать, где окажется счетчик программ. JVM делает все возможное для оптимизации виртуальной диспетчеризации, а .NET - нет. Однако .NET обеспечивает выход из виртуальной диспетчеризации в виде типов значений. Таким образом, представление кортежей как типов значений могло бы, опять же, значительно улучшить производительность. Например, позвонив
GetHashCode
для кортежа из 2 миллионов раз требуется 0,17 с, но для его вызова в эквивалентной структуре требуется всего 0,008 с, т.е. тип значения в 20 раз быстрее, чем ссылочный тип.Реальная ситуация, когда эти проблемы с производительностью кортежей обычно возникают, заключается в использовании кортежей в качестве ключей в словарях. Я фактически наткнулся на эту ветку, перейдя по ссылке из вопроса о переполнении стека. F # выполняет мой алгоритм медленнее, чем Python! где авторская программа на F # оказалась медленнее, чем его Python именно потому, что он использовал коробочные кортежи. Распаковка вручную с использованием рукописного
struct
шрифта делает его программу F # в несколько раз быстрее и быстрее, чем Python. Этих проблем никогда бы не возникло, если бы кортежи были представлены типами значений, а не ссылочными типами для начала ...источник
Tuple<_,...,_>
типы могли быть запечатаны, и в этом случае виртуальная отправка не потребовалась бы, несмотря на то, что они являются ссылочными типами. Мне больше любопытно, почему они не запечатаны, чем почему они являются ссылочными типами.Причина, скорее всего, заключается в том, что только кортежи меньшего размера будут иметь смысл в качестве типов значений, поскольку они будут иметь небольшой объем памяти. Кортежи большего размера (т.е. кортежи с большим количеством свойств) фактически пострадали бы от производительности, поскольку они были бы больше 16 байт.
Вместо того, чтобы некоторые кортежи были типами значений, а другие были ссылочными типами, и заставляли разработчиков знать, какие из них, я представляю, люди в Microsoft думали, что сделать их все ссылочные типы проще.
Ах, подозрения подтвердились! Пожалуйста, смотрите Building Tuple :
источник
Если бы типы .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 # и они чрезвычайно часто используются в этом языке, "структурные" кортежи могут подвергнуть программистов F # риску написания неэффективных программ, даже не подозревая об этом. Это случилось бы так легко:
let t3 = 1L, 2L, 3L let t33 = t3, t3, t3
На мой взгляд, "структурные" кортежи с большой вероятностью вызовут значительную неэффективность в повседневном программировании. С другой стороны, существующие в настоящее время кортежи «классов» также вызывают определенную неэффективность, как упоминал @Jon. Однако я думаю, что произведение «вероятность возникновения» на «потенциальный ущерб» будет намного выше для структур, чем для классов. Следовательно, текущая реализация - меньшее зло.
В идеале должны быть как кортежи «классов», так и кортежи «структуры», оба с синтаксической поддержкой в F #!
Изменить (2017-10-07)
Кортежи структур теперь полностью поддерживаются следующим образом:
источник
ref
, или может не понравиться тот факт, что так называемые «неизменяемые структуры» нет, особенно когда они упакованы. Жаль, что .net никогда не реализовывал концепцию передачи параметров принудительным элементомconst ref
, поскольку во многих случаях такая семантика - это то, что действительно требуется.Dictionary
, например здесь: stackoverflow.com/questions/5850243 /…Для 2-кортежей вы всегда можете использовать KeyValuePair <TKey, TValue> из более ранних версий Common Type System. Это ценностный тип.
Небольшое уточнение к статье Мэтта Эллиса будет заключаться в том, что разница в семантике использования между ссылочными типами и типами значений является лишь «незначительной», когда действует неизменяемость (что, конечно, будет иметь место здесь). Тем не менее, я думаю, что было бы лучше в дизайне BCL не вводить путаницу, связанную с переходом Tuple к ссылочному типу на некотором пороге.
источник
Я не знаю, но использовали ли вы когда-нибудь 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)
источник