#!/usr/bin/perl -w #------------------------------------------------------------------------------- # smp_launcher.pl v1.0 - february 2010 # Author: F. LECERF # see http://chicken.genouest.org for update or full documentation #------------------------------------------------------------------------------- use strict; use Getopt::Long; use Proc::Simple; #------------------------------------------------------------------------------- # CONSTANT value definition #------------------------------------------------------------------------------- $|=1; my $soft = "smp_launcher.pl"; my $VERSION = "1.0"; my $year = "2010"; my $file = undef; my $verbose = undef; my $nb_cpu = undef; my $unique_mode = undef; my $repeat_mode = undef; my $data_mode = undef; # Benchmark my $begin_time = times(); my %Options = ( 'file=s' => \$file, 'cpu=s' => \$nb_cpu, 'verbose' => \$verbose, 'unique' => \$unique_mode, 'repeat=s' => \$repeat_mode, 'data=s' => \$data_mode, ); my %OptionsHelp = ( 'file ' => '-f [file], command file', 'data ' => '-d [data], file list of data file', 'repeat ' => '-r [nb], repeat analysis X times', 'unique ' => '-u, launch unique analysis from command file', 'verbose' => '-v, verbose (optionnal)', 'cpu ' => '-c [nb], number of cpu', ); #------------------------------------------------------------------------------- sub usage ( $ ) { my ($msg)=@_; print "Error: $msg\n"; print "--------------------------------------------------------------------------------\n"; print "$soft $VERSION ($year)\n"; print "--------------------------------------------------------------------------------\n"; print "This script permits to launch several process in background\n"; print "The command to launch in background have to be defined in a separate file\n"; print "You can choose either to:\n"; print " - launch different analyses (specified in file) one time (-u)\n"; print " - launch same analyses with different input data (-d)\n"; print " - launch same analyses with same data many times (-r)\n"; print "--------------------------------------------------------------------------------\n"; print "Usage: $0 [options]\n"; print "Options:\n"; map {printf "\t$_: %s\n",$OptionsHelp{$_};} keys %OptionsHelp; print "Please cite: Lecerf F., $year\n"; exit(1); } #------------------------------------------------------------------------------- # Check parameters #------------------------------------------------------------------------------- GetOptions(%Options); defined $file || &usage('Command file required (-f)'); defined $nb_cpu || &usage('Number of cpu required (-c)'); ((defined $unique_mode)||(defined $data_mode)||(defined $repeat_mode)) || &usage('Analysis parameter mode required (-u or -d or -r)'); print "--------------------------------------------------------------------------------\n"; print "$soft $VERSION ($year)\n"; print "--------------------------------------------------------------------------------\n"; #------------------------------------------------------------------------------- # Loading commands from file #------------------------------------------------------------------------------- open (IN,$file) || die "cannot open COMMAND file: $file"; my @CommandList = (); while (my $line = ) { $line=~s/\s+$//; push (@CommandList,$line); } close (IN); my $nb_command = scalar (@CommandList); print "$nb_command command(s) loaded from file\n"; #------------------------------------------------------------------------------- # Setting up ANALYSIS command #------------------------------------------------------------------------------- my @List_proc = (); my @Command = (); # same analysis many times - simulatin MODE if (defined $repeat_mode) { print "setting up jobs in REPEAT mode...\n"; my $proc = undef; my $single_command = shift (@CommandList); print " - command: $single_command\n"; print " - preparing $repeat_mode job processes\n"; for (my $i=1;$i<=$repeat_mode;$i++) { $proc = Proc::Simple->new(); push (@List_proc,$proc); push (@Command,$single_command); } } # list of different commands elsif (defined $unique_mode) { print "setting up jobs in UNIQUE mode...\n"; my $proc = undef; my $nb_analysis = scalar (@CommandList); print " - preparing $nb_analysis job processes\n"; @Command = @CommandList; for (my $i=1;$i<=$nb_analysis;$i++) { $proc = Proc::Simple->new(); push (@List_proc,$proc); } } # same analysis on different datafiles elsif (defined $data_mode) { print "setting up jobs in DATA mode...\n"; my $proc = undef; # reading data filename from filelist my @Datafilename = (); open (DATA,"$data_mode") || die "cannot open datafile list: $data_mode!\n"; while (my $line = ) { $line=~s/\s+$//; push (@Datafilename,$line); } close (DATA); my $single_command = shift (@CommandList); my $nb_analysis = scalar (@Datafilename); print " - command: $single_command\n"; print " - preparing $nb_analysis job processes\n"; # creating command line with data filename & list of process foreach my $datafile (@Datafilename) { my $commandline = $single_command." ".$datafile; push (@Command,$commandline); $proc = Proc::Simple->new(); push (@List_proc,$proc); } } #------------------------------------------------------------------------------- # Launch ANALYSES #------------------------------------------------------------------------------- my %RunningJob = (); my $nb_jobs = keys %RunningJob; my $job_id = 0; print "\nLaunching job processes in background...\n"; while (1) { my $nb_remaining_process = scalar(@List_proc); if (($nb_jobs < $nb_cpu)&&($nb_remaining_process != 0)) { # getting one job and one command my $job = shift (@List_proc); my $command = shift (@Command); # launching job $job_id++; my $o_stdout = "job_".$job_id.".stdout"; my $o_stderr = "job_".$job_id.".stderr"; $job->redirect_output($o_stdout,$o_stderr); $job->start($command); my $pid = $job->pid; # store launched jobs $RunningJob{$job_id}{'command'} = $command; $RunningJob{$job_id}{'job'} = $job; $RunningJob{$job_id}{'pid'} = $pid; $nb_jobs++; print " ++ job No. $job_id launched (pid $pid)\n"; } foreach my $job_id (keys %RunningJob) { my $launched_job = $RunningJob{$job_id}{'job'}; my $pid = $RunningJob{$job_id}{'pid'}; #print " JOB No. $job_id with PID $pid\n"; my $running = $launched_job->poll(); if ($running == 0) { $nb_jobs--; print " -- job No $job_id terminated (pid=$pid)\n"; delete ($RunningJob{$job_id}); } } last if ((keys %RunningJob == 0)); } print "\nALL jobs performed.\n"; my $end_time=times(); printf "--\nExecution time: %.2f seconds CPU user-time\n",($end_time-$begin_time); =head1 NAME SMP Launcher - a script to run multiple job on a SMP computer =head1 DESCRIPTION This script will manage a queue job list (PERL programs or not, whatever) for you on a SMP computer. You can choose between 3 analysis mode: run many different programs (batch mode), run the same program with different input data files (loop mode) or run the same program many times (simulation mode). =head1 README A full documentation is available at http://chicken.genouest.org =head1 PREREQUISITES This script requires the C module. It also requires C module. =pod OSNAMES Linux (tested on Debian with PERL 5.10.0) and MacOS X (tested on 10.5.8 with PERL 5.8.8) =head1 SCRIPT CATEGORIES Unix/System_administration ComputerScience =cut