Consejos para mantener bajo el uso de la memoria Perl
memory (6)
¿Qué tipo de problema te encuentras y qué significa "grande" para ti? Tengo amigos que necesitan para cargar archivos de 200 Gb en la memoria, por lo que su idea de buenos consejos es muy diferente a la del comprador de presupuesto para las rebanadas mínimas de VM que sufren 250 Mb de RAM (¿de verdad? Mi teléfono tiene más que eso).
En general, Perl se aferra a cualquier memoria que use, incluso si no la está usando. Tenga en cuenta que la optimización en una dirección, por ejemplo, la memoria, podría afectar negativamente a otra, como la velocidad.
Esta no es una lista completa (y hay más en Programming Perl ):
☹ Use las herramientas de perfilado de memoria de Perl para ayudarlo a encontrar áreas problemáticas. Consulte Perfilado del uso de la memoria del montón en programas perl y ¿Cómo encontrar la cantidad de memoria física ocupada por un hash en Perl?
☹ Use variables léxicas con el alcance más pequeño posible para permitir que Perl reutilice esa memoria cuando no la necesite.
☹ Evita crear grandes estructuras temporales. Por ejemplo, leer un archivo con un foreach
lee toda la entrada a la vez. Si solo lo necesita línea por línea, use while
.
foreach ( <FILE> ) { ... } # list context, all at once
while( <FILE> ) { ... } # scalar context, line by line
☹ Tal vez ni siquiera necesite tener el archivo en la memoria. Archivos de mapas de memoria en lugar de sorberlos
☹ Si necesita crear estructuras de big data, considere algo como DBM::Deep u otros motores de almacenamiento para mantener la mayor parte de ella fuera de la RAM y en el disco hasta que lo necesite.
☹ No permita que las personas usen su programa. Cada vez que lo he hecho, he reducido la huella de memoria en aproximadamente un 100%. También reduce las solicitudes de soporte.
☹ Pase grandes trozos de texto y grandes agregados por referencia para que no haga una copia, almacenando la misma información dos veces. Si tiene que copiarlo porque quiere cambiar algo, es posible que esté atascado. Esto se aplica en ambos sentidos como argumentos de subrutina y valores de retorno de subrutina:
call_some_sub( /$big_text, /@long_array );
sub call_some_sub {
my( $text_ref, $array_ref ) = @_;
...
return /%hash;
}
☹ Rastrea fugas de memoria en los módulos. Tuve grandes problemas con una aplicación hasta que me di cuenta de que un módulo no estaba liberando memoria . Encontré un parche en la cola RT del módulo, lo apliqué y resolví el problema.
☹ Si necesita manejar una gran cantidad de datos una vez pero no desea la huella de memoria persistente, descargue el trabajo a un proceso secundario. El proceso secundario solo tiene la huella de memoria mientras está funcionando. Cuando obtiene la respuesta, el proceso secundario se apaga y lo libera de memoria. Del mismo modo, los sistemas de distribución de trabajo, como Gearman , pueden diseminar el trabajo entre las máquinas.
☹ Convierta las soluciones recursivas en iterativas. Perl no tiene optimización de recursividad de cola, por lo que cada llamada nueva se agrega a la pila de llamadas. Puede optimizar el problema de la cola usted mismo con trucos con goto o un módulo, pero eso es mucho trabajo para aferrarse a una técnica que probablemente no necesite.
☹ ¿Usó 6 Gb o solo cinco? Bueno, para decirte la verdad, con toda esta emoción, perdí el rastro. Pero siendo este es Perl, el idioma más poderoso del mundo, y te limpiaría la memoria, tienes que hacerte una pregunta: ¿me siento afortunado? Bueno, ¿verdad, punk?
Hay muchos más, pero es muy temprano en la mañana para descubrir cuáles son. Cubro algunos en Mastering Perl y Effective Perl Programming .
¿Cuáles son algunos buenos consejos para mantener bajo el uso de memoria en una secuencia de comandos de Perl? Estoy interesado en aprender cómo mantener la huella de mi memoria lo más baja posible para los sistemas que dependen de los programas de Perl. Sé que Perl no es muy bueno en lo que respecta al uso de la memoria, pero me gustaría saber si hay algún consejo para mejorarlo.
Entonces, ¿qué puedes hacer para mantener una secuencia de comandos Perl usando menos memoria? Estoy interesado en cualquier sugerencia, ya sean sugerencias reales para escribir código o consejos sobre cómo compilar Perl de manera diferente.
Editar para Bounty: tengo un programa perl que sirve como servidor para una aplicación de red. Cada cliente que se conecta recibe su propio proceso hijo actualmente. También he usado hilos en lugar de horquillas, pero no he podido determinar si usar hilos en vez de horquillas es realmente más eficiente en cuanto a la memoria.
Me gustaría intentar usar hilos en lugar de horquillas de nuevo. Creo que en teoría debería ahorrar en el uso de la memoria. Tengo algunas preguntas al respecto:
- ¿Los hilos creados en Perl evitan la copia de bibliotecas de módulos Perl en la memoria de cada hilo?
- ¿Los threads (usar subprocesos) son la forma más eficiente (o la única) de crear subprocesos en Perl?
- En los hilos, puedo especificar un paramater stack_size, ¿qué debería considerar específicamente al especificar este valor y cómo afecta el uso de la memoria?
Con subprocesos en Perl / Linux, ¿cuál es el método más confiable para determinar el uso real de la memoria por subproceso?
Además de las sugerencias de Brian Foy, descubrí que los siguientes también me ayudaron MUCHO.
- Donde sea posible, no "use" módulos externos, no sabe cuánta memoria utilizan. Encontré reemplazando los módulos LWP y HTTP :: Request :: Common con el uso de memoria recortada Curl o Lynx a la mitad.
- Cortarlo de nuevo modificando nuestros propios módulos e incorporando solo las subrutinas requeridas usando "requerir" en lugar de una biblioteca completa de subs innecesarios.
Brian menciona el uso de variables léxicas con el alcance más pequeño posible. Si está bifurcando, usar "undef" también ayuda al liberar inmediatamente la memoria para que Perl la vuelva a usar. Así que declaras un escalar, matriz, hash o incluso sub, y cuando hayas terminado con alguno de ellos, utiliza:
my (@divs) = localtime (tiempo); $ VAR {minute} = $ divs [1];
undef @divs; undef @array; undef $ escalar; undef% hash; undef y sub;
Y no use ninguna variable innecesaria para hacer que su código sea más pequeño. Es mejor codificar todo lo que sea posible para reducir el uso del espacio de nombres.
Luego, hay muchos otros trucos que puedes probar dependiendo de la funcionalidad de tu aplicación. La nuestra fue ejecutada por cron, cada minuto. Descubrimos que podíamos bifurcar la mitad de los procesos con un sueño (30) para que la mitad corriera y completara dentro de los primeros 30 segundos, liberando la CPU y la memoria, y la otra mitad funcionaría después de un retraso de 30 segundos. Redujo a la mitad el uso de recursos. En general, logramos reducir el uso de RAM de más de 2 GB a 200 MB, un 90% de ahorro.
Nos las arreglamos para tener una idea bastante buena del uso de memoria con
top -M
ya que nuestra secuencia de comandos se ejecutó en un servidor relativamente estable con un solo sitio. Así que ver "carnero libre" nos dio una muy buena indicación del uso de memery.
También "ps" grepping para su secuencia de comandos y si bifurcación, la clasificación por memoria o uso de la CPU fue de gran ayuda.
ps -e -o pid,pcpu,pmem,stime,etime,command --sort=+cpu | grep scriptname | grep -v grep
Ambos hilos y horquillas se CoW (Copia en Escribir) páginas de memoria. Con los hilos puedes definir variables compartidas, pero por defecto copiarás tus variables por hilo. En ambos casos, puede esperar un mayor uso de memoria.
No sé exactamente qué tipo de aplicación está tratando, pero es posible que desee considerar escribir su aplicación utilizando el modelo impulsado por eventos en lugar de los procesos padre / hijo. Te recomiendo que le AnyEvent un vistazo a AnyEvent , es bastante simple y dado que la aplicación se convierte en un solo subprocesamiento (o proceso), guardarás algo de memoria (y aún más rápido en algunos casos). La gente incluso ha escrito servidores web con AnyEvent con muy buen rendimiento y casi no se nota que tiene un solo hilo. Eche un vistazo, por ejemplo, a Twiggy
Intenta usar más almacenamiento en caché. La lógica para implementar la rutina de almacenamiento en caché siempre es la misma, por lo que puede automatizar el uso del módulo Memoize CPAN. Use Devel::Size para verificar la huella de memoria real.
Mis dos monedas de diez centavos.
¿Los hilos creados en Perl evitan la copia de bibliotecas de módulos Perl en la memoria de cada hilo?
- No, es solo un proceso, lo que no se repite en la pila de programas, cada hilo debe tener el suyo.
¿Los subprocesos (usar subprocesos) son la forma más eficiente (o la única) de crear subprocesos en Perl?
- IMO Cualquier método eventualmente llama a la API pthread que realmente hace el trabajo.
En los hilos, puedo especificar un paramater stack_size, ¿qué debería considerar específicamente al especificar este valor y cómo afecta el uso de la memoria?
- Como los hilos se ejecutan en el mismo espacio de proceso, la pila no se puede compartir. El tamaño de la pila le dice a los pthreads qué tan lejos deben estar el uno del otro. Cada vez que se llama a una función, las variables locales se asignan en la pila. Así que el tamaño de la pila limita qué tan profundo puede recurse. puede asignar tan poco como sea posible en la medida en que su aplicación aún funcione.
Con subprocesos en Perl / Linux, ¿cuál es el método más confiable para determinar el uso real de la memoria por subproceso?
* Stack storage is fixed after your thread is spawned, heap and static storage is shared and
they can be used by any thread so this notion of memory usage per-thread doesn''t really
apply. It is per process.
Comparing fork and thread:
* fork duplicate the process and inherites the file handles
advantages: simpler application logic, more fault tolerant.
the spawn process can become faulty and leaking resource
but it will not bring down the parent. good solution if
you do not fork a lot and the forked process eventually
exits and cleaned up by the system.
disadvantages: more overhead per fork, system limitation on the number
of processes you can fork. You program cannot share variables.
* threads runs in the same process with addtional program stacks.
advantages: lower memory footprint, thread spawn if faster and ligther
than fork. You can share variables.
disadvantages: more complex application logic, serialization of resources etc.
need to have very reliable code and need to pay attention to
resource leaks which can bring down the entire application.
IMO, depends on what you do, fork can use way less memory over the life time of the
application run if whatever you spawn just do the work independently and exit, instead of
risking memory leaks in threads.
Si está realmente desesperado, podría intentar montar algo de memoria como un sistema de archivos ( tmpfs / ramdisk) y leer / escribir / borrar archivos en él. Supongo que la implementación de tmpfs es lo suficientemente inteligente como para liberar la memoria cuando eliminas un archivo.
También podría mmap (ver File :: Map , Sys :: Mmap ) un archivo enorme en los tmpfs, una idea que obtuve de Cache :: FastMmap .
Nunca lo intenté, pero debería funcionar :)