haskell - metatags - ¿Cómo debo interpretar la salida del generador de perfiles de ghc?
meta tags ejemplos (3)
Debe usar, por ejemplo, hp2ps para obtener una vista gráfica de lo que está sucediendo. Mirar el archivo hp en bruto es difícil.
Tengo un proceso de servidor implementado en haskell que actúa como un db en memoria simple. Los procesos del cliente pueden conectarse y luego agregar y recuperar datos. El servicio utiliza más memoria de la que esperaba, y estoy intentando averiguar por qué.
La métrica más cruda que tengo es linux "top". Cuando comienzo el proceso, veo un tamaño de imagen "VIRT" de ~ 27MB. Después de ejecutar un cliente para insertar 60,000 elementos de datos, veo un tamaño de imagen de ~ 124MB.
Ejecutando el proceso para capturar estadísticas de GC (+ RTS -S), veo inicialmente
Alloc Copied Live GC GC TOT TOT Page Flts
bytes bytes bytes user elap user elap
28296 8388 9172 0.00 0.00 0.00 0.32 0 0 (Gen: 1)
y al agregar los elementos de 60k veo que los bytes en vivo crecen sin problemas para
...
532940 14964 63672180 0.00 0.00 23.50 31.95 0 0 (Gen: 0)
532316 7704 63668672 0.00 0.00 23.50 31.95 0 0 (Gen: 0)
530512 9648 63677028 0.00 0.00 23.50 31.95 0 0 (Gen: 0)
531936 10796 63686488 0.00 0.00 23.51 31.96 0 0 (Gen: 0)
423260 10047016 63680532 0.03 0.03 23.53 31.99 0 0 (Gen: 1)
531864 6996 63693396 0.00 0.00 23.55 32.01 0 0 (Gen: 0)
531852 9160 63703536 0.00 0.00 23.55 32.01 0 0 (Gen: 0)
531888 9572 63711876 0.00 0.00 23.55 32.01 0 0 (Gen: 0)
531928 9716 63720128 0.00 0.00 23.55 32.01 0 0 (Gen: 0)
531856 9640 63728052 0.00 0.00 23.55 32.02 0 0 (Gen: 0)
529632 9280 63735824 0.00 0.00 23.56 32.02 0 0 (Gen: 0)
527948 8304 63742524 0.00 0.00 23.56 32.02 0 0 (Gen: 0)
528248 7152 63749180 0.00 0.00 23.56 32.02 0 0 (Gen: 0)
528240 6384 63756176 0.00 0.00 23.56 32.02 0 0 (Gen: 0)
341100 10050336 63731152 0.03 0.03 23.58 32.35 0 0 (Gen: 1)
5080 10049728 63705868 0.03 0.03 23.61 32.70 0 0 (Gen: 1)
Esto parece decirme que el montón tiene ~ 63MB de datos en vivo. Esto podría ser coherente con los números de arriba, para el momento en que agregue espacio de pila, espacio de código, sobrecarga de GC, etc.
Así que intenté usar el generador de perfiles del montón para averiguar qué conforma este 63MB. Los resultados son confusos. Ejecutando con "+ RTS -h", y mirando el archivo hp generado, la última y más grande instantánea tiene:
containers-0.3.0.0:Data.Map.Bin 1820400
bytestring-0.9.1.7:Data.ByteString.Internal.PS 1336160
main:KV.Store.Memory.KeyTree 831972
main:KV.Types.KF_1 750328
base:GHC.ForeignPtr.PlainPtr 534464
base:Data.Maybe.Just 494832
THUNK 587140
Todos los otros números en la instantánea son mucho más pequeños que esto. Al agregar estos, el uso máximo de memoria es de ~ 6MB, como se refleja en la salida del gráfico:
¿Por qué esto es inconsistente con los bytes en vivo como se muestra en las estadísticas de GC? Es difícil ver cómo mis estructuras de datos podrían requerir 63 MB, y el generador de perfiles dice que no lo son. ¿A dónde va la memoria?
Gracias por cualquier consejo o consejos sobre esto.
Tim
No todo está incluido en el perfil por defecto, por ejemplo, hilos y pilas. Pruebe con +RTS -xT
.
Tengo una teoría. Mi teoría es que su programa está utilizando mucho como ByteStrings
. Mi teoría es que debido a que el contenido principal de ByteStrings
es ByteStrings
, no se muestran al perfilar. Por lo tanto, podría quedarse sin almacenamiento sin que el contenido más grande de su montón aparezca en el gráfico de perfiles.
Para empeorar las cosas, cuando toma subcadenas de ByteStrings
, retienen el puntero al bloque de memoria asignado originalmente. Por lo tanto, incluso si intenta almacenar solo una pequeña parte de algunas ByteString
, podría terminar reteniendo la totalidad de las ByteString
asignadas originalmente y esto no aparecerá en su perfil de pila.
Esa es mi teoría de todos modos. No conozco suficientes datos sobre cómo funciona el generador de perfiles de GHC ni sobre cómo se implementan los ByteStrings
para saberlo con certeza. Tal vez alguien más pueda intervenir y confirmar o disputar mi teoría.
Edit2: tibbe observa que el búfer utilizado por ByteString
s está ByteString
. Por lo tanto, si está asignando / liberando muchos Bytestring
pequeños, puede fragmentar su montón, lo que significa que se queda sin un montón utilizable con la mitad sin asignar.
Edición: JaffaCake me dice que a veces el generador de perfiles del montón no muestra la memoria asignada por ByteStrings.