2011-03-15 7 views
21

Tengo un proceso de servidor implementado en haskell que actúa como un simple db en memoria. Los procesos del cliente pueden conectarse y luego agregar y recuperar datos. El servicio usa más memoria de la que esperaría, y estoy intentando averiguar por qué.¿Cómo debo interpretar el resultado del generador de perfiles ghc?

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.

Ejecución del proceso de capturar estadísticas 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 en la adición de los elementos 60k veo los bytes en vivo crecen sin problemas a

... 
    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) 

Este parece estar diciéndome que el montón tiene ~ 63MB de datos en vivo. Esto bien podría ser consistente con los números de arriba, cuando agrega espacio de pila, espacio de código, sobrecarga de GC, etc.

Así que traté de usar el perfilador de montón para averiguar qué compone este 63MB. Los resultados son confusos Correr con "+ RTS -h", y buscando en el archivo de CV generada , el último y más grande de instantáneas 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. La adición de estos hasta da el uso de memoria pico como ~ 6MB, como se refleja en la salida gráfico:

enter image description here

Por qué esto es inconsistente con los bytes en directo como se muestra en las estadísticas de GC? Es difícil de ver cómo mis estructuras de datos podrían requerir 63MB, y el perfilador dice que no lo son. ¿A dónde va la memoria?

Gracias por cualquier sugerencia o sugerencia sobre esto.

Tim

+2

El enlace de la imagen imgur.com que ha incluido parece estar roto. – Heatsink

+0

Si incluye una imagen a través del enlace "imagen" de StackOverflow en la barra de herramientas (o escribe Ctrl-G), es menos probable que se rompa. – ShreevatsaR

Respuesta

1

que debe utilizar, por ejemplo, hp2ps para obtener una vista gráfica de lo que está pasando. Mirar el archivo en bruto de hp es difícil.

+2

Gracias - Usé hp2ps. Como usuarios nuevos de desbordamiento de pila, no se me permitió pegar la imagen resultante :-(. Pero las líneas pegadas arriba del archivo hp reflejan la vista gráfica mostrada por hp2s. – timbod

+0

@timbod: Ahora debería tener suficiente reputación para incluir el imagen, eso probablemente sería útil para hacer. –

1

No todo está incluido en el perfil por defecto, por ejemplo, hilos y pilas. Pruebe con +RTS -xT.

+0

He intentado con '+ RTS -h -S -xt'. Esto todavía me da resultados sustancialmente diferentes en el conteo de bytes activos (63MB) frente a los datos pico como se muestra en el perfil (aproximadamente 6 MB). Por lo tanto, todavía no sé cómo se usa la memoria. – timbod

3

Tengo una teoría. Mi teoría es que su programa está usando mucho como ByteStrings. Mi teoría es que debido a que el contenido principal de ByteStrings es malloc, no se muestran durante el perfilado. Por lo tanto, podría quedarse sin montón sin que aparezca el mayor contenido de su pila en el gráfico de perfiles.

Para empeorar las cosas, cuando toma las subcadenas de ByteStrings, retienen por defecto el puntero al bloque de memoria asignado originalmente. Por lo tanto, incluso si intenta almacenar solo un pequeño fragmento de alguno ByteString, podría terminar reteniendo todo el ByteString originalmente asignado y esto no aparecerá en su perfil de montón.

Esa es mi teoría de todos modos. No sé suficientes datos sobre cómo funciona el perfilador de montículos de GHC ni sobre cómo se implementan ByteStrings para saber con certeza.Tal vez alguien más pueda responder y confirmar o disputar mi teoría.

Edit2: tibbe observa que se ha anclado el almacenamiento intermedio utilizado por ByteString s. Entonces, si está asignando/liberando muchos pequeños Bytestring s, puede fragmentar su montón, lo que significa que se queda sin montón utilizable con la mitad de él sin asignar.

Editar: JaffaCake me dice que a veces el perfilador de montón no mostrará la memoria asignada por ByteStrings.

Cuestiones relacionadas