120 votes

Empreinte mémoire des types de données Haskell

Comment trouver la quantité réelle de mémoire nécessaire pour stocker une valeur d'un certain type de données en Haskell (principalement avec GHC) ? Est-il possible de l'évaluer en cours d'exécution (par exemple dans GHCi) ou est-il possible d'estimer les besoins en mémoire d'un type de données composé à partir de ses composants ?

En général, si les besoins en mémoire des types a y b sont connus, quelle est la charge mémoire de types de données algébriques tels que

data Uno = Uno a
data Due = Due a b

Par exemple, combien d'octets en mémoire ces valeurs occupent-elles ?

1 :: Int8
1 :: Integer
2^100 :: Integer
\x -> x + 1
(1 :: Int8, 2 :: Int8)
[1] :: [Int8]
Just (1 :: Int8)
Nothing

Je comprends que l'allocation réelle de mémoire est plus élevée en raison du retard de la collecte des déchets. Elle peut être sensiblement différente en raison de l'évaluation paresseuse (et la taille du thunk n'est pas liée à la taille de la valeur). La question est la suivante : étant donné un type de données, quelle est la taille de sa valeur lorsqu'elle est entièrement évaluée ?

J'ai trouvé qu'il y a :set +s dans GHCi pour voir les statistiques de mémoire, mais il n'est pas clair comment estimer l'empreinte mémoire d'une seule valeur.

153voto

Simon Marlow Points 9153

(Ce qui suit s'applique à GHC, d'autres compilateurs peuvent utiliser des conventions de stockage différentes)

Règle générale : un constructeur coûte un mot pour un en-tête, et un mot pour chaque champ . Exception : un constructeur sans champs (comme Nothing o True ) ne prend pas de place, car GHC crée une seule instance de ces constructeurs et la partage entre toutes les utilisations.

Un mot représente 4 octets sur une machine de 32 bits, et 8 octets sur une machine de 64 bits.

Ainsi, par exemple

data Uno = Uno a
data Due = Due a b

un Uno prend 2 mots, et un Due prend 3.

El Int est défini comme suit

data Int = I# Int#

maintenant, Int# prend un mot, donc Int en prend 2 au total. La plupart des types non emballés prennent un mot, les exceptions étant Int64# , Word64# y Double# (sur une machine 32 bits) qui prennent 2. GHC dispose en fait d'un cache de petites valeurs de type Int y Char Ainsi, dans de nombreux cas, ils ne prennent pas d'espace dans le tas. A String ne requiert de l'espace que pour les cellules de la liste, à moins que vous n'utilisiez la fonction Char s > 255.

Un site Int8 a une représentation identique à celle de Int . Integer est défini comme suit :

data Integer
  = S# Int#                            -- small integers
  | J# Int# ByteArray#                 -- large integers

donc un petit Integer ( S# ) prend 2 mots, mais un grand nombre entier prend une quantité variable d'espace en fonction de sa valeur. A ByteArray# prend 2 mots (en-tête + taille) plus l'espace pour le tableau lui-même.

Notez que un constructeur défini avec newtype est gratuit . newtype est une idée purement compilatoire, qui ne prend pas de place et ne coûte pas d'instructions au moment de l'exécution.

Plus de détails dans La disposition des objets du tas dans le commentaire de GHC .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X