desempenho de preenchimento de cadeia em scripts de shell: qual é a melhor quantidade e para qual shell?

1

Eu estava tentando determinar o melhor desempenho para o preenchimento de sequência de caracteres, como em:% str+="A" #one por loop

Eu vim com este script para o bash:

#!/bin/bash
bReport=false
nLimit=${1-3000}; #up to 25000

echo "nLimit='$nLimit'"
shopt -s expand_aliases
nStop=100000;fMaxWorkTime=1.0;
alias GetTime='date +"%s.%N"';
nTimeBegin="'GetTime'";
nDelayPart="'GetTime'";
strFinal="";
str="";
fPartWorkSleep="'bc <<< "scale=10;($fMaxWorkTime/$nStop)*$nLimit"'"
echo "fPartWorkSleep='$fPartWorkSleep'"
nCount=0;
while true;do 
    str+="A";
    ((nCount++))&&:;
    if(((nCount%nLimit)==0)) || ((nCount==nStop));then 
        strFinal+="$str";
        str="";
        if $bReport;then
            echo "'bc <<< "$(GetTime)-$nDelayPart"' #${#strFinal} #'bc <<< "$(GetTime)-$nTimeBegin"'";
            nDelayPart="'GetTime'";
        fi
        sleep $fPartWorkSleep # like doing some weigthy thing based on the amount of data processed
    fi;
    if((nCount==nStop));then 
        break;
    fi;
done;
echo "strFinal size ${#strFinal}"
echo "took 'bc <<< "$(GetTime)-$nTimeBegin"'"

E no bash, o melhor desempenho / tamanho é quando str é limitado de 3000 a 25000 caracteres (na minha máquina). Depois que cada parte é preenchida, ela deve ser esvaziada e alguma ação global pode ser executada com o valor str (e o peso é relativo ao seu tamanho).

Então, minha pergunta é: qual shell tem o melhor desempenho de preenchimento de string? com base no que expus. Estou disposto a usar outro shell do que o bash, apenas para esse tipo de algoritmo, se ele for mais rápido.

PS .: Eu tive que usar o nCount como verificações no desempenho degradado do tamanho da string.

    
por Aquarius Power 21.12.2015 / 02:44

1 resposta

2

for sh  in bash zsh yash dash mksh ksh
do      printf  "\n%s:\t" "$sh"
        time    "$sh" -c '
                        str="some string"
                        set     "" ""
                        while   ${20001+"break"}
                        do      set "$@$@";done
                        IFS=A;  printf %.100000s\n "$str$*$*$*$*$*"'|
                wc -c
done
bash:   100001
"$sh" -c   0.15s user 0.01s system 94% cpu 0.176 total
wc -c  0.00s user 0.00s system 1% cpu 0.175 total

zsh:    100001
"$sh" -c   0.03s user 0.01s system 97% cpu 0.034 total
wc -c  0.00s user 0.00s system 9% cpu 0.034 total

yash:   100001
"$sh" -c   0.06s user 0.01s system 94% cpu 0.067 total
wc -c  0.00s user 0.00s system 5% cpu 0.067 total

dash:   100001
"$sh" -c   0.02s user 0.01s system 92% cpu 0.029 total
wc -c  0.00s user 0.00s system 11% cpu 0.028 total

ksh:    100001
"$sh" -c   0.02s user 0.00s system 96% cpu 0.021 total
wc -c  0.00s user 0.00s system 16% cpu 0.021 total

Portanto, isso compensa os vários shells configurados como $sh no loop for na rapidez com que eles podem gerar uma string de 100.000 caracteres. Os primeiros 11 desses 100.000 caracteres são some string como primeiro definido para o valor de $str , mas o preenchimento final é de 999.989 A caracteres.

Os shells obtêm os caracteres A em $* , que substitui o primeiro caractere no valor do parâmetro especial do shell $IFS como um delimitador de concatenação entre cada parâmetro posicional no argumento do shell array. Como todos os argumentos são "" null, os únicos caracteres em $* são os caracteres delimitadores.

Os argumentos são acumulados em uma taxa exponencial para cada iteração do loop while - que somente break s quando o parâmetro $20001 foi finalmente ${set+} . Até lá, basicamente o loop while faz:

### first iteration
while $unset_param; do set "" """" ""; done
### second iteration
while $unset_param; do set "" "" """" "" ""; done
### third iteration
while $unset_param; do set "" "" "" "" """" "" "" "" ""; done

... e assim por diante.

Após o loop while concluir o $IFS ser definido como A , o parâmetro especial do shell $* será concatenado cinco vezes para a final de $str . printf reduz o argumento %s tring resultante para um máximo de .100000 bytes antes de gravá-lo no stdout.

Pode-se usar a mesma estratégia como:

str='some string'
set "" ""
while ${51+"break"}; do set "$@$@"; done
shift "$((${#}-(51-${#str}))"

... o que resulta em uma contagem total de argumentos de 40 - e, portanto, 39 delimitadores ...

IFS=.; printf %s\n "$str$*"
some string.......................................

E você pode reutilizar os mesmos argumentos que já definiu com um% diferente% co_de para um preenchimento diferente:

for IFS in a b c; do printf %s\n "$str$*"; done
some stringaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
some stringbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
some stringccccccccccccccccccccccccccccccccccccccc

Você também pode preencher os argumentos nulos com uma string de formato $IFS em vez de usar printf :

printf "%s m%sy%1ss%st%sr%si%sn%sg" "$str$@"
some string my string my string my string my string my string
    
por 21.12.2015 / 03:54