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. Эти проблемы никогда бы не возникли, если бы кортежи были представлены типами значений, а не ссылочными типами для начала ...