IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Programmation et administration système Perl Discussion :

Lancement de job en parallele : multithreading, multiprocess (threads, fork, job parallel, etc.)


Sujet :

Programmation et administration système Perl

  1. #1
    Responsable Perl et Outils

    Avatar de djibril
    Homme Profil pro
    Inscrit en
    avril 2004
    Messages
    19 793
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : avril 2004
    Messages : 19 793
    Points : 499 019
    Points
    499 019
    Par défaut Lancement de job en parallele : multithreading, multiprocess (threads, fork, job parallel, etc.)
    Salut,

    Compte tenu du nombre important de threads à ce sujet, j'ouvre un thread concacré à ce souci de parallelisation de job en perl.
    Ce serait bien que chacun participe en donnant des exemples de scripts propres et commentés avec l'utilisation des threads, des fork et autres modules pour le lancement de jobs en paralelle via du multithreading ou de la programmation parallèle.
    Ce serait bien aussi d'avoir une idée des modules les plus utilisés et surtout les plus fiables avec leur compatibilité Linux, Windows, Mac OS...

    N'ayant jamais été amené à utiliser cette technique ou très peu, je suis bien évidemment intéressé et avec les petites recherches à ce sujet, j'ai déjà listé quelques modules intéressant que voici :
    • module thread : thread, thread::Shared (ici)
    • module threads : threads, threads::Shared (ici)
    • module parallel : Parallel::Simple, Parallel::Jobs, Parallel::Forker (ici)
    • module MPI : Parallel::MPI, Parallel::MPI::Simple, Parallel::Mpich::MPD ... (ici)


    Y en a surement d'autres.

    A vos claviers et exemples pour ceux qui s'y connaissent déjà bien.

    Merci de votre participation

    N.B. Dans l'idéal, un tutoriel verrait le jour.

  2. #2
    Membre éprouvé Avatar de Gardyen
    Homme Profil pro
    Bio informaticien
    Inscrit en
    août 2005
    Messages
    637
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bio informaticien

    Informations forums :
    Inscription : août 2005
    Messages : 637
    Points : 1 050
    Points
    1 050
    Par défaut
    Mon petit laïus sur threads

    en premier compatibilité linux/windows:
    les threads fonctionnent sous les 2 OS, mais il faut avoir installer la version multithread, qui n'est pas forcement installée par défaut (ce qui a été mon cas sous linux). En cas de réinstallation il est à prévoir de recompiler/réinstaller certains modules (de mémoire j'ai du réinstaller DBI entre autres).
    Sous windows, avec ActivePerl 5.8.8 build 819, les threads sont installés par défaut.

    petit exemple de threading:
    Voila un petit script que j'utilise pour tester les fonctionnalités. Il est largement améliorable !!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    #!/usr/bin/perl
    use strict;
    use warnings;
     
    use Data::Dumper;
     
    use threads;
    use threads::shared;
    use Thread::Semaphore;
     
    my @list = ("command1", "command2", "command3", "command4", "command5", "command6", "command7", "command8", "command9", "command10");
     
    my $nombre_de_jobs_en_parallele = 4;
     
    # le sémaphore permet de réguler le nombre de jobs en simultané, bloquant le processus principal tant qu'il n'y a pas de place de libre
    my $semaphore = Thread::Semaphore->new($nombre_de_jobs_en_parallele);
     
    # quelques variables partagées
    my $cpt:shared;
    my $failed:shared;
     
    $cpt = 0;
    $failed = 0;
     
    my $started = 0;
     
    # le point d'entrée du job en parallèle
    sub forked_job($$$$$){
    	my $command = shift;
    	my $index = shift;
    	my $cpt_ref = shift;
    	my $failed_ref = shift;
    	my $sema_ref = shift;
     
    	# simule un travail de durée indéterminée entre 0 et 10 sec
    	my $int = int 10 * rand();
    	print "Le job $index va dormir $int sec au lieu d'exécuter la commande $command\n";
    	sleep($int);
     
    	# simule une erreur
    	my $str = "";
    	if ($int == 4){
    		$str = " avec une erreur";
    		$$failed_ref++;
    	}
    	print "Le job $index vient de se terminer$str\n";
     
    	# incrémente le nombre de jobs finis
    	$$cpt_ref++;
     
    	# on a une place de libre. Ne pas oublier de libérer le sémaphore même en cas d'erreur
    	$$sema_ref->up();
     
    	return;
    }
     
    # démarre tous les jobs, sauf erreur
    while ($started < scalar @list && !$failed){
    	my $command = $list[$started];
     
    	# incrémente le compteur
    	$started++;
     
    	# avons nous une place de libre ?
    	$semaphore->down();
     
    	# si le sémaphore est a 0, le processus principal va se bloquer en attendant une nouvelle place
    	print "Creation du job $started\n";
    	my $thr = threads->create("forked_job", (
    		$command,
    		$started,
    		\$cpt,
    		\$failed,
    		\$semaphore
    		)
    	);
    	# détache le job du thread principal, rend la main au thread principal
    	$thr->detach();
     
    	# si on veut attendre la fin du job pour redonner la main, on utilise
    	# $thr->join();
    }
     
    # attend les derniers jobs
    while ($cpt < $started){
    	print "Seul $cpt jobs finis sur $started, sleeping\n";
    	sleep(3);
    }
    print "$cpt jobs lances, $failed échoués, sur les ".scalar @list." prévus\n";
    print "The End.\n";
    en attendant les experts
    Nous les geeks, c'est pas qu'on a une case en moins, c'est juste qu'on compte à partir de zéro.
    Plus les choses changent, plus elles restent les mêmes

  3. #3
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : avril 2003
    Messages : 6 245
    Points : 8 560
    Points
    8 560
    Par défaut
    Citation Envoyé par djibril Voir le message
    - module thread : Thread
    Attention Thread est le vieux (5.6) module pour les threads, le module actuel est "threads" qui n'utilise plus le même modèle, il est bien plus fiable. threads::shared permet de partager certaines variables et de poser des verrous, attention, tous les types de variables ne peuvent pas être partagés entre threads, en particulier un grand nombre de modules souffrent de ce fait (et d'autres problèmes plus répandus et liés au parallélisme lui-même) et ne sont pas adaptés à un usage multi-thread (n'oubliez pas que ceci est vrai dans beaucoup d'autres langages).
    Un point qui diffère entre les threads de Perl et ceux d'autres langages est que par défaut rien n'est partagé entre les threads.

    Thread::Queue est sans doute l'un des modules les plus utiles pour véritablement travailler avec des threads, il permet d'envoyer des valeurs d'un thread à l'autre dans une file robuste. Thread::Semaphore fournit l'autre classique du parallélisme, c'est à dire le sémaphore. Ces deux modules sont dans le CORE.

    Thread::Appartment et PSiCHE semblent proposer une solution ambitieuse, mais qui requiert sans doute un temps d'adaptation.

    En attendant et avec un peu d'imagination, il n'est pas trop difficile de restreindre les modules qui ne sont pas thread-friendly à un seul thread et d'utiliser un système simple comme exposé dans ce post.

    --
    Jedaï

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    décembre 2007
    Messages
    132
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2007
    Messages : 132
    Points : 99
    Points
    99
    Par défaut
    Venant de poster concernant justement le fork()
    J'ajoute à mon tour ma contribution.
    Voici un code qui illustre fork() et l'utilisation de pipe() pour communiquer des enfants au père.

    Code perl : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #!/usr/bin/perl
     
    use strict;
    use warnings;
    use constant {
        MAX_PROCESS   => 10,
    };
     
    my ($child, $pid, @childs, @process_list, $child_pid);
     
     
    for (1...MAX_PROCESS) {
        pipe(FROM_CHILD, TO_PARENT);
        $pid = fork();
        if ($pid) { # Parent code
            push(@childs, $pid);        
            close TO_PARENT;
            $child_pid = <FROM_CHILD>; #On lit la réponse du fils
            chomp($child_pid);
            close FROM_CHILD;
            print "My child's pid : $child_pid \n";
            push(@process_list, $child_pid);
        }
        else {  #child
            close FROM_CHILD;
            print TO_PARENT $$,"\n";  #On écrit au parent le processus courant (du fils)
            close TO_PARENT;
            print "New process launched (",$_,"/",MAX_PROCESS,"): [$$]\n";
            sleep 5;
            exit(0);
        }
    }
     
    print "-> @process_list \n"; #resultat les pids de chaque processus enfants
     
    foreach (@childs) {
        waitpid($_,0);
    }

  5. #5
    Membre régulier
    Étudiant
    Inscrit en
    mai 2010
    Messages
    176
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : mai 2010
    Messages : 176
    Points : 75
    Points
    75
    Par défaut
    Voici un exemple avec Parralel::ForkManager. CF ici

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #!/opt/perl/bin/perl
     
    use strict;
    use warnings;
     
    use ForkManager;
     
    my $pm = Parallel::ForkManager->new(10);        #nombre de processus max
    $pm->run_on_finish( sub {
        printf "%s : Process completed: @_\n", scalar localtime
    });
     
    for my $i (1..15) {     #combien d'action a faire
        $pm->start($i) and next;
        sleep rand 9;
        $pm->finish;
    }
     
    printf "%s: Waiting for some child to finish\n", scalar localtime;
    $pm->wait_all_children;
     
    printf "%s: All processes finished.\n", scalar localtime;
    Resultat affiché:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    perl ./script.pl
    Fri Dec  2 11:07:05 2011 : Process completed: 11995 0 7 0 0
    Fri Dec  2 11:07:05 2011 : Process completed: 11998 0 10 0 0
    Fri Dec  2 11:07:06 2011 : Process completed: 11992 0 4 0 0
    Fri Dec  2 11:07:07 2011 : Process completed: 11996 0 8 0 0
    Fri Dec  2 11:07:07 2011 : Process completed: 11999 0 11 0 0
    Fri Dec  2 11:07:07 2011: Waiting for some child to finish
    Fri Dec  2 11:07:07 2011 : Process completed: 12041 0 15 0 0
    Fri Dec  2 11:07:08 2011 : Process completed: 11990 0 2 0 0
    Fri Dec  2 11:07:08 2011 : Process completed: 12013 0 13 0 0
    Fri Dec  2 11:07:08 2011 : Process completed: 11997 0 9 0 0
    Fri Dec  2 11:07:09 2011 : Process completed: 11991 0 3 0 0
    Fri Dec  2 11:07:09 2011 : Process completed: 11993 0 5 0 0
    Fri Dec  2 11:07:09 2011 : Process completed: 11994 0 6 0 0
    Fri Dec  2 11:07:09 2011 : Process completed: 12040 0 14 0 0
    Fri Dec  2 11:07:10 2011 : Process completed: 11989 0 1 0 0
    Fri Dec  2 11:07:12 2011 : Process completed: 12000 0 12 0 0
    Fri Dec  2 11:07:12 2011: All processes finished.
    Et résultat du ps -ef | grep perl:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    user12  11968 16676  2 11:07 pts/3    00:00:00 perl ./script.pl
    user12  11989 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  11990 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  11991 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  11993 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  11994 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  11996 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  11997 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  11999 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  12000 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  12013 11968  0 11:07 pts/3    00:00:00 perl ./script.pl
    user12  12016  4528  0 11:07 pts/5    00:00:00 grep perl
    Cet exemple permet de voir la simplicité d'utilisation de ce module que je pense utiliser pour faire un démarrage en parallèle de différents jobs.

    (Je suis un tout débutant en perl, si vous estimez que ce post ne sert pas ou n'est pas suffisamment pertinent, supprimez le, je ne vous en voudrai pas )

  6. #6
    Responsable Perl et Outils

    Avatar de djibril
    Homme Profil pro
    Inscrit en
    avril 2004
    Messages
    19 793
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : avril 2004
    Messages : 19 793
    Points : 499 019
    Points
    499 019
    Par défaut
    pour ce programme, toute participation est toujours appréciée .

  7. #7
    Responsable Perl et Outils

    Avatar de djibril
    Homme Profil pro
    Inscrit en
    avril 2004
    Messages
    19 793
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : avril 2004
    Messages : 19 793
    Points : 499 019
    Points
    499 019
    Par défaut
    Je relance le débat !

  8. #8
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : mai 2012
    Messages : 3 612
    Points : 12 465
    Points
    12 465
    Billets dans le blog
    1
    Par défaut
    J'ai utilisé MPI dans le passé, mais ce n'était pas en Perl (C et Fortran). MPI est surtout utile pour construire des systèmes distribués (processus tournant sur des bécanes différentes, distantes). On peut faire du parallélisme sur une même machine avec MPI, mais ce n'est pas vraiment le but.

    Sinon, les seules fois où j'ai fait du parallélisme en Perl, c'était en lançant des processus en tâches de fond depuis le shell sous Unix ou l'équivalent du shell (DCL) sous VMS.

  9. #9
    Membre habitué
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    novembre 2004
    Messages
    319
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : novembre 2004
    Messages : 319
    Points : 144
    Points
    144
    Par défaut
    Voici un exemple de script qui permet de lancer X traitements simultanés.
    Pour information, ce script fonctionne depuis plusieurs années

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
     
      $fichiers="$path  -iname '*nom_du_fichiert_*.$suffix[$k]*'";
      # on traite les noms de fichiers suffixés <jour>
      open ( Filelist ,"/usr/bin/find $fichiers -ls |")
        or die "Problème sur les noms de fichiers nom_du_fichier_<...>";
     #--------------------------------------------------
      # traitement des fichiers
      #--------------------------------------------------
      while(<Filelist>) {
        # pour chaque fichier nom_du_fichier_<cata>.<day>
        $count_files++;
        my @name=split(' ');
        my $filename = $name[10];
        my @info_path = split('/',$filename);
        my @status = stat ( $filename );
        my $cata_name = `basename $filename`;
        chomp $cata_name;
        $cata_name=~s/.*bpimagelist_//g;
        $cata_name=~s/\.$suffix[$k].*//g;
        my $other_cata_name = $info_path[6] ? $info_path[6] : '';
           if (( $status[9] > time - $file_max_time ) and ( $cata_name eq $other_cata_name )) {
          # NOTA : pour eviter d'intégrer des fichiers obsolètes
          print " jobs_vol_nb - $info_path[4] . $info_path[5] . $other_cata_name . $suffix[$k] : => traitement...\n";
          my $catalogue = $other_cata_name;
          if ($pid = fork) {
            # dans le processus pere
            push ( @children, $pid);
            $nb_process++;
            if ( $nb_process >= $MAX_PROCESS ) {
              # on ne lance pas plus de $MAX_PROCESS traitements simultanés
              # donc on attend la terminaison d'un des fils...
              $waitedpid = wait;
              my $children_not_terminated = 1;
              while ($children_not_terminated) {
                # la terminaison des fils <<open ( Filelist ,"/usr/bin/find $fichiers -ls |")>>
                # ne doit pas générer un nouveau fils import_job_vol_nb.pl
                FOUND : {
                  for (my $i=0; $i < @children; $i++) {
                    if ( $children[$i] == $waitedpid ) {
                      splice(@children, $i,1);
                      $children_not_terminated = 0;
                      last FOUND;
                    }
                  }
                }
                if ($children_not_terminated) {
                  $waitedpid = wait;
                }
              }
              $nb_process--;
            }
          }
          else {
            # dans les processus fils
            die "cannot fork: $!" unless defined $pid;
            parse_jobs_vol_nb($filename, $catalogue);
            exit;
          }
        }
        else {
          if ( $cata_name eq $other_cata_name ) {
            print " jobs_vol_nb - $info_path[4] . $info_path[5] . $other_cata_name . $suffix[$k] : OBSOLETE : => ALARME !\n";
          }
        }
      }
      close Filelist;
      # on passe à la liste de fichiers suivante
    }
    Ceci étant que la boucle qui gère la liste des fichiers à traiter
    Si tu tapes ta tête contre une cruche et que ça sonne creux,n'en déduis pas que c'est la cruche qui est vide.

  10. #10
    Responsable Perl et Outils

    Avatar de djibril
    Homme Profil pro
    Inscrit en
    avril 2004
    Messages
    19 793
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : avril 2004
    Messages : 19 793
    Points : 499 019
    Points
    499 019
    Par défaut
    Dès que possible, je me lance dans un tutoriel.

  11. #11
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : mai 2012
    Messages : 3 612
    Points : 12 465
    Points
    12 465
    Billets dans le blog
    1
    Par défaut
    Un autre système distribué que j'ai utilisé et bien aimé est PVM (Parallel Virtual Machine). Ce n'était pas en Perl à l'époque (C et Fortran), mais il y a un module Perl qui a l'air assez complet pour l'utiliser.

  12. #12
    Membre à l'essai
    Profil pro
    Inscrit en
    avril 2005
    Messages
    5
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : avril 2005
    Messages : 5
    Points : 11
    Points
    11
    Par défaut Many Core Engine
    Bonjour

    Il existe aussi le module MCE pour faire des traitements en parallèle.

    Le projet sur google code :
    http://code.google.com/p/many-core-engine-perl/

    Le lien vers le module sur metacpan :
    https://metacpan.org/release/MCE

    Amicalement

    sdeseille

Discussions similaires

  1. Réponses: 6
    Dernier message: 28/05/2014, 15h14
  2. Réponses: 13
    Dernier message: 28/06/2012, 02h03
  3. Réponses: 2
    Dernier message: 02/01/2010, 18h59
  4. Réponses: 4
    Dernier message: 22/11/2008, 17h15
  5. Réponses: 18
    Dernier message: 13/04/2005, 15h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo