Как я могу найти фактический объем памяти требуемым сохранить значение некоторого типа данных в Haskell (главным образом с GHC)? Действительно ли возможно оценить его во времени выполнения (например, в GHCi), или действительно ли возможно оценить требования к памяти составного типа данных от его компонентов?
В целом, если требования к памяти типов a
и b
известны, что является памятью наверху алгебраических типов данных, таких как:
data Uno = Uno a
data Due = Due a b
Например, сколько занимают байты в памяти эти значения?
1 :: Int8
1 :: Integer
2^100 :: Integer
\x -> x + 1
(1 :: Int8, 2 :: Int8)
[1] :: [Int8]
Just (1 :: Int8)
Nothing
Я понимаю, что фактическое выделение памяти происходит выше из-за задержанной сборки "мусора". Это может существенно отличаться из-за отложенных вычислений (и преобразовать размер, не связан с размером значения). Вопрос, учитывая тип данных, сколько памяти его значение берет, когда полностью оценено?
Я нашел, что существует a :set +s
опция в GHCi для наблюдения статистики памяти но не ясно, как оценить объем потребляемой памяти единственного значения.
(Нижеследующее относится к GHC, другие компиляторы могут использовать другие соглашения о хранении)
Эмпирическое правило: конструктор стоит одно слово для заголовка, и одно слово для каждого поля. Исключение: конструктор без полей (например, Nothing
или True
) не занимает места, потому что GHC создает один экземпляр этих конструкторов и делит его между всеми использованиями.
Слово - это 4 байта на 32-битной машине и 8 байт на 64-битной.
Поэтому, например,
data Uno = Uno a
data Due = Due a b
для Uno
требуется 2 слова, а для Due
- 3.
Тип Int
определяется как
data Int = I# Int#
теперь, Int#
занимает одно слово, поэтому Int
занимает 2 слова. Большинство невложенных типов занимают одно слово, исключение составляют Int64#
, Word64#
и Double#
(на 32-битной машине), которые занимают 2. На самом деле GHC имеет кэш небольших значений типа Int
и Char
, поэтому во многих случаях они вообще не занимают места в куче. String
требует места только для ячеек списка, если вы не используете Char
s > 255.
Int8
имеет представление, идентичное Int
. Integer
определяется следующим образом:
data Integer
= S# Int# -- small integers
| J# Int# ByteArray# -- large integers
поэтому маленькое Integer
(S#
) занимает 2 слова, а большое целое число занимает переменное количество места в зависимости от его значения. ByteArray#
занимает 2 слова (заголовок + размер) плюс место для самого массива.
Обратите внимание, что конструктор, определенный с помощью newtype
, является свободным. newtype
- это чисто компилятивная идея, она не занимает места и не стоит инструкций во время выполнения.
Подробнее в Расположение объектов кучи в комментарии GHC.