OVH Cloud OVH Cloud

lancer N processus sur M en parallele

7 réponses
Avatar
Pierrick LE GALL
Bonjour,

Fort probablement mon problème est simple et se trouve déjà résolu
quelque part. N'hésitez pas à me renvoyer vers la documentation
appropriée si elle existe.

Je cherche à créer un script qui me permette, sur une liste de M (50 par
exemple) commandes shell d'en avoir toujours N (10 pour l'exemple
toujours) qui tournent en parallèle. Sauf jusqu'à atteindre le 10eme et
sur les 10 derniers...

La fonction open me permet de récupérer le pid correspondant à la
commande shell lancée. A partir de là, je peux maintenir un tableau des
pid lancés et contrôler toutes les secondes que tous ces pid sont encore
en vie. S'il y en a moins de 10, et tant qu'il m'en reste, je lance des
commandes.

push @rands, int(rand 10) for (1..50);

foreach (@rands)
{
my $pid = open(P, "sleep $_ & |");
}

(bon, là, c'est juste le tout début de la phase de test et de
compréhension du problème).

Ce qu'il me manque, c'est la fonction qui permet de vérifier qu'un pid
est en vie.

J'avoue que je me demande très franchement si je pars dans le bon sens
et s'il n'y a pas de technique plus propre.

@+

--
Pierrick LE GALL <http://le-gall.net>

7 réponses

Avatar
dominix
"Pierrick LE GALL" a écrit dans le message de
news:
Bonjour,

Fort probablement mon problème est simple et se trouve déjà résolu
quelque part. N'hésitez pas à me renvoyer vers la documentation
appropriée si elle existe.

Je cherche à créer un script qui me permette, sur une liste de M (50 par
exemple) commandes shell d'en avoir toujours N (10 pour l'exemple
toujours) qui tournent en parallèle. Sauf jusqu'à atteindre le 10eme et
sur les 10 derniers...

La fonction open me permet de récupérer le pid correspondant à la
commande shell lancée. A partir de là, je peux maintenir un tableau des
pid lancés et contrôler toutes les secondes que tous ces pid sont encore
en vie. S'il y en a moins de 10, et tant qu'il m'en reste, je lance des
commandes.

push @rands, int(rand 10) for (1..50);

foreach (@rands)
{
my $pid = open(P, "sleep $_ & |");
}

(bon, là, c'est juste le tout début de la phase de test et de
compréhension du problème).

Ce qu'il me manque, c'est la fonction qui permet de vérifier qu'un pid
est en vie.

J'avoue que je me demande très franchement si je pars dans le bon sens
et s'il n'y a pas de technique plus propre.

@+


cet article devrait t'aider.
(google doing many thinks like ping)
http://www.stonehenge.com/merlyn/UnixReview/col41.html

-- dominix

Avatar
Jérémy JUST
On Tue, 25 Jan 2005 00:38:48 +0100
Pierrick LE GALL wrote:

Je cherche à créer un script qui me permette, sur une liste de M (50
par exemple) commandes shell d'en avoir toujours N (10 pour l'exemple
toujours) qui tournent en parallèle.


Si Perl ne te sert qu'à lancer des commandes, tu as plutôt intérêt à
utiliser un outil plus simple, qui n'est fait que pour ça: « make ».

La version GNU de make a une option « -j » qui fait ce que tu veux.


(bon, là, c'est juste le tout début de la phase de test et de
compréhension du problème).


Si tu veux quand même le faire en Perl, je te conseille de décrire
précisément, sur le papier, en français, *ce que* tu veux faire, puis
décrire *comment* tu vas le faire.
Une fois que tu auras écrit ça en français, ce sera très simple de
l'écrire en Perl.

D'après le code que tu as écrit, j'ai l'impression que tu tâtonnes
directement en Perl, ce qui va te faire perdre un temps fou.


Ce qu'il me manque, c'est la fonction qui permet de vérifier qu'un pid
est en vie.


Regarde du côté de « wait » et « waitpid ».

--
Jérémy JUST

Avatar
Pierrick LE GALL
Jérémy JUST writes:

(bon, là, c'est juste le tout début de la phase de test et de
compréhension du problème).


Si tu veux quand même le faire en Perl, je te conseille de décrire
précisément, sur le papier, en français, *ce que* tu veux faire, puis
décrire *comment* tu vas le faire.
Une fois que tu auras écrit ça en français, ce sera très simple de
l'écrire en Perl.
D'après le code que tu as écrit, j'ai l'impression que tu tâtonnes
directement en Perl, ce qui va te faire perdre un temps fou.


Je ne souhaitais pas vous ennuyer avec les détails, mais je sais très
précisément le résultat que je veux obtenir.

Le but est de pouvoir lancer N fois le même script SQL sur une base de
données (Oracle) avec un paramètre différent à chaque fois. Le script
doit être passé M fois au total (M paramètres différents). Le but est
d'utiliser les possibilités multi-processeur (16) de la machine pour
améliorer un temps d'exécution actuellement faramineux (15aine
d'heures).

Dans mon cas, il faut réussir à avoir N exécutions de script en
parallèle, et pas davantage (sinon, les performances risquent de
s'écrouler).

La solution que je sais faire actuellement est la suivante, en shell :
lancement d'un script tant que (ps -ef | grep mon_script | wc -l )
inférieur à N et qu'il reste des scripts à lancer, avec 5 secondes
d'intervalle entre chaque test. Je serai étonné que quelqu'un puisse
trouver ça "propre".

Ce qu'il me manque, c'est la fonction qui permet de vérifier qu'un pid
est en vie.


Regarde du côté de « wait » et « waitpid ».


J'avais déjà un peu regardé, mais ça ne semblait pas convenir, après
lecture rapide. Si tu me dis que je devrais investiguer davantage, je
vais le faire.

Merci

--
Pierrick LE GALL <http://le-gall.net>


Avatar
gomor-usenet
Pierrick LE GALL wrote in message news:...
[..]
Ce qu'il me manque, c'est la fonction qui permet de vérifier qu'un pid
est en vie.
[..]


Pour tester l'allocation d'un PID, il suffit d'expédier le
signal 0 au PID.

Dans `perldoc -f kill':

--
If SIGNAL is zero, no signal is sent to the process. This is a
useful way to check that a child process is alive and hasn't
changed its UID. See perlport for notes on the portability of
this construct.
--

--
^ ___ ___ FreeBSD Network - http://www.GomoR.org/ <-+
| / __ |__/ Security Engineer, searching for work |
| __/ | ---[ zsh$ alias psed='perl -pe ' ]--- |
+--> Net::Packet <=> http://search.cpan.org/~gomor/ <--+

Avatar
Paul Gaborit
À (at) Tue, 25 Jan 2005 00:38:48 +0100,
Pierrick LE GALL écrivait (wrote):
Je cherche à créer un script qui me permette, sur une liste de M (50 par
exemple) commandes shell d'en avoir toujours N (10 pour l'exemple
toujours) qui tournent en parallèle. Sauf jusqu'à atteindre le 10eme et
sur les 10 derniers...


L'utilisation de 'make' avec son option '-j' est effectivement une bonne piste
(simple à mettre en oeuvre).

Si vous préférez une solution 'pure' Perl et si vous ne souhaitez pas écrire
vous-même la gestion des processus, vous pouvez utiliser le module
Parallel::Jobs (sur le CPAN le plus proche).

--
Paul Gaborit - <http://www.enstimac.fr/~gaborit/>
Perl en français - <http://www.enstimac.fr/Perl/>

Avatar
Jérémy JUST
On Tue, 25 Jan 2005 08:11:42 +0100
Pierrick LE GALL wrote:

Je ne souhaitais pas vous ennuyer avec les détails, mais je sais très
précisément le résultat que je veux obtenir.


Maintenant, il faut écrire *comment* tu vas l'obtenir. Par exemple:

<<<<<
* Je fais une liste de toutes mes commandes.

* Quinze fois, je prends une commande dans la liste et je la lance.

* Tant que la liste n'est pas vide,
j'attends qu'une des commandes se termine,
puis j'en prends une nouvelle dans la liste et je la lance.







Et hop, tu n'as plus qu'à traduire en Perl!
Pour t'aider, « prendre une commande dans la liste », ça se fait avec
« shift », et « attendre qu'une commande se termine », c'est avec
« wait ».


La solution que je sais faire actuellement est la suivante, en shell :
lancement d'un script tant que (ps -ef | grep mon_script | wc -l )
inférieur à N et qu'il reste des scripts à lancer, avec 5 secondes
d'intervalle entre chaque test.


Tu devrais vraiment aller jeter un oeil à make (et son option « -j »).
Tu régleras ton problème en 15 minutes (lecture succincte de la doc
comprise).


--
Jérémy JUST





Avatar
Pierrick LE GALL
"dominix" writes:

"Pierrick LE GALL" a écrit:

[...]

Je cherche à créer un script qui me permette, sur une liste de M (50
par exemple) commandes shell d'en avoir toujours N (10 pour l'exemple
toujours) qui tournent en parallèle. Sauf jusqu'à atteindre le 10eme
et sur les 10 derniers...


cet article devrait t'aider.
(google doing many thinks like ping)
http://www.stonehenge.com/merlyn/UnixReview/col41.html


En effet, la lecture de cet article a été d'une grande aide et m'a
rappelé mes cours de programmation multi-processus...

Merci également à Jérémy JUST, Gomor et Paul Gaborit pour leurs
conseils, tout ceci allait dans le même sens. Je pense tenter bientôt la
solution make -j, mais à titre d'expérience plutôt car le machine cible
n'aura certainement pas GNU make d'installé :-/

Ressemblant très fortement au script de Randal L. Schwartz, voici mon
adaptation :

[code perl]

#!/usr/bin/perl -w

use strict;
use Getopt::Long;

my %opt = ();
GetOptions(%opt, 'max=i', 'listfile=s');
foreach ('max', 'listfile')
{
die 'Error: option --'.$_.' is mandatory'."n" if not defined $opt{$_};
}

if (not -r $opt{listfile})
{
die 'Error: file '.$opt{listfile}.' cannot be read'."n";
}

my @commands;

open(LIST, '< '.$opt{listfile});
while (<LIST>)
{
chomp;
s/r$//;
next if /^$/;
push @commands, $_;
}
close(LIST);

# @commands = map 'echo '.$_.' > /dev/null; sleep '.(int(rand 20) + 0), (1..3);
my %running_pids;

sub wait_for_a_kid
{
my $pid = wait;
return 0 if $pid < 0;
warn '[', $running_pids{$pid}, '] is finished', "n";
1;
}

sub launch_command
{
my $command = shift;
system $command;
1;
}

for (@commands)
{
wait_for_a_kid() if keys %running_pids >= $opt{max};

if (my $pid = fork)
{
# parent does...
$running_pids{$pid} = $_;
warn '[', $running_pids{$pid}, '] launched', "n";
}
else
{
# child does...
exit(!launch_command($_));
}
}

1 while wait_for_a_kid();

print 'done', "n";

[/code perl]

Il vous suffit de remplir le fichier commands.list (pour mes tests, vous
voyez dans le code que j'ai utiliser des sleep avec un rand) et de
lancer :

$ parallel.pl --max=5 --listfile=commands.list

@+

--
Pierrick LE GALL <http://le-gall.net>