Кто-нибудь знает ответ и / или имеет мнение по этому поводу?
Поскольку кортежи обычно не очень большие, я предполагаю, что для них имеет смысл использовать структуры, а не классы. Что скажешь?
Кто-нибудь знает ответ и / или имеет мнение по этому поводу?
Поскольку кортежи обычно не очень большие, я предполагаю, что для них имеет смысл использовать структуры, а не классы. Что скажешь?
Ответы:
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 :
Первым важным решением было рассматривать кортежи как ссылочный или как значение. Поскольку они неизменяемы каждый раз, когда вы хотите изменить значения кортежа, вам нужно создать новый. Если это ссылочные типы, это означает, что при изменении элементов кортежа в жестком цикле может образоваться много мусора. Кортежи F # были ссылочными типами, но у команды было ощущение, что они могли бы добиться улучшения производительности, если бы вместо них два, а возможно, и три кортежа элементов были типами значений. Некоторые группы, создававшие внутренние кортежи, использовали значения вместо ссылочных типов, потому что их сценарии были очень чувствительны к созданию большого количества управляемых объектов. Они обнаружили, что использование типа значения дает им лучшую производительность. В нашем первом черновике спецификации кортежа мы сохранили двух-, трех- и четырехэлементные кортежи как типы значений, а остальные были ссылочными типами. Однако во время встречи по дизайну, на которой присутствовали представители других языков, было решено, что этот «разделенный» дизайн будет сбивать с толку из-за немного различающейся семантики между двумя типами. Последовательность в поведении и дизайне была определена как более высокий приоритет, чем возможное повышение производительности. Основываясь на этих входных данных, мы изменили дизайн так, чтобы все кортежи были ссылочными типами, хотя мы попросили команду F # провести некоторое исследование производительности, чтобы увидеть, не произошло ли ускорение при использовании типа значения для некоторых размеров кортежей. У него был хороший способ проверить это, поскольку его компилятор, написанный на F #, был хорошим примером большой программы, которая использовала кортежи в самых разных сценариях. В конце концов, команда разработчиков F # обнаружила, что не происходит повышения производительности, когда некоторые кортежи были типами значений, а не ссылочными типами. Это заставило нас почувствовать себя лучше, приняв решение использовать ссылочные типы для кортежа.
Если бы типы .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)
ValueTuple<...>
. См. Ссылку на типы кортежей C #