Как узнать представления памяти типов данных в GHC? - PullRequest
15 голосов
/ 04 июля 2011

Недавно в блогах, таких как Вычисление размера хэш-карты , объяснялось, как рассуждать о пространственных сложностях часто используемых типов контейнеров.Теперь я сталкиваюсь с вопросом о том, как на самом деле «увидеть», какой макет памяти выбирает моя версия GHC (в зависимости от флагов компиляции и целевой архитектуры) для странных типов данных (конструкторов), таких как

data BitVec257 = BitVec257 {-# UNPACK #-} !Word64
                           {-# UNPACK #-} !Word64
                           {-# UNPACK #-} !Bool
                           {-# UNPACK #-} !Word64
                           {-# UNPACK #-} !Word64

data BitVec514 = BitVec514 {-# UNPACK #-} !BitVec257
                           {-# UNPACK #-} !BitVec257

В Cесть операторы sizeof и offsetof, которые позволяют мне «увидеть», какой размер и выравнивание были выбраны для полей C struct.

Я пытался взглянуть на GHC Core внадеюсь найти там подсказку, но я не знал, что искать.Может ли кто-нибудь указать мне правильное направление?

Ответы [ 2 ]

11 голосов
/ 04 июля 2011

Моя первая идея состояла в том, чтобы использовать эту аккуратную функцию litte , благодаря Саймону Марлоу:

{-# LANGUAGE MagicHash,UnboxedTuples #-}
module Size where

import GHC.Exts
import Foreign

unsafeSizeof :: a -> Int
unsafeSizeof a =
  case unpackClosure# a of
    (# x, ptrs, nptrs #) ->
      sizeOf (undefined::Int) + -- one word for the header
        I# (sizeofByteArray# (unsafeCoerce# ptrs)
             +# sizeofByteArray# nptrs)

Использование:

Prelude> :!ghc -c Size.hs

Size.hs:15:18:
    Warning: Ignoring unusable UNPACK pragma on the
             third argument of `BitVec257'
    In the definition of data constructor `BitVec257'
    In the data type declaration for `BitVec257'
Prelude Size> unsafeSizeof $! BitVec514 (BitVec257 1 2 True 3 4) (BitVec257 1 2 True 3 4)
74

(Обратите внимание, что GHCговорит вам, что не может распаковать Bool, так как это тип суммы.)

Вышеупомянутая функция утверждает, что ваш тип данных использует 74 байта на 64-битной машине.Я считаю, что трудно поверить.Я ожидаю, что тип данных будет использовать 11 слов = 88 байт, одно слово на поле.Даже Bool s берут одно слово, так как они являются указателями на (статически размещенные) конструкторы.Я не совсем уверен, что здесь происходит.

Что касается выравнивания, я считаю, что каждое поле должно быть выровнено по словам.

4 голосов
/ 04 июля 2011

Следы памяти типов данных Haskell

(следующее относится к GHC, другие компиляторы могут использовать другие соглашения о хранении)

Правило большого пальца: конструктор стоит одинслово для заголовка и одно слово для каждого поля.Исключение: конструктор без полей (например, Nothing или True) не занимает места, потому что GHC создает один экземпляр этих конструкторов и разделяет его среди всех применений.

Слово - 4 байта на 32-разрядной машинеи 8 байтов на 64-битной машине.

Так, например,

data Uno = Uno a
data Due = Due a b

Uno занимает 2 слова, а Due - 3.

Также я верю в этоможно написать функцию haskell, которая выполняет те же задачи, что и sizeof или offsetof

...