0

I have a piece of code that takes 30 hours on a supercomputer; the admin says it can use only one core at a time. It executes a piece of imaging code through a 60 by 60 matrix, with each iteration (3600 of them) running the zdichi_eps imaging program and writing data to a file. My thought is that the script below can be rewritten using the GNU PARALLEL command, and perhaps using for loops instead of the two while loops. Since I can use up to 64 cores, this would decrease the time signifidcantly I would think. Please take a look and tell me what you think. How would this be changed? Is it possible? Thanks! I would be glad to donate to any cause for the helpful individual for a working version that keeps me from fighting with the datacenter!

set tb = gau_9.tab      # tab file
set target = reformat   # target name (from the command line)
set dte = May2010v1     # date of the run.
set mult1 = 0.7958  # multiplication factor
set mult2 = `echo $mult1 | awk '{printf("%.6f",$1/2)}'`
set inclin = 40       # inclination angle
set vsin = 113.3        # vsini
set aim = 0.03221 # <chisq aim>
set tphot = 5600    # photosphere temperature
set tspot = 4100    # spot temperature
set omega0 = 11.0038    # omega_0; matched to the period
set startx = 10.8   # starting omega_eq
set stepx = 0.01    # step in omega_eq
set nx = 60         # number of datapoint for omega_eq
set starty = 0.0     # starting domega
set stepy = 0.01    # step in domega
set ny = 60     # number of datapoints for domega.
set npts = 30       # number of points used in zdichi_ep        set spec = 
$target.rs
set tab  = $tb
set b1 = $target.b1
set s1 = $target.s1
set out  = $target$aim$dte.out
set map  = $target$aim$dte.m
set junk = $target.dr_junk
set screen = $target.screen.dat
set zdi = $target.zdi.in
set om
set do

set ntot = 0
echo " " > $out 
echo " " > $junk
echo " " > $screen

echo "starting diffrot" > $target.afile

set i = 0 

while ($i < $nx) 
  set omega = `echo $startx $stepx $i | awk '{printf("%.5f",$1+$2*$3)}'`
  echo "Count i, Count ny " $i $ny
  echo "omega : " $omega
  set line
  set j = 0 
  while ($j < $ny) 
    echo "Count J Count ny " $j  $ny
    set domeg = `echo $starty $stepy $j | awk '{printf("%.5f",$1+$2*$3)}'`
    echo "1 delta omega : " $domeg
    set test = `echo $omega $domeg | awk '{tmp = $1-0.361387*$2-4.461019; 
    tmp = sqrt(tmp*tmp); if (tmp < 0.02) print 1; else print 0; }'`
    echo "test value : " $test 
    set test = 1
    if ($test == 0) then
        set spot = 9.000000
        set chi2 = 9.000000
        set testzdi = 9.00000
    else
        set beta  = `echo $omega $omega0 | awk '{printf("%e",1.0-$1/$2)}'`
        set gamma = `echo $domeg $omega0 | awk '{printf("%e",$1/$2)}'`
        echo "test, beta and gamma : " $test $beta $gamma
        echo "22 sending data to zdichi via diffrot2"
        echo "33 The data sent :" $vsin $spec $tab $mult1 $beta $aim $gamma $mult2 >> $screen

        # setting up the input file for zdichi_eps

        echo n > $zdi
        echo n >> $zdi
        echo 5000 $inclin $vsin >> $zdi
        echo n >> $zdi
        echo 1 >> $zdi
        echo $tphot $tspot >> $zdi
        echo $beta $gamma >> $zdi
        echo $spec >> $zdi
        echo $tab >> $zdi
        echo $mult1 $mult2 >> $zdi
        echo y >> $zdi
        echo 1.0 >> $zdi
        echo 1 >> $zdi
        echo y >> $zdi
        echo 1 >> $zdi
        echo $aim $npts >> $zdi
        echo $b1 >> $zdi
        echo y >> $zdi
        echo $s1 >> $zdi

        # finished setting up the input file for zdichi_eps

        /home/zdichi_eps < $zdi >> $out
        echo "44 finished with zdichi" 
        @ ntot++ 
        set chi2 = `tail -20 $out | grep '<29>' | awk '{printf("%.6f", $3)}'`
        set spot = `tail -20 $out | grep '<29>' | awk '{printf("%.6f", $5)}'`
        set testzdi = `tail -20 $out | grep '<29>' | awk '{printf("%.6f", $6)}'`
        echo "55 The returned values were: " $omega $domeg $test $chi2 $spot $testzdi >> $screen
    endif   

    if ($i == 0) then 
        set do = `echo $do $domeg`
    endif

    set line = `echo $line $chi2`
    @ j++

  end
  set om = `echo $om $omega`
  echo $line >> $junk
  @ i++
end
  • 2
    is that... csh? – ilkkachu Dec 21 '17 at 20:46
3

Fastest way to parallelize this kind of things:

  1. Write all parameter files reformat.zdi.${i}_${j}.in
  2. Call your program on every file through parallel

e.g. this:

#!/bin/bash
for f in ./reformat.zdi.*.in ;do
    echo "/home/zdichi_eps < $f > ${f%.in}.out"
done | parallel -j64
  • sorry. At the beginning I execute #!/bin/tcsh. – Gregory Perugini Dec 22 '17 at 1:50
  • @GregoryPerugini The language doesn't matter. It would be about the same with tcsh (you can replace ${f%.in}.out with just $f.out). The point is, you want to modify your script to only write parameter files then create another one to run the program inparallel – n.caillou Dec 22 '17 at 3:03
  • Or shorter: parallel '/home/zdichi_eps < {} > {.}.out' ::: reformat.zdi.* – Ole Tange Dec 22 '17 at 10:06
0

In Bash the preferred way would be to make a function that takes $i and $j as parameters:

do_zdichi() {
  i="$1"
  j="$2"
  [set all variables here]
  /home/zdichi_eps < $zdi >> $out
  [postprocess output]
}
export -f do_zdichi
eval parallel do_zdichi ::: {0..$nx} ::: {0..$ny}

You just need to make sure in the function that any file name you write to has a unique name (e.g. output.$i.$j is fine, but myoutput is not), so it is not overwritten by another process running in parallel.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.