Actualmente estoy experimentando con un pequeño servidor web Haskell escrito en Snap que carga y pone a disposición del cliente una gran cantidad de datos. Y me cuesta muchísimo ganar el control del proceso del servidor. En momentos aleatorios, el proceso usa una gran cantidad de CPU por segundos o minutos y deja de responder a las solicitudes de los clientes. A veces, el uso de la memoria aumenta (y en ocasiones disminuye) cientos de megabytes en segundos.¿Cómo ganar el control de un montón de 5 GB en Haskell?
Esperemos que alguien tenga más experiencia con procesos Haskell de larga ejecución que usan mucha memoria y pueden darme algunos consejos para hacer que la cosa sea más estable. Lo he estado depurando durante días y ahora estoy empezando a desesperarme un poco.
Un pequeño resumen de mi configuración:
En el inicio del servidor leí unos 5 gigabytes de datos en un gran (anidada) Estructura Data.Map-por igual en la memoria. El mapa anidado tiene un valor estricto y todos los valores dentro del mapa son de tipos de datos con todo su campo hecho estricto también. He dedicado mucho tiempo para asegurarme de que no quedan nada sin evaluar. La importación (dependiendo de la carga de mi sistema) toma alrededor de 5-30 minutos. Lo extraño es que la fluctuación en carreras consecutivas es mucho más grande de lo que esperaría, pero ese es un problema diferente.
La estructura de big data vive dentro de un 'TVar' compartido por todos los hilos del cliente generados por el servidor Snap. Los clientes pueden solicitar partes arbitrarias de los datos usando un lenguaje de consulta pequeño. La cantidad de solicitud de datos generalmente es pequeña (hasta 300 kb o menos) y solo toca una pequeña parte de la estructura de datos. Todas las solicitudes de solo lectura se realizan con un 'readTVARIO', por lo que no requieren ninguna transacción de STM.
El servidor se inicia con los siguientes indicadores: + RTS -N -I0 -qg -qb. Esto inicia el servidor en modo de subprocesos múltiples, deshabilita el tiempo de inactividad y el GC paralelo. Esto parece acelerar mucho el proceso.
El servidor se ejecuta principalmente sin ningún problema. Sin embargo, de vez en cuando, un cliente solicita un tiempo de espera y la CPU alcanza el 100% (o incluso más del 100%) y continúa haciéndolo durante un largo tiempo. Mientras tanto, el servidor ya no responde a la solicitud.
Hay pocas razones que se me ocurre que podría causar que el uso de la CPU:
La solicitud sólo se necesita mucho tiempo porque hay mucho trabajo por hacer. Esto es algo improbable porque a veces sucede para solicitudes que han demostrado ser muy rápidas en las ejecuciones anteriores (con una velocidad de 20-80 ms o más).
Todavía hay algunos trozos no evaluados que deben computarse antes de que los datos puedan procesarse y enviarse al cliente. Esto también es poco probable, con la misma razón que el punto anterior.
De alguna manera la recolección de basura se inicia y comienza a escanear todo mi montón de 5GB. Me puedo imaginar que esto puede tomar mucho tiempo.
El problema es que no tengo ni idea de cómo averiguar qué está pasando exactamente y qué hacer con esto. Debido a que el proceso de importación lleva tanto tiempo, los resultados de los perfiles no me muestran nada útil. Parece que no hay forma de activar y desactivar condicionalmente el generador de perfiles dentro del código.
Yo personalmente sospecho que el GC es el problema aquí.Estoy usando GHC7, que parece tener muchas opciones para modificar cómo funciona GC.
¿Qué configuraciones de GC se recomiendan cuando se utilizan grandes pilas con datos generalmente muy estables?
Hmmm .. interesante ... la cantidad de RAM que tiene la caja en la que está ejecutando esta aplicación de servidor – Ankur
Hay un total de 8GB de RAM en mi máquina. Eso debería ser suficiente. –
Sí ... parece ser suficiente para evitar fallas en la página – Ankur