#!/usr/bin/perl require "sys/wait.ph"; print "batcher v1.0 -- the ultimate batching program\n"; print " by: Hani Jamjoom \n\n"; if ( $#ARGV < 0 ) { print "USUAGE: batcher --cmd \"command line\" \n" . " --post \"command line\"\n\n" . " --opt name val\n" . " --opt_range name start_val stop_val step_size\n" . " --opt_list name val_or_string_between_quotes ... \n\n" . " --machines m1 m2 m3 ... \n" . " --outfile filename\n" . " --break_on_first\n" . " --sync_machines\n" . " --check \n"; exit(0); } # PARSE OPTIONS $machines_count = 1; $break_on_first = 0; $sync_machine = 0; while ( $#ARGV >= 0 ) { $in_arg = shift(@ARGV); if ( $in_arg eq "--cmd" ) { $in_cmd[$cmd_count] = shift(@ARGV); $cmd_count++; } elsif ( $in_arg eq "--opt" ) { $in_name = shift(@ARGV); $in_val = shift(@ARGV); $opt_val{$in_name} = $in_val; } elsif ( $in_arg eq "--opt_range" ) { $in_name = shift(@ARGV); $in_start_val = shift(@ARGV); $in_stop_val = shift(@ARGV); $in_step_size = shift(@ARGV); $opt_range_start{$in_name} = $in_start_val; $opt_range_stop{$in_name} = $in_stop_val; $opt_range_step{$in_name} = $in_step_size; } elsif ( $in_arg eq "--opt_list" ) { $in_name = shift(@ARGV); $in_val = "NOTHING"; while ( (index($in_val, "--") == -1) && ($#ARGV >= 0) ) { $in_val = shift(@ARGV); if ( index($in_val, "--") == -1 ) { $opt_list{$in_name} .= $in_val . "="; } else { unshift(ARGV, $in_val); } } } elsif ( $in_arg eq "--outfile" ) { $in_output = shift(@ARGV); } elsif ( $in_arg eq "--break_on_first" ) { $break_on_first = 1; } elsif ( $in_arg eq "--check" ) { $check = 1; } elsif ( $in_arg eq "--sync_machines" ) { $sync_machines = 1; } elsif ( $in_arg eq "--post" ) { $in_post = shift(@ARGV); } elsif ( $in_arg eq "--machines" ) { $in_val = ""; $machines_count = 0; while ( (index($in_val, "--") == -1) && ($#ARGV >= 0) ) { $in_val = shift(@ARGV); if ( index($in_val, "--") == -1 ) { $in_machines[$machines_count] = $in_val; $machines_count++; } else { unshift(ARGV, $in_val); } } } else { print "SYNTAX ERROR: Dude, you wrote somethig ($in_arg) that is not supported\n"; exit(0); } } # Some Range Value Initializations $depth_max = 0; # How far into the list did we change $depth_curr = 0; foreach $key (sort keys(%opt_range_start)) { $opt_current{$key} = $opt_range_start{$key}; } foreach $key (sort keys(%opt_list)) { ($opt_current{$key}, $junk) = split(/=/, $opt_list{$key}, 2); } foreach $key (sort keys(%opt_val)) { $opt_current{$key} = $opt_val{$key}; } @opt_range = sort keys(%opt_current); # list machines if ($machines_count > 1) { print "\nYou have specified the following ($machines_count) machines to use:\n "; for ($i=0; $i<$machines_count;$i++) { print "$in_machines[$i]"; if ($i < ($machines_count-1)) { print ","; } else { print "\n"; } } } # check for review if ( $check == 1 ) { print " -- PLEASE REVIEW WHAT I AM ABOUT TO DO --\n\n"; } $total_runs=0; while ( !$done ) { $outfile = $in_output; for ($i=0; $i<$cmd_count; $i++) { $cmd[$i] = $in_cmd[$i]; } $total_runs++; # Perform variable sustitution foreach $key (sort keys(%opt_current)) { for ($i=0; $i<$cmd_count; $i++) { $cmd[$i] =~ s/$key/$opt_current{$key}/g; } $outfile =~ s/$key/$opt_current{$key}/g; } for ($i=0; $i<$cmd_count; $i++) { $cmd[$i] =~ s/outfile/$outfile/g; $cmd[$i] =~ s/machine/$in_machines[$i]/g; } # Print Sample if ( $check == 1 ) { if (!$sample ) { print "Here is a sample execution:\n"; for ($i=0; $i<$cmd_count; $i++) { print "> $cmd[$i]\n"; print "[IN PARALLEL:] " if ($cmd_count > 1); } print ".. Checking if files exist:\n"; $sample = 1; } } print "WARNING: $outfile exists\n" if (-e $outfile); # Determine next file to run $done = &next_file_to_run; } if ($check ==1) { print "\n> Do you want to me to proceed (y/n)? "; $in_char = getc; if ( $in_char ne 'y' ) { printf "Goodbye...\n"; exit(0); } } # REAL WORK STARTS HERE... $total_time = 0; $curr_run = 0; $done = 0; $outfiles = ""; for($i=0; $i<$machines_count; $i++) { $machines_state[$i] = 0; $machines_outfiles[$i] = ""; $machines_times[$i] = 0; $machines_start[$i] = time(); $machines_runs[$i] = 0; } while ( !$done ) { $curr_run++; # skip all the machine selection if non is specified. if ( $machines_count == 1 ) { $curr_machine = 0; $elapse = time() - $machines_start[0]; $machines_time[0] += $elapse; goto _NEW_JOB; } # Go through machines and if all are busy, then # wait for some to finish. Once one finishes, # then start the next job. _FIND_MACHINE: $finished_machines = 0; if ($sync_machines == 0 ) { $start_pos = $curr_machine; } else { $start_pos = 0; } $curr_machine = -1; for($i=$start_pos; $i<$machines_count; $i++) { if ($machines_state[$i] == 0) { $curr_machine = $i; goto _NEW_JOB; } } _NO_MACHINE: for($i=0; $i<$machines_count; $i++) { if (($machines_pid[$i] > 0) && (waitpid($machines_pid[$i],&WNOHANG) != 0)) { $machines_state[$i] = 0; $curr_machine = $i; $finished_machines++; $elapse = time() - $machines_start[$i]; $machines_time[$i] += $elapse; $machines_pid[$i] = -1; } } if ( $curr_machine < 0 ) { sleep 1; goto _NO_MACHINE; } if ( $sync_machines ) { if ( $finished_machines == $machines_count ) { $start_pos = 0; goto _FIND_MACHINE; } else { $finished_machines = 0; goto _NO_MACHINE; } } _NEW_JOB: # Keep Track of how fast is each machine $machines_start[$curr_machine] = time(); $machines_state[$curr_machine] = 1; $machines_runs[$curr_machine]++; $pid == -1; if ( $machines_count > 1) { if ( $pid = fork) { $outfile = $in_output; foreach $key (sort keys(%opt_current)) { $outfile =~ s/$key/$opt_current{$key}/g; } $outfile =~ s/machine/$in_machines[$curr_machine]/g; if ( index($outfiles,$outfile) == -1 ) { $outfiles .= " " . $outfile; } if ( index($machines_outfiles[$curr_machine],$outfile) == -1 ) { $machines_outfiles[$curr_machine] .= " " . $outfile; } $machines_pid[$curr_machine] = $pid; goto _END_RUN; } elsif (($pid == 0) || ($machines_count == 0)) { goto _THIS_RUN; } else { die "Can't fork: $!\n"; } } _THIS_RUN: $total_time = 0; for($i=0;$i<$machines_count;$i++) { $total_time += $machines_time[$i]; } $average_time = $total_time / $curr_run; $TTF = ($average_time) * ($total_runs - $curr_run); $outfile = $in_output; for ($i=0; $i<$cmd_count; $i++) { $cmd[$i] = $in_cmd[$i]; } # Perform variable sustitution foreach $key (sort keys(%opt_current)) { for ($i=0; $i<$cmd_count; $i++) { $cmd[$i] =~ s/$key/$opt_current{$key}/g; } $outfile =~ s/$key/$opt_current{$key}/g; } $outfile =~ s/machine/$in_machines[$curr_machine]/g; for ($i=0; $i<$cmd_count; $i++) { $cmd[$i] =~ s/outfile/$outfile/g; $cmd[$i] =~ s/machine/$in_machines[$curr_machine]/g; } if ( index($outfiles,$outfile) == -1 ) { $outfiles .= " " . $outfile; } if ( index($machines_outfiles[$curr_machine],$outfile) == -1 ) { $machines_outfiles[$curr_machine] .= " " . $outfile; } #Start Running print "Executing ($curr_run/$total_runs):\n"; printf(" + Average time = %0.2f sec, Time remaining = %0.2f sec\n", $average_time, $TTF); for ($i=0; $i<$cmd_count; $i++) { print " + \$> $cmd[$i] \n"; } for ($i=0; $i<$cmd_count; $i++) { if ($pid = fork) { $pid_group[$i] = $pid; } elsif (defined $pid) { `$cmd[$i]`; exit(0); } else { die "Can't fork: $!\n"; } } $finished_count = 0; _RUN_WAIT: # Who is done? for ($i=0; $i<$cmd_count; $i++) { if ( ($pid_group[$i] >0) && (waitpid($pid_group[$i],&WNOHANG) != 0) ) { $pid_group[$i] = -1; $finished_count++; } } # Wait for parallel commands to complete. If break_on_fist is defined, # kill other commands. if ( !$break_on_first && ($finished_count < $cmd_count )) { sleep 1; goto _RUN_WAIT; } else { # At least one command must complete if ($finished_count == 0 ) { sleep 1; goto _RUN_WAIT; } for ($i=0; $i<$cmd_count;$i++) { if ($pid_group[$i] > 0 ) { print "sending kill signal to $i\n"; kill 9, $pid_group[$i]; } } } # Child must exit. if ($machines_count > 1 ) { exit(0); } _END_RUN: # Determine next file to run $done = &next_file_to_run; } _FINAL_WAIT: for($i=0; $i<$machines_count; $i++) { if (($machines_state[$i] == 1) && (waitpid($machines_pid[$i],&WNOHANG) != 0)) { $machines_state[$i] = 0; } } for($i=0; $i<$machines_count; $i++) { if ($machines_state[$i] != 0) { sleep 1; goto _FINAL_WAIT; } } print "...... Post Processing\n"; for ($i=0; $i<$machines_count; $i++) { $outfile = $in_output; $post = $in_post; # Perform variable sustitution foreach $key (sort keys(%opt_current)) { $outfile =~ s/$key/$opt_current{$key}/g; } foreach $key (sort keys(%opt_current)) { $post =~ s/$key/$opt_current{$key}/g; $outfile =~ s/$key/$opt_current{$key}/g; } $post =~ s/outfile/$outfile/g; $post =~ s/o_files/$outfiles/g; $post =~ s/m_files/$machines_outfiles[$i]/g; $post =~ s/machine/$in_machines[$i]/g; `$post`; } print "...... Bye\n"; exit(0); sub next_file_to_run { local($i) = 0; local($done2) = 0; while ( ($i <= $#opt_range) && !$done2 ) { local($key) = $opt_range[$i]; &next_val($key); if ( $opt_current{$key} == -1 ) { &next_val($key); $i++; } else { $done2 = 1; } } if ( $i > $#opt_range ) { $done = 1; return 1; } return 0; } sub next_val { local($i); local($key) = $_[0]; if ( defined $opt_range_start{$key} ) { if ($opt_current{$key} == -1) { $opt_current{$key} = $opt_range_start{$key}; } else { $opt_current{$key} += $opt_range_step{$key}; if ( $opt_current{$key} > $opt_range_stop{$key} ) { $opt_current{$key} = -1; } } } elsif ( defined $opt_list{$key} ) { @val = split(/=/, $opt_list{$key}); if ($opt_current{$key} == -1) { $opt_current{$key} = $val[0]; } else { for($i=0; $i <= $#val; $i++) { if ( ($opt_current{$key} eq $val[$i]) && ($i < $#val) ) { $opt_current{$key} = $val[$i+1]; return; } } $opt_current{$key} = -1; } } elsif ( defined $opt_val{$key} ) { if ($opt_current{$key} == -1) { $opt_current{$key} = $opt_val{$key}; } else { $opt_current{$key} = -1; } } else { print "ERROR: var name not defined\n"; exit(0); } }