eliminate subshells for faster process?

316 Views Asked by At

I've read that scripts that are calling for a subshell are slow, which would explain why my script are slow.

for example here, where I'm running a loop that gets an number from an array, is this running a subshell everytime, and can this be solved without using subshells?

mmode=1
modes[1,2]="9,12,18,19,20,30,43,44,45,46,47,48,49"
until [[ -z $kik ]];do
    ((++mloop))
    kik=$(echo  ${modes[$mmode,2]} | cut -d "," -f $mloop)
    
    filename=$(basename "$f")
    # is all these lines
    
    xcolorall=$((xcolorall+$storednr)
    # also triggering
    
    pros2=$(echo "100/$totpix*$xcolorall" | bc -l) 
    IFS='.' read -r pros5 pros6 <<< "$pros2"
    procenthittotal2=$pros5.${pros6:0:2}
            
    #subshells and if,
    # is it possible to circumvent it?  
    #and more of the same code..
done

updated: the pros2 variable is calculating percent, how many % xcolorall are of totpix and the kik variable is getting a number from the array modes, informing the loop about what color it should count in this loop. I suspect these are the main hoggers, is there anyway to do this without subshells?

1

There are 1 best solutions below

8
On BEST ANSWER

You can replace all the subshells and extern commands shown in your question with bash built-ins.

  • kik=$(echo ${modes[$mmode,2]} | cut -d "," -f $mloop) can be replaced by
    mapfile -d, -t -s$((mloop-1)) -n1 kik <<< "${modes[$mmode,2]}".
    If $mmode is constant here, better replace the whole loop with
    while IFS=, read -r kik; do ...; done <<< "${modes[$mmode,2]}".
  • filename=$(basename "$f") can be replaced by
    filename=${f##*/} which runs 100 times faster, see benchmark.
  • pros2=$(echo "100/$totpix*$xcolorall" | bc -l) can be replaced by
    (( pros2 = 100 * xcolorall / totpix )) if you don't care for the decimals, or by
    precision=2; (( pros = 10**precision * 100 * xcolorall / totpix )); printf -v pros "%0${precision}d" "$pros"; pros="${pros:0: -precision}.${pros: -precision}" if you want 2 decimal places.
    Of course you can leave out the last commands (for turning 12345 into 123.45) until you really need the decimal number.

But if speed really matters, write the script in another language. I think awk, perl, or python would be a good match here.