Pregunta

EDITAR: he etiquetado este C con la esperanza de obtener más respuesta. Es más la teoría que me interesa que una implementación de lenguaje específico. Entonces, si es un codificador en C, trate el siguiente PHP como pseudocódigo y no dude en responder con una respuesta escrita en C.

Estoy tratando de acelerar un script PHP CLI haciendo que ejecute sus tareas en paralelo en lugar de en serie. Las tareas son completamente independientes entre sí, por lo que no importa en qué orden comienzan / terminan.

Aquí está la secuencia de comandos original (tenga en cuenta que todos estos ejemplos se eliminan para mayor claridad):

<?php

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

foreach ($items as $item) {
    do_stuff_with($item);
}

He logrado que funcione en los $ items en paralelo con pcntl_fork () como se muestra a continuación:

<?php

ini_set('max_execution_time', 0); 
ini_set('max_input_time', 0); 
set_time_limit(0);

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

$pids = array();
foreach ($items as $item) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die("couldn't fork()");
    } elseif ($pid > 0) {
        // parent
        $pids[] = $pid;
    } else {
        // child
        do_stuff_with($item);
        exit(0);
    }   
}

foreach ($pids as $pid) {
    pcntl_waitpid($pid, $status);
}

Ahora quiero extender esto para que haya un máximo de, digamos, 10 niños activos a la vez. ¿Cuál es la mejor manera de manejar esto? He intentado algunas cosas pero no he tenido mucha suerte.

¿Fue útil?

Solución

No hay syscall para obtener una lista de pids secundarios, pero ps puede hacerlo por usted.

El conmutador

--ppid enumerará todos los elementos secundarios para el proceso, por lo que solo necesita contar el número de líneas generadas por ps .

Alternativamente, puede mantener su propio contador que incrementará en fork () y disminuirá en la señal SIGCHLD , suponiendo que ppid permanezca sin cambios durante horquillado procesado.

Otros consejos

Lo mejor que se me ocurre es agregar todas las tareas a una cola, iniciar el número máximo de hilos que desee, y luego hacer que cada hilo solicite una tarea de la cola, ejecute la tarea y solicite la siguiente. . No olvide que los hilos terminen cuando no haya más tareas que hacer.

La bifurcación es una operación costosa. Por lo que parece, lo que realmente desea es múltiples subprocesos , no multi procesamiento . La diferencia es que los hilos son mucho más livianos que los procesos, ya que los hilos comparten un espacio de direcciones virtuales, pero los procesos tienen espacios de direcciones virtuales separados.

No soy un desarrollador de PHP, pero una búsqueda rápida en Google revela que PHP no admite múltiples subprocesos de forma nativa, pero hay bibliotecas para hacer el trabajo.

De todos modos, una vez que descubras cómo generar hilos, debes averiguar cuántos hilos generar. Para hacer esto, necesita saber cuál es el cuello de botella de su aplicación. ¿El cuello de botella es CPU, memoria o E / S? Has indicado en tus comentarios que estás vinculado a la red y que la red es un tipo de E / S.

Si estuvieras vinculado a la CPU, solo obtendrás tanto paralelismo como los núcleos de la CPU; más hilos y solo estás perdiendo el tiempo haciendo cambios de contexto. Suponiendo que pueda calcular cuántos hilos totales generar, debe dividir su trabajo en esa cantidad de unidades y hacer que cada hilo procese una unidad de forma independiente.

Si estuviera vinculado a la memoria, entonces el subprocesamiento múltiple no ayudaría.

Dado que estás vinculado a E / S, descubrir cuántos hilos generar es un poco más complicado. Si todos los elementos de trabajo tardan aproximadamente el mismo tiempo en procesarse con una varianza muy baja, puede estimar cuántos subprocesos se generan midiendo cuánto tarda un elemento de trabajo. Sin embargo, dado que los paquetes de red tienden a tener latencias muy variables, es poco probable que sea así.

Una opción es usar grupos de subprocesos: crea un grupo completo de subprocesos y luego, para que cada elemento que se procese, vea si hay un subproceso libre en el grupo. Si es así, tiene ese hilo que realiza el trabajo y pasa al siguiente elemento. De lo contrario, espera a que un hilo esté disponible. Elegir el tamaño del grupo de subprocesos es importante: demasiado grande y está perdiendo el tiempo haciendo cambios de contexto innecesarios. Muy pocos, y estás esperando hilos con demasiada frecuencia.

Otra opción más es abandonar el subprocesamiento múltiple / multiprocesamiento y simplemente hacer E / S asíncrona en su lugar. Como mencionó que está trabajando en un procesador de un solo núcleo, esta será probablemente la opción más rápida. Puede usar funciones como socket_select () para probar si un socket tiene datos disponibles. Si lo hace, puede leer los datos; de lo contrario, pasará a un socket diferente. Esto requiere hacer mucha más contabilidad, pero evita esperar a que entren datos en un socket cuando los datos están disponibles en un socket diferente.

Si desea evitar subprocesos y E / S asíncronas y seguir con el multiprocesamiento, aún puede valer la pena si el procesamiento por artículo es lo suficientemente costoso. Entonces puede hacer la división de trabajo de esta manera:

$my_process_index = 0;
$pids = array();

// Fork off $max_procs processes
for($i = 0; $i < $max_procs - 1; $i++)
{
  $pid = pcntl_fork();
  if($pid == -1)
  {
    die("couldn't fork()");
  }
  elseif($pid > 0)
  {
    // parent
    $my_process_index++;
    $pids[] = $pid
  }
  else
  {
    // child
    break;
  }
}

// $my_process_index is now an integer in the range [0, $max_procs), unique among all the processes
// Each process will now process 1/$max_procs of the items
for($i = $my_process_index; $i < length($items); $i += $max_procs)
{
  do_stuff_with($items[$i]);
}

if($my_process_index != 0)
{
  exit(0);
}

hombre 2 setrlimit

Eso será por usuario, lo que puede ser lo que quieras de todos modos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top