Posts etiquetados ‘paralelizacion’


You can read the English version of this post in http://phpsblog.agustinvillalba.com/parallelize-processes-php/

Hoy vamos a explicar cómo poder lanzar múltiples hilos en nuestros scripts PHP, pudiendo así paralelizar aquellos procesos que tengan una gran carga de procesador o que simplemente puedan lanzarse en paralelo dado que no tienen dependencias entre ellos y todos resuelven de forma parcial un problema común.

¿Por qué paralelizar procesos?

Cuando se programa un algoritmo, según el lenguaje en el que lo hagamos, podemos resolver las partes del algoritmo de forma secuencial o paralela. Si tenemos una parte del algoritmo que tiene una gran carga y requiere mucho tiempo para su resolución, podríamos ir resolviendo las demás partes mientras se resuelve la parte más lenta. De forma que el tiempo total de ejecución de nuestro algoritmo pasa de ser la suma de las partes a solamente la parte más lenta. Es cierto que la paralelización realmente se aprovecha cuando disponemos de recursos redundados, por ejemplo procesadores, pero en la gran mayoría de los servidores actuales disponemos de varios núcleos, por lo que tiene bastante utilidad aprender a paralelizar nuestros procesos.

La base teórica de la paralelización nos dice que cuando proceso padre lanza un proceso hijo, se crea en memoria un proceso idéntico al padre, con un identificador de proceso (pid) propio (diferente al del padre) y ejecutándose a partir de la instrucción siguiente a la que creó al propio proceso hijo.

Lanzando multiples hilos en PHP

Antes que nada hemos de tener en cuenta que para poder paralelizar procesos en PHP necesitamos tener instalada la extensión de control de procesos (Process Control Extension [http://www.php.net/manual/en/refs.fileprocess.process.php] en inglés). Dentro de esta extensión, los módulos que nos permitirán paralelizar nuestros procesos son el propio de Control de Procesos (PHP PCNTL [http://www.php.net/manual/en/book.pcntl.php] en inglés) y el de memoria compartida entre procesos (PHP Shared Memory [http://www.php.net/manual/en/book.shmop.php] en inglés). Para poder explicar la paralelización de procesos vamos a poner un ejemplo (bastante simple) en el que un proceso (padre) lanzará 10 hilos en paralelo (hijos) los cuales generarán un número aleatorio, colocarán en una zona de memoria compartida con su padre, éste recogerá cada número generado por sus hijos y devolverá la suma de todos ellos.

Veamos el código:

<?php
function multiple_forks()
{
    $array_pids = array();
    $sumatorio = 0;
    //Almacenamos el process id del proceso padre
    $parent_pid = getmypid();
    for($i=0;$i<10;$i++)
    {
        if(getmypid() == $parent_pid)
        {//Estamos en el proceso padre, asi que lanzamos el proceso hijo y guardamos si pid
            $array_pids[] = pcntl_fork(); //pcntl_fork nos permite lanzar un proceso hijo en paralelo
        }
    }

    //Una vez hemos lanzado los 10 hijos pasamos a generar los números aleatorios (en los hijos)
    //o ir sumandolos si estamos en el padre
    if(getmypid() == $parent_pid)
    {//Estamos en el proceso padre
        while(count($array_pids) > 0)
        {//Mientras queden hijos en ejecución, quedamos a la espera de que terminen
            $pid = pcntl_waitpid(-1,$status);
            //Abrimos la memoria compartida con nuestro hijo $pid
            $shared_id = shmop_open($pid,"a",0,0);
            $share_data = shmop_read($shared_id,0,shmop_size($shared_id));
            $sumatorio += $share_data;
            //Marcamos el bloque para que sea eliminado y lo cerramos
            shmop_delete($shared_id);
            shmop_close($shared_id);
            //Eliminamos el proceso de la cola de hijos en ejecucion
            foreach($array_pids as $key => $hijo)
            {
                if($pid == $hijo) unset($array_pids[$key]);
            }
        }
    }
    else
    {//Estamos en el hijo
        $num = rand(0,100);
        $shared_id = shmop_open(getmypid(),"c",0644,strlen($num));
        if(!$shared_id)
        {//No se pudo crear la memoria compartida
            echo "Error al crear la memoria compartida en el hijo ".getmypid()."\n";
        }
        else
        {
            if(strlen($num) != shmop_write($shared_id,$num,0))
            {
                echo "Error al intentar escribir el numero $num en el hijo ".getmypid()."\n";
            }
            else
            {
                shmop_close($shared_id);
            }
         }
         //Salimos indicando al padre que todo ha ido bien
         exit(0);
    }
    return $sumatorio;
}
?>

Muy probablemente el código se pueda optimizar aun más, pero no es el objetivo de este artículo. Veamos las funciones de control de procesos y memoria compartida que hemos utilizado en el código:

  • pcntl_fork. Nos permite lanzar hijos de un proceso padre, devolviendo el pid del hijo lanzado. (pcntl_fork en PHP)
  • pcntl_waitpid. Nos permite poner al padre en espera de que un hijo suyo termine su ejecución. El parámetro -1 indica que queda a la espera de que algún hijo termine, el primero que lo haga. (pcntl_waitpid en PHP).
  • shmop_open. Nos permite crear o abrir un bloque de memoria. El primer parámetro es a modo de identificador, nada mejor que usar el pid del hijo como identificador, así el padre podrá conocer el identificador con el que se creó dicho bloque de memoria y acceder a los datos compartidos. (shmop_open en PHP)
  • shmop_read. Nos permite leer un bloque de memoria compartida. (shmop_read en PHP)
  • shmop_delete. Marca el bloque de memoria para ser liberado. El bloque será liberado automáticamente por el sistema cuando todos los procesos concurrentes asociados con el bloque se desvinculen del bloque. (shmop_delete en PHP)
  • shmop_close. Cierra un bloque de memoria, indicandole al sistema que el proceso se desvincula del bloque. (shmop_close en PHP)
  • shmop_size. Nos permite conocer el tamaño que ocupa un bloque de memoria compartida. (shmop_size en PHP)
  • shmop_write. Nos permite escribir datos en un bloque de memoria compartida. (shmop_write en PHP)

Con esto tenemos la base necesaria para poder crear procesos paralelos en nuestros controladores y lanzar así tantos hilos como procesadores dispongamos en nuestro servidor.

You can read the English version of this post in http://phpsblog.agustinvillalba.com/parallelize-processes-php/