En plus de user239558 réponse, et en réponse à votre commentaire, je tiens à souligner certains des outils qui vous permettent d'inspecter le segment de la représentation de votre valeur, de trouver des réponses à des questions de ce genre vous-même et de voir l'effet des optimisations et des différentes façons de compilation.
vous indique la taille d'une fermeture. Ici vous pouvez voir que (sur un ordinateur 64 bits) dans évalué forme et après la collecte des ordures, Foo 1 2
nécessite 24 octets sur son propre, y compris les dépendances, 40 octets au total:
Prélude de GHC.Données. datasize Test> let x = Foo 1 2
Prélude de GHC.Données. datasize Test> x
Foo {a = 1, b = 2}
Prélude de GHC.Données. Datasize Test> Système.Mem.performGC
Prélude de GHC.Données. datasize Test> closureSize x
24
Prélude de GHC.Données. datasize Test> recursiveSize x
40
Pour reproduire ce que vous devez charger les données de définition en forme compilée avec -O
, sinon, l' {-# UNPACK #-}
pragma n'a aucun effet.
Maintenant, laissez-nous créer un thunk et de voir que la taille va considérablement:
Prélude de GHC.Données. datasize Test> laissez thunk = 2 + 3::Int
Prélude de GHC.Données. datasize Test> let x = Foo 1 thunk
Prélude de GHC.Données. datasize Test> x `seq` return ()
Prélude de GHC.Données. Datasize Test> Système.Mem.performGC
Prélude de GHC.Données. datasize Test> closureSize x
24
Prélude de GHC.Données. datasize Test> recursiveSize x
400
Maintenant, c'est tout à fait excessive. La raison en est que ce calcul inclut des références à la statique des fermetures, Num
typeclass dictionnaires et autres, et en général la GHCi bytecode est très unoptimized. Donc, nous allons mettre cela dans une bonne Haskell programme. L'exécution de
main = do
l <- getArgs
let n = length l
n `seq` return ()
let thunk = trace "I am evaluated" $ n + n
let x = Foo 1 thunk
a x `seq` return ()
performGC
s1 <- closureSize x
s2 <- closureSize thunk
r <- recursiveSize x
print (s1, s2, r)
donne (24, 24, 48)
, alors maintenant, l' Foo
de la valeur est constituée d' Foo
lui-même et un thunk. Pourquoi seulement le thunk, ne devrait-il pas être aussi une référence à l' n
l'ajout d'un autre de 16 octets? Pour répondre à cela, nous avons besoin d'un meilleur outil:
Cette bibliothèque (par moi) peut enquêter sur le tas et vous dire précisément comment vos données y est représenté. Ainsi, l'ajout de cette ligne dans le fichier ci-dessus:
buildHeapTree 1000 (asBox x) >>= putStrLn . ppHeapTree
nous obtenons (quand on passe par les cinq paramètres du programme) le résultat Foo (_thunk 5) 1
. Notez que l'ordre des arguments est échangé sur le tas, parce que les pointeurs viennent toujours avant les données. La plaine 5
indique que la fermeture de la thunk stocke ses argument "unboxed".
En dernier exercice, nous avons le vérifier en faisant le thunk paresseux en n
: Maintenant
main = do
l <- getArgs
let n = length l
n `seq` return ()
let thunk = trace "I am evaluated" $ n
let x = Foo 1 thunk
a x `seq` return ()
performGC
s1 <- closureSize x
s2 <- closureSize thunk
s3 <- closureSize n
r <- recursiveSize x
buildHeapTree 1000 (asBox x) >>= putStrLn . ppHeapTree
print (s1, s2, s3, r)
donne un tas de représentation de l' Foo (_thunk (I# 4)) 1
avec une part de fermeture pour n
(comme indiqué par la présence de l' I#
constructeur) et montre les tailles attendues pour les valeurs et leur total, (24,24,16,64)
.
Oh, et si cela est encore trop haut niveau, getClosureRaw vous donne les premières octets.