Huella de memoria de los tipos de datos Haskell
memory-management ghc (2)
¿Cómo puedo encontrar la cantidad real de memoria requerida para almacenar un valor de algún tipo de datos en Haskell (principalmente con GHC)? ¿Es posible evaluarlo en tiempo de ejecución (por ejemplo, en GHCi) o es posible estimar los requisitos de memoria de un tipo de datos compuesto a partir de sus componentes?
En general, si se conocen los requisitos de memoria de los tipos b
, ¿cuál es la sobrecarga de memoria de los tipos de datos algebraicos, tales como:
data Uno = Uno a
data Due = Due a b
Por ejemplo, ¿cuántos bytes en memoria ocupan estos valores?
1 :: Int8
1 :: Integer
2^100 :: Integer
/x -> x + 1
(1 :: Int8, 2 :: Int8)
[1] :: [Int8]
Just (1 :: Int8)
Nothing
Entiendo que la asignación real de memoria es mayor debido a la recolección diferida de basura. Puede ser significativamente diferente debido a la evaluación diferida (y el tamaño del procesador no está relacionado con el tamaño del valor). La pregunta es, dado un tipo de datos, ¿cuánta memoria tiene su valor cuando se evalúa por completo?
Encontré que hay una opción :set +s
en GHCi para ver las estadísticas de memoria, pero no está claro cómo estimar la huella de memoria de un solo valor.
(Lo siguiente se aplica a GHC, otros compiladores pueden usar diferentes convenciones de almacenamiento)
Regla de oro: un constructor cuesta una palabra para un encabezado y una palabra para cada campo . Excepción: un constructor sin campos (como Nothing
o True
) no ocupa espacio, porque GHC crea una instancia única de estos constructores y la comparte entre todos los usos.
Una palabra es 4 bytes en una máquina de 32 bits y 8 bytes en una máquina de 64 bits.
Por ejemplo,
data Uno = Uno a
data Due = Due a b
un Uno
toma 2 palabras, y un Due
toma 3.
El tipo Int
se define como
data Int = I# Int#
ahora, Int#
toma una palabra, por lo que Int
toma 2 en total. La mayoría de los tipos no compartidos toman una palabra, las excepciones son Int64#
, Word64#
y Double#
(en una máquina de 32 bits) que toman 2. GHC en realidad tiene una caché de pequeños valores de tipo Int
y Char
, por lo que en muchos casos no tomar espacio de montón en absoluto. Una String
solo requiere espacio para las celdas de la lista, a menos que use Char
s> 255.
Un Int8
tiene una representación idéntica a Int
. Integer
se define así:
data Integer
= S# Int# -- small integers
| J# Int# ByteArray# -- large integers
así que un Integer
pequeño ( S#
) toma 2 palabras, pero un entero grande toma una cantidad variable de espacio dependiendo de su valor. Un ByteArray#
toma 2 palabras (encabezado + tamaño) más espacio para la matriz en sí.
Tenga en cuenta que un constructor definido con newtype
es gratuito . newtype
es puramente una idea en tiempo de compilación, no ocupa espacio y no cuesta instrucciones en tiempo de ejecución.
Más detalles en The Layout of Heap Objects en el comentario de GHC .
El paquete ghc-datasize proporciona la función recursiveSize para calcular el tamaño de un objeto GHC. Sin embargo...
Se realiza una recolección de basura antes de que se calcule el tamaño, porque el recolector de basura dificultaría las caminatas en montón.
... ¡así que no sería práctico llamar esto a menudo!
Consulte también ¿Cómo averiguar las representaciones de memoria de GHC de los tipos de datos? y ¿cómo puedo determinar el tamaño de un tipo en Haskell? .