Qual é a maneira mais rápida de executar um script?

22

Eu queria saber qual é a maneira mais rápida de executar um script, eu tenho lido que há uma diferença na velocidade entre mostrar a saída do script no terminal, redirecioná-lo para um arquivo ou talvez /dev/null.

Portanto, se a saída não for importante, qual é a maneira mais rápida de fazer com que o script funcione mais rápido, mesmo que seja mínimo.

bash ./myscript.sh 
-or-
bash ./myscript.sh > myfile.log
-or-
bash ./myscript.sh > /dev/null
Kingofkech
fonte
Comparando "redirecionando para arquivo regular" e "redirecionar para / dev / null" parece tão estranho para mim ...
el.pescado

Respostas:

31

Atualmente, os terminais estão mais lentos do que costumavam ser, principalmente porque as placas gráficas não se preocupam mais com a aceleração 2D. Portanto, a impressão em um terminal pode diminuir a velocidade de um script, principalmente quando a rolagem está envolvida.

Consequentemente, ./script.shé mais lento do que ./script.sh >script.log, o que, por sua vez, é mais lento do que /script.sh >/dev/null, porque o último envolve menos trabalho. No entanto, se isso faz diferença suficiente para qualquer finalidade prática, depende da quantidade de saída produzida pelo script e da rapidez com que ela é produzida. Se o seu script gravar 3 linhas e sair, ou se imprimir 3 páginas a cada poucas horas, você provavelmente não precisará se preocupar com redirecionamentos.

Edit: Alguns benchmarks rápidos (e completamente quebrados):

  • Em um console Linux, 240x75:

    $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done)
    real    3m52.053s
    user    0m0.617s
    sys     3m51.442s
  • Em um xterm260x78:

    $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done)
    real    0m1.367s
    user    0m0.507s
    sys     0m0.104s
  • Redirecione para um arquivo em um disco Samsung SSD 850 PRO 512GB:

     $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done >file)
     real    0m0.532s
     user    0m0.464s
     sys     0m0.068s
  • Redirecionar para /dev/null:

     $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done >/dev/null)
     real    0m0.448s
     user    0m0.432s
     sys     0m0.016s
Satō Katsura
fonte
6
@Kingofkech com menos de 200 linhas / segundo. Não faria muita diferença. (Para efeitos de comparação, timeout 1 yes "This is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line"imprime 100000+ linhas num único segundo no meu MBP.)
Muni
4
@Kingofkech Se este for um script, edite um arquivo e comente a linha que imprime saída desnecessária. Você vai ganhar muito, especialmente se este é um comando externo (não shell built-in) executada 3 milhões de vezes ...
jimmij
2
Para uma interpretação estreita de "costumava ser". Um terminal vt220 é muito mais lento que os emuladores de terminais atuais. Além disso, as estações de trabalho SUN Sparc (as que eu usei) tinham um console muito lento; portanto, apenas redirecionar a saída para um arquivo ao compilar um projeto maior aceleraria imensamente o tempo de compilação.
Kusalananda
1
@Kusalananda Isso é verdade, xtermo HP Apollo de 30 anos atrás costumava rastrear em comparação com o xtermsHP-UX de 20 anos atrás. No entanto, um console Linux com uma placa de vídeo Matrox de 15 anos atrás era mais lento que o mesmo console Linux com uma placa S3 de 20 anos atrás. E um console Linux com buffer de quadros de alta resolução em uma placa moderna é simplesmente inutilizável. :)
Satō Katsura
6
@Kingofkech São cerca de 2400 bps. Alguns de nós realmente viveram anos com essas velocidades. :)
Satō Katsura
14

Eu teria concordado instintivamente com a resposta de Satō Katsura; faz sentido. No entanto, é fácil o suficiente para testar.

Testei escrevendo um milhão de linhas na tela, escrevendo (anexando) em um arquivo e redirecionando para /dev/null. Testei cada um deles por vez e depois fiz cinco repetições. Estes são os comandos que eu usei.

$ time (for i in {1..1000000}; do echo foo; done)
$ time (for i in {1..1000000}; do echo foo; done > /tmp/file.log) 
$ time (for i in {1..1000000}; do echo foo; done > /dev/null)

Em seguida, plotei o total de vezes abaixo.

gráfico de tempo vs. saída

Como você pode ver, as presunções de Satō Katsura estavam corretas. De acordo com a resposta de Satō Katsura, também duvido que o fator limitante seja a saída, por isso é improvável que a escolha da saída tenha um efeito substancial na velocidade geral do script.

FWIW, minha resposta original tinha um código diferente, que tinha o arquivo anexado e /dev/nullredirecionado dentro do loop.

$ rm /tmp/file.log; touch /tmp/file.log; time (for i in {1..1000000}; do echo foo >> /tmp/file.log; done) 
$ time (for i in {1..1000000}; do echo foo > /dev/null; done)

Como John Kugelman aponta nos comentários, isso acrescenta muita sobrecarga. Como a pergunta está, essa não é realmente a maneira correta de testá-la, mas vou deixá-la aqui, pois mostra claramente o custo de reabrir um arquivo repetidamente de dentro do próprio script.

gráfico de tempo vs. saída

Nesse caso, os resultados são revertidos.

Sparhawk
fonte
FWIW Adicionei uma referência rápida à minha resposta. Notavelmente, o console Linux é> 200 vezes mais lento que um xterm, que por sua vez é ~ 3 vezes mais lento que /dev/null.
Satō Katsura
Você também deve testar com alguns limites de taxa. A saída do OP é de cerca de 200 linhas / s.
Muru
@muru Você quer dizer imprimir uma linha, esperar 1/200 segundos e depois repetir? Eu posso tentar, mas acho que serão resultados semelhantes, mas demore muito mais tempo para o sinal superar o ruído. Embora talvez eu possa subtrair o tempo de espera antes da análise.
Sparhawk #
@ Sparhawk algo assim. Eu acho que nesse nível de saída, a CPU terá tempo de sobra para atualizar a tela sem diminuir a taxa de saída. Quando o programa não faz nada além de vomitar linhas sem pausa, os buffers do terminal são preenchidos mais rapidamente do que a tela pode ser atualizada e cria um gargalo.
muru 13/10
3

Outra maneira de acelerar um script é usar um interpretador de shell mais rápido. Comparar as velocidades de um POSIX circuito ocupado, executados sob bash v4.4 , ksh v93u + 20120801 , e dash v0.5.8 .

  1. bash:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | bash -s > /dev/null

    Saída:

    real    0m25.146s
    user    0m24.814s
    sys 0m0.272s
  2. ksh:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | ksh -s > /dev/null

    Saída:

    real    0m11.767s
    user    0m11.615s
    sys 0m0.010s
  3. dash:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | dash -s > /dev/null

    Saída:

    real    0m4.886s
    user    0m4.690s
    sys 0m0.184s

Um subconjunto de comandos bashe kshé retrocompatível com todos os comandos em dash. Um bashscript que usa apenas comandos nesse subconjunto deve funcionar dash.

Alguns bashscripts que usam novos recursos podem ser convertidos para outro intérprete. Se o bashscript depende muito de recursos mais recentes, pode não valer a pena - alguns novos bashrecursos são aprimoramentos mais fáceis de codificar e mais eficientes (apesar de bashgeralmente serem mais lentos), de modo que o dashequivalente (que pode envolver a execução vários outros comandos), seria mais lento.

Em caso de dúvida, execute um teste ...

agc
fonte
Então, para acelerar meu script bash, preciso reescrevê-los no bash ou no ksh?
Kingofkech
@Kingofkech Pode ser que o código que você escreveu para o bash esteja prontamente correto no código ksh ou dash. Tente apenas mudar o intérprete.
bli