2010-12-13 45 views
6

Salut J'ai un script simple qui prend un fichier et exécute un autre script Perl dessus. Le script le fait pour tous les fichiers image du dossier actuel. Cela fonctionne sur une machine avec 2 processeurs Xeon quad core, 16 Go de RAM, exécutant RedHat Linux.Comment puis-je modifier mon script Perl pour utiliser plusieurs processeurs?

Le premier script work.pl appelle fondamentalement magicplate.pl transmet certains paramètres et le nom du fichier pour magicplate.pl à traiter. Magic Plate prend environ une minute pour traiter chaque image. Parce que work.pl exécute la même fonction plus de 100 fois et parce que le système a plusieurs processeurs et cœurs, je pensais diviser la tâche afin qu'elle puisse fonctionner plusieurs fois en parallèle. Je pourrais diviser les images en différents dossiers si nécessaire. Toute aide est la bienvenue. Merci

Voici ce que j'ai jusqu'à présent:

use strict; 
use warnings; 


my @initialImages = <*>; 

foreach my $file (@initialImages) { 

    if($file =~ /.png/){ 
     print "processing $file...\n"; 
     my @tmp=split(/\./,$file); 
     my $name=""; 
     for(my $i=0;$i<(@tmp-1);$i++) { 
      if($name eq "") { $name = $tmp[$i]; } else { $name=$name.".".$tmp[$i];} 
     } 

     my $exten=$tmp[(@tmp-1)]; 
     my $orig=$name.".".$exten; 

     system("perl magicPlate.pl -i ".$orig." -min 4 -max 160 -d 1"); 
    } 
}  

Répondre

3

Vous pouvez utiliser Parallel :: ForkManager (mis MAX_PROCESSES $ au nombre de dossiers traités en même temps):

use Parallel::ForkManager; 
use strict; 
use warnings; 

my @initialImages = <*>; 

foreach my $file (@initialImages) { 

    if($file =~ /.png/){ 
     print "processing $file...\n"; 
     my @tmp=split(/\./,$file); 
     my $name=""; 
     for(my $i=0;$i<(@tmp-1);$i++) { 
      if($name eq "") { $name = $tmp[$i]; } else { $name=$name.".".$tmp[$i];} 
     } 

     my $exten=$tmp[(@tmp-1)]; 
     my $orig=$name.".".$exten; 

    $pm = new Parallel::ForkManager($MAX_PROCESSES); 
    my $pid = $pm->start and next; 
     system("perl magicPlate.pl -i ".$orig." -min 4 -max 160 -d 1"); 
    $pm->finish; # Terminates the child process 

    } 
}  

Mais comme le suggère Hugmeir d'exécuter l'interpréteur perl encore et encore pour chaque nouveau fichier ce n'est pas une bonne idée.

+2

"Exécuter encore et encore l'interpréteur Perl pour chaque nouveau fichier n'est pas une bonne idée" - Oui, mais lorsque vous utilisez un fork, vous ne lancez pas un nouvel interpréteur Perl. Fork copie le processus parent, et Linux utilise CoW, donc c'est encore moins cher qu'une copie complète. – JimB

+2

Aussi, pourquoi commencez-vous un nouvel interprète après votre fourchette? Exécutez le code Perl dans le nouveau processus enfant. – JimB

+0

@JimB: Je veux dire l'appel système ne pas forking. Et j'utilise l'appel système parce que le code original l'a utilisé. – gangabass

7

Vous devriez considérer ne pas créer un nouveau processus pour chaque fichier que vous voulez traiter - Il est terriblement inefficace, et probablement ce qui se passe la plupart des votre temps ici. Le simple chargement de Perl et de tous les modules que vous utilisez encore et encore devrait créer une surcharge. Je me souviens d'une affiche sur PerlMonks qui a fait quelque chose de similaire, et a fini par transformer son deuxième script en un module, réduisant le temps de travail d'une heure à un couple de minutes. Avec le deuxième script refactorisé en tant que module, here's an example of thread usage, dans lequel BrowserUK crée un pool de threads, en l'alimentant dans une file d'attente.

+5

Démarrage d'un nouvel interpréteur perl est horriblement inefficace, mais la création d'un nouveau processus avec fork() est très rapide (surtout depuis que Linux utilise CoW). – JimB

+2

Non. Si votre travail nécessite 1 minute de temps CPU, le temps passé au démarrage de la nouvelle tâche sera assez négligeable.Perl pourrait utiliser, disons, 1 seconde de CPU pour démarrer son environnement (si vous avez beaucoup de modules chargés, j'ai vu ça) mais après ça, tout est à vous. Lisez attentivement la question. – MarkR

+1

NB: Les threads Perl aspirent. Vraiment, ils le font. Ils créent des tas de copies de tout (pas de copies CoW, de vraies copies). Ils ne fonctionnent pas correctement dans certains cas, mais utilisent toujours des tas de ressources inutiles. Fourchette à la place, c'est beaucoup plus efficace et plus susceptible de fonctionner. – MarkR

3
  • Importez "maigcplate" et utilisez le filetage.
  • Démarrer magicplate.pl en arrière-plan (vous devez ajouter la limitation des processus)
  • importation « magicplate » et utiliser fork (ajouter la limitation des processus et un faucheur Kiddy)
  • Marque « maigcplate » un démon avec une piscine des travailleurs = Nombre de processeurs
    • utiliser une implémentation MQ pour la communication
    • prises utiliser pour la communication
  • Utilisez webserver (nginx, apache, ...) et envelopper dans REST pour un webservice
  • etc ...

Tous ces centre autour de la création de travailleurs multiples qui peuvent chaque exécution de leur propre cpu. Certaines implémentations utiliseront mieux les ressources (celles qui ne démarrent pas un nouveau processus) et seront plus faciles à implémenter et à maintenir.