Question

EDIT: J'ai marqué ce C dans l'espoir d'obtenir plus de réponse. C'est plus la théorie qui m'intéresse qu'une implémentation de langage spécifique. Donc, si vous êtes un codeur C, veuillez traiter le code PHP suivant comme un pseudo-code et n'hésitez pas à répondre avec une réponse écrite en C.

J'essaie d'accélérer un script PHP en lui faisant exécuter ses tâches en parallèle plutôt qu'en série. Les tâches sont complètement indépendantes les unes des autres, donc l'ordre dans lequel elles commencent / finissent n'a pas d'importance.

Voici le script original (notez que tous ces exemples ont été supprimés pour plus de clarté):

<?php

$items = range(0, 100);

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

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

J'ai réussi à le faire fonctionner sur les $ items en parallèle avec pcntl_fork () comme indiqué ci-dessous:

<?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);
}

Maintenant, je souhaite étendre cette procédure à un maximum de 10 enfants actifs en même temps. Quelle est la meilleure façon de gérer cela? J'ai essayé plusieurs choses mais je n'ai pas eu beaucoup de chance.

Était-ce utile?

La solution

Il n’existe aucun appel système pour obtenir une liste des pid enfants, mais ps peut le faire pour vous.

Le commutateur

- ppid listera tous les enfants à traiter. Il vous suffit donc de compter le nombre de lignes générées par ps .

Vous pouvez également gérer votre propre compteur que vous augmenterez avec fork () et décrémenterez avec le signal SIGCHLD , en supposant que ppid reste inchangé. traité à la fourche.

Autres conseils

La meilleure chose à faire est d'ajouter toutes les tâches à une file d'attente, de lancer le nombre maximal de threads souhaités, puis de demander à chaque thread de demander une tâche à partir de la file d'attente, d'exécuter la tâche et de demander le suivant. . N'oubliez pas de mettre fin aux discussions lorsqu'il n'y a plus de tâches à faire.

Le fourrage est une opération coûteuse. En apparence, ce que vous voulez vraiment, ce sont des threads multiples et non des traitements multiples . La différence réside dans le fait que les threads sont beaucoup plus légers que les processus, car ils partagent un espace d'adressage virtuel, mais les processus ont des espaces d'adressage virtuels distincts.

Je ne suis pas un développeur PHP, mais une recherche rapide sur Google révèle que PHP ne prend pas en charge le multithreading de manière native, mais il existe des bibliothèques pour faire le travail.

Quoi qu'il en soit, une fois que vous avez compris comment générer des threads, vous devez déterminer le nombre de threads à générer. Pour ce faire, vous devez savoir quel est le goulot d'étranglement de votre application. Le processeur, la mémoire ou les E / S sont-ils des goulots d’étranglement? Vous avez indiqué dans vos commentaires que vous êtes lié au réseau et que le réseau est un type d'E / S.

Si vous étiez lié au processeur, vous n'obtiendrez autant de parallélisme que de cœurs de processeur; plus de threads et vous perdez juste du temps à faire des changements de contexte. En supposant que vous puissiez déterminer le nombre total de threads à générer, vous devez diviser votre travail en autant d'unités et faire en sorte que chaque thread traite une unité de manière indépendante.

Si vous étiez lié à la mémoire, le multithreading ne vous aiderait pas.

Puisque vous êtes lié aux E / S, déterminer le nombre de threads à générer est un peu plus compliqué. Si le traitement de tous les éléments de travail nécessite environ le même temps avec une très faible variance, vous pouvez estimer le nombre de threads à générer en mesurant la durée d'un élément de travail. Toutefois, comme les paquets réseau ont généralement des latences très variables, il est peu probable que ce soit le cas.

L’une des options consiste à utiliser des pools de threads: vous créez tout un tas de threads, puis pour chaque élément à traiter, vous voyez s’il existe un thread libre dans le pool. Si tel est le cas, vous devez laisser ce fil effectuer le travail et passer à l'élément suivant. Sinon, vous attendez qu'un fil soit disponible. Le choix de la taille du pool de threads est important - trop grand et vous perdez du temps à faire des changements de contexte inutiles. Trop peu et vous attendez trop souvent de discussions.

Une autre option consiste à abandonner le multithreading / le multitraitement et à ne faire que des E / S asynchrones. Puisque vous avez mentionné que vous travaillez sur un processeur monocœur, ce sera probablement l’option la plus rapide. Vous pouvez utiliser des fonctions telles que socket_select () pour tester si un socket a des données disponibles. Si tel est le cas, vous pouvez lire les données, sinon vous passez sur un autre socket. Cela nécessite beaucoup plus de comptabilité, mais vous évitez d'attendre que des données arrivent sur un socket lorsque celles-ci sont disponibles sur un autre socket.

Si vous souhaitez éviter les threads et les E / S asynchrones et vous en tenir au multitraitement, il peut toujours être intéressant que le traitement par élément soit suffisamment coûteux. Vous pourriez alors faire la division du travail comme suit:

$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);
}

man 2 setrlimit

Cela va être par utilisateur ce qui peut être ce que vous voulez de toute façon.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top