Как функциональные языки представляют алгебраические типы данных в памяти?

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

data Nucleotide = A | T | C | G

Я полагаю, вы поступили бы так же в Standard ML или OCaml (я никогда толком не пользовался ни тем, ни другим).

Очевидно, что значение типа Nucleotide может содержаться в двух битах. Однако это приведет к тому, что время доступа будет медленнее, чем если бы вы использовали один байт на значение Nucleotide, так как вам нужно было бы выбрать два интересующих бита с помощью бинарных операторов.

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

data Maybe a = Just a | Nothing

Ясно, что значение Maybe a формы Just a логически больше, чем значение формы Nothing. В крайнем случае вроде этого:

data Hulk a b c d e = Big a b c d e | Little

вы определенно не захотите хранить в Little значения нулевые указатели или нулевые значения для пяти значений, содержащихся в Big значениях. Я предполагаю, что вы просто использовали бы память переменного размера, выделенную кучей, с идентификатором конструктора в начале (например, 0 для Big и 1 для Little). Однако, если вы хотите сохранить Hulk значений в стеке (более быстрое представление), вам нужно будет сохранить пустую память вместе со значениями Little, чтобы все значения типа Hulk имели одинаковый размер. Еще один компромисс.

Саймон Марлоу ответил на мой общий вопрос относительно GHC в предыдущем вопросе StackOverflow. Однако у меня есть три связанных вопроса, на которые нет ответа:

  • Используют ли стандартный ML (SML / NJ и MLton) и OCaml один и тот же метод?
  • Если да, то экспериментируют ли какие-либо менее распространенные компиляторы этих языков (или их братьев и сестер) с другими методами?
  • Есть ли в этих языках достаточно простой способ (в идеале - прагма или флаг опции) использовать более эффективное представление памяти, например двухбитное представление Nucleotide? Такая эффективность памяти необходима для многих приложений биоинформатики; если бы каждый Nucleotide должен был быть одним байтом, высокопроизводительные алгоритмы биоинформатики должны были бы прибегнуть к ручной настройке битов.

person Mike    schedule 18.07.2014    source источник
comment
Для haskell вы можете проверить параметры GHC, например -ddump-asm или -ddump-simpl, чтобы просмотреть, как он хранится на нижний уровень. По сути, для вашего простого примера каждый тег представляется как long, но там есть некоторые метаданные, и я тоже не совсем уверен, что он делает. Основная суть состоит в том, что каждый конструктор превращается в замыкание, а затем они объединяются, чтобы сформировать замыкание типа данных.   -  person bheklilr    schedule 18.07.2014
comment
Вы, конечно, не получите более определенного ответа (или ответчика) о GHC, чем Саймон Марлоу в связанном вопросе. Поскольку это де-факто стандартная реализация Haskell, возможно, вам следует сделать свой вопрос специфичным для другого языка - или, возможно, мы можем закрыть этот как дубликат этого. Что вы думаете?   -  person Daniel Wagner    schedule 18.07.2014
comment
@DanielWagner: Я полагаю, что это не полный ответ на мой текущий вопрос, поскольку я также спрашивал о SML и OCaml. Я перефразирую его, чтобы спросить об общих методах и реализациях, эффективных с точки зрения памяти.   -  person Mike    schedule 18.07.2014
comment
Представление данных вряд ли станет чем-то, о чем можно легко сделать общие заявления, поскольку спецификации функциональных языков высокого уровня оставляют много возможностей для различных реализаций. Кто-то может сказать об OCaml окончательно, но у SML есть множество реализаций. Однако я предполагаю, что ни один из этих языков не даст вам автоматически желаемое компактное представление с помощью только прагмы или флага, поскольку они имеют тенденцию к распределению кучи и единообразному представлению из-за параметрического полиморфизма.   -  person Levi Pearson    schedule 18.07.2014
comment
@LeviPearson: Хороший аргумент. Я указал, о каких компиляторах SML я думал: SML / NJ и MLton. Я не знаю других, которые обычно используются.   -  person Mike    schedule 19.07.2014
comment
Разве двухбитное представление Nucleotide на самом деле не быстрее для многих алгоритмов с большим объемом памяти, потому что ЦП вызывает меньше промахов в кеш-памяти?   -  person Jeff Burdges    schedule 17.02.2015


Ответы (1)


Однозначного ответа нет: типы данных являются абстрактными структурами и могут быть реализованы различными способами по усмотрению разработчика. На практике такие соображения, как раздельная компиляция, имеют тенденцию к некоторому ограничению.

В конкретном случае упаковки типа данных, содержащего только конструкторы с нулевым значением, в как можно меньшее количество битов, вы можете продолжить, определив функции от типа данных до небольшого целого числа и обратно. Целочисленный тип, скрытый абстрактным типом (или в Haskell, newtype), также был бы разумным выбором. Ваша работа - это упаковка и распаковка небольших целых чисел в любую агрегированную форму, с которой вы работаете.

Кстати, в Real World OCaml есть очень хорошая глава о представлении значений OCaml (приблизительное резюме: не сильно отличается от GHC для целей этого вопроса).

person gsg    schedule 19.07.2014
comment
Значения OCaml не сильно различаются, когда вы остаетесь в общем подмножестве, но так называемые «полиморфные варианты», которые не входят в общее подмножество, сами по себе заслуживают внимания. - person Pascal Cuoq; 19.07.2014
comment
Действительно, есть еще предметы. Я не думаю, что эти конструкции имеют большое отношение к вопросу OP, поэтому я перефразирую, чтобы предположить, что есть еще кое-что по теме. - person gsg; 19.07.2014