Página 1 de 1

Uso de memoria excesivo con FORK

NotaPublicado: 2016-10-22 05:38 @277
por zolidus
Hola a todos.

Tengo algunas dudas sobre cómo ejecutar tareas en paralelo con Perl. Comúnmente realizo lo anterior empleando las herramientas que proporciona el módulo Parallel::ForkManager. Sin embargo, he observado que cuando se crean los procesos hijos hay un consumo excesivo de memoria.

He aquí un ejemplo de la parte de un programa que utilizo para ejecutar subprocesos:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.          my $fork= new Parallel::ForkManager($ncpus);
  2.          foreach (0..$#proc) {
  3.                  $fork->start and next;
  4.                  my ($index1, $index2) = split /:/, $proc[$_];
  5.                  &grho($core,$total,$index1,$index2,$files[$_],\@vector,\@numbers);
  6.                  $fork->finish;
  7.          }
  8.          $fork->wait_all_children;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

En este caso particular la subrutina "grho" genera el archivo "$files[$_]", y calcula una serie de elementos a partir de la información presente en @vector y @numbers. Estos arrays tienen aproximadamente 10^7 elementos.

El programa en cuestión realiza más operaciones (lo anterior es solo una parte), y genera varias estructuras de datos de tamaño considerable (5 GB).

Según entiendo del uso Parallel::ForkManager, cada vez que se ejecuta un proceso hijo se copian todas las variables y la información del proceso padre, lo cual es la causa del uso excesivo de la memoria.

Mi pregunta es: ¿existe alguna otra manera más eficiente de correr subprocesos en perl?

Re: Uso de memoria excesivo con FORK

NotaPublicado: 2016-10-22 10:22 @473
por explorer
Si quieres ejecutar varios subprocesos, entonces ya sabes que la memoria del ordenador se repartirá entre ellos. Mejor dicho, los procesos competirán por reservar la memoria que necesitan.

Los procesos son independientes, por lo que sí, se crean copias de todas las variables. Si solo tenemos a @vector y @numbers como estructuras que ocupan mucho, no creo que pase nada porque existan tantos procesos como $ncpus indiques. El problema está en la información que genera cada proceso. Si genera 5 GB, entonces es mucha información como para mantenerla en memoria. Si es una estructura de datos que simplemente generamos y ya está, pues la podemos ir guardando a disco, a medida de que se crea, y entonces no ocupa memoria. El problema es si la necesitamos para más operaciones. Si multiplicamos 5 GB por cada proceso, pues enseguida te quedas sin memoria.

Una solución para no ocupar memoria, y seguir accediendo a grandes estructuras, es usando el espacio de almacenamiento permanente (o sea, el disco duro).

La ida es guardar a disco las estructuras más grandes, de tal manera que nos permitan, al mismo tiempo, trabajar con ellas y tener espacio en RAM para arrancar más procesos.

Una buena solución para acceder a archivos grandes como si fueran datos en RAM, es usando la facilidad mmap del sistema operativo. Perl tiene varios módulos para hacer que, por ejemplo, una variable, de forma transparente, nos dé la información como si estuviéramos accediendo a RAM: https://metacpan.org/search?size=500&q= ... pe=modules

Si estás trabajando con Perl v5.24, ya trae integrado la capa mmap para PerlIO, por lo que podemos acceder a los archivos sin importar lo grandes que sean: https://metacpan.org/pod/PerlIO::mmap

Otra opción, que podría resolver el tema, sería pasar de arrancar subprocesos con fork a usar hilos (threads). En este caso, es recomendable usar la última versión estable de Perl (v5.24.0. v5.24.1 sale la semana que viene), y leerse el documento perlthrtut: https://metacpan.org/pod/distribution/p ... thrtut.pod En él también se comenta cómo compartir variables entre los hilos.