Como obter o tempo de execução de um script de forma eficaz?

Eu gostaria de exibir o tempo de conclusão de um script.

O que eu faço atualmente é –

#!/bin/bash date ## echo the date at start # the script contents date ## echo the date at end 

Isso mostra apenas a hora de início e término do script. Seria possível exibir uma saída granulada, como tempo do processador / tempo io, etc?

Comentários

Resposta

Jus t use time ao chamar o script:

time yourscript.sh 

Comentários

  • Produz três vezes: real, user e sys. Para saber seus significados, consulte aqui .
  • e ” real ” é provavelmente o que as pessoas querem saber – ” Real é a hora do relógio de parede – hora do início ao fim da chamada ”
  • Existe uma maneira de capturar o stdout em um arquivo? Por exemplo, algo como time $( ... ) >> out.txt
  • Você também pode fazer isso com sequências de comandos na linha de comando, por exemplo: time (command1 && command2)
  • Ou ele poderia, em seu script, apenas fazer: echo $ SECONDS presumindo que seja bash ….

Resposta

Se time não for uma opção,

start=`date +%s` stuff end=`date +%s` runtime=$((end-start)) 

Comentários

  • Observe que isso só funciona se você não ‘ precisar de precisão de menos de um segundo. Para alguns usos, isso pode ser aceitável , para outros não. Para uma precisão ligeiramente melhor (você ‘ ainda está invocando date duas vezes, por exemplo, então você pode melhor obter precisão de milissegundos na prática, e provavelmente menos), tente usar date +%s.%N. (%N é nanossegundos desde o segundo inteiro .)
  • @ChrisH Oh. Bom apontar isso; a expansão aritmética bash é apenas para números inteiros. Vejo dois óbvios ous opções; elimine o período na string de formato de data (e trate o valor resultante como nanossegundos desde a época), então use date +%s%N ou use algo mais capaz como bc para calcular o tempo de execução real a partir dos dois valores, como sugere o jwchew. Ainda assim, acho que essa abordagem é uma maneira subótima de fazer isso; time é consideravelmente melhor se disponível, pelos motivos descritos acima.
  • Basta instalar bc e fazer o seguinte: runtime=$( echo "$end - $start" | bc -l )
  • Se o seu script levar vários minutos, use: echo "Duration: $((($(date +%s)-$start)/60)) minutes
  • Na sequência de @ rubo77 comentário, se o seu script demorar várias horas, use hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); echo "Runtime: $hours:$minutes:$seconds (hh:mm:ss)"

Resposta

Basta chamar times sem argumentos ao sair de seu script.

Com ksh ou zsh, você também pode usar time. Com zsh, time também fornecerá a hora do relógio de parede, além do usuário e sistema tempo de CPU.

Para preservar o status de saída do seu script, você pode fazê-lo:

ret=$?; times; exit "$ret" 

Ou você também pode adicionar uma armadilha em EXIT:

trap times EXIT 

Dessa forma, os horários serão chamados sempre que o shell sair e o status de saída será preservado.

$ bash -c "trap times EXIT; : {1..1000000}" 0m0.932s 0m0.028s 0m0.000s 0m0.000s $ zsh -c "trap time EXIT; : {1..1000000}" shell 0.67s user 0.01s system 100% cpu 0.677 total children 0.00s user 0.00s system 0% cpu 0.677 total 

Observe também que todos os bash, ksh e zsh têm uma $SECONDS variável especial que é incrementada automaticamente a cada segundo. Em zsh e ksh93, essa variável também pode ser transformada em ponto flutuante (com typeset -F SECONDS ) para obter mais precisão. Este é apenas o tempo do relógio de parede, não o tempo da CPU.

Comentários

  • Essa variável $ SECONDS é muito útil, obrigado!
  • Sua abordagem elimina o efeito de eventos temporais? – Acho que sua abordagem está próxima da time abordagem apresentada anteriormente.- – Acho que a abordagem timeit apresentada no código MATLAB pode ser útil aqui.
  • Olá, @St é phane Chazelas. Descobri que times não ‘ dá tempo de parede, certo? por exemplo, bash -c 'trap times EXIT;sleep 2'
  • @Binarus, sim, esse recurso vem de ksh. Ao contrário de ksh93 / zsh, além de bash não suportar ponto flutuante, ele ‘ também está quebrado, pois depois de definir SECONDS como 0, ele mudará para 1 de 0 a 1 segundo mais tarde ( uma vez que considera apenas o resultado de time(), que tem apenas uma segunda granularidade completa).
  • @Binarus, não que ‘ s entre 0 e 1. Se você definir SECONDS como 0 às 12: 00: 00.000, mudará para 1 um segundo depois. Mas se você defini-lo para 12h00,999, ele mudará para 1 um milissegundo depois. Mas sim, concordo que provavelmente não ‘ importa muito na prática se você não ‘ não tiver precisão inferior a um segundo de qualquer maneira.

Resposta

Estou um pouco atrasado para o movimento, mas queria postar minha solução (para precisão de sub-segundos ) caso outras pessoas encontrem este tópico durante a pesquisa. O resultado está no formato de dias, horas, minutos e, finalmente, segundos:

res1=$(date +%s.%N) # do stuff in here res2=$(date +%s.%N) dt=$(echo "$res2 - $res1" | bc) dd=$(echo "$dt/86400" | bc) dt2=$(echo "$dt-86400*$dd" | bc) dh=$(echo "$dt2/3600" | bc) dt3=$(echo "$dt2-3600*$dh" | bc) dm=$(echo "$dt3/60" | bc) ds=$(echo "$dt3-60*$dm" | bc) LC_NUMERIC=C printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds 

Espero que alguém lá fora acha isso útil!

Comentários

  • Acho que não há outra maneira (apenas bash), exceto usando ‘ bc ‘ para fazer os cálculos. BTW realmente bom script;)
  • Cuidado com o FreeBSD ‘ s date não oferece suporte à precisão de subsegundos e apenas acrescentará literal “N” ao carimbo de data / hora.
  • Script muito bom, mas meu bash não ‘ para lidar com a parte do subsegundo. Eu também aprendi que / 1 em bc efetivamente elimina isso, então adicionei @ / 1 @ ao cálculo de $ ds e ele exibe coisas muito boas agora!
  • bc suporta módulo: dt2=$(echo "$dt%86400" | bc). Apenas dizendo … BTW, eu prefiro a forma dt2=$(bc <<< "${dt}%86400"), mas que ‘ é totalmente pessoal.
  • Eu não ‘ não sei nada sobre bc ainda, mas a divisão por 86400 me deixa desconfiado do seguinte motivo: Existem dias que não têm 86400 segundos, por exemplo, dias em que a hora é alterada de DST para a hora normal e vice-versa, ou dias com segundos intercalados. Se esses dias fizerem parte do intervalo de tempo que você deseja calcular, ou seja, entre res1 e res2 (incluindo esses valores), seu script provavelmente falhará.

Resposta

Meu método para bash :

# Reset BASH time counter SECONDS=0 # # do stuff # ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec" 

Comentários

  • Isso mostra o tempo decorrido, mas não ‘ t show ” saída refinada, como tempo do processador, tempo de E / S ” conforme solicitado na pergunta.
  • Aqueles que procuram uma resposta para a questão apresentada provavelmente acharão esta solução útil. Seu comentário seria melhor endereçado à pergunta dos OPs.

Resposta

Pessoalmente, gosto de embrulhar todos os meus código de script em alguma função “principal” como:

main () { echo running ... } # stuff ... # calling the function at the very end of the script time main 

Observe como é fácil usar o comando time Neste cenário. Obviamente, você não está medindo o tempo preciso, incluindo o tempo de análise do script, mas acho que é preciso o suficiente na maioria das situações.

Comentários

  • Ótima solução, não ‘ não requer ferramentas extras e é independente.

Resposta

#!/bin/bash start=$(date +%s.%N) # HERE BE CODE end=$(date +%s.%N) runtime=$(python -c "print(${end} - ${start})") echo "Runtime was $runtime" 

Sim, isso chama Python, mas se você pode conviver com isso, então esta é uma solução muito boa e concisa.

Comentários

  • Esteja ciente de que executar esse comando python em um subshell e ler sua saída levará vários milhões de nanossegundos na maioria dos sistemas atuais. O mesmo para executar date.
  • ” Vários milhões de nanossegundos ” são vários milissegundos . Pessoas cronometrando scripts bash geralmente não se preocupam muito com isso (a menos que executem vários bilhões de scripts por megassegundo).
  • Observe também que, mesmo que o o cálculo é feito dentro de um processo python, os valores foram inteiramente coletados antes de python ser chamado. O tempo de execução do script será medido corretamente, sem os ” milhões de nanossegundos ” sobrecarga.
  • time main acima é muito mais limpo …sim, podemos viver com muita degradação, mas ‘ é muito melhor sem que ‘ continuem engenheiros!
  • $(echo $end - $start | bc) fará a mesma coisa sem python

Resposta

Esta pergunta é bastante antiga, mas ao tentar encontrar minha maneira favorita de fazer isso, este tópico surgiu alto … e estou surpreso que ninguém tenha mencionado isso:

perf stat -r 10 -B sleep 1 

“perf” é uma ferramenta de análise de desempenho incluída no kernel em “tools / perf” e geralmente disponível para instalação como um pacote separado (“perf” no CentOS e “linux-tools” no Debian / Ubuntu). O Linux Kernal perf Wiki tem muito mais informações sobre ele.

A execução de “perf stat” fornece alguns detalhes, incluindo o tempo médio de execução bem no final:

1.002248382 seconds time elapsed ( +- 0.01% ) 

Comentários

  • O que é perf?
  • @B Layer – editou minha resposta t o dê uma breve descrição de ‘ perf ‘.
  • perf stat agora reclama sobre ” Você pode não ter permissão para coletar estatísticas. ” a menos que você execute alguns comandos com sudo, o que o torna inútil em todos os cenários em que você não ‘ não possui completamente a máquina de destino.
  • Incrível, obrigado! Solução muito mais refinada do que time outras sugerindo. Em particular, time não é aplicável para medir soluções de competição de código (uma vez que não ‘ tempo de impressão em milissegundos e menos) , enquanto sua solução sim.

Resposta

Uma pequena função shell que pode ser adicionada antes comandos para medir seu tempo:

tm() { local start=$(date +%s) $@ local exit_code=$? echo >&2 "took ~$(($(date +%s)-${start})) seconds. exited with ${exit_code}" return $exit_code } 

Em seguida, use-o em seu script ou em sua linha de comando como:

tm the_original_command with all its parameters 

Comentários

  • Valeria a pena adicionar milissegundos, tentei ilke s=$(date +%s000), não tenho certeza se funcionar.
  • @loretoparisi por milissegundos, você pode tentar date +%s%N. Consulte serverfault.com/questions/151109/…
  • Isso é ótimo, exceto mais seguro use "$@"

Resposta

#!/bin/csh #PBS -q glean #PBS -l nodes=1:ppn=1 #PBS -l walltime=10:00:00 #PBS -o a.log #PBS -e a.err #PBS -V #PBS -M [email protected] #PBS -m abe #PBS -A k4zhang-group START=$(date +%s) for i in {1..1000000} do echo 1 done END=$(date +%s) DIFF=$(echo "$END - $START" | bc) echo "It takes DIFF=$DIFF seconds to complete this task..." 

Comentários

  • Como isso é realmente diferente das outras respostas já fornecidas, que usam date antes e depois o script e mostrar a diferença entre eles?

Resposta

  1. Apenas use time [any command] . Ex: time sleep 1 ficará hibernando por um tempo real (isto é: conforme cronometrado por um cronômetro) de ~ 1.000 a ~ 1.020 seg, conforme mostrado aqui:

      $ time sleep 1 real 0m1.011s user 0m0.004s sys 0m0.000s  

    Que coisa linda. Você pode colocar qualquer comando após ele, e ele exibe o resultado em um formato agradável e legível. Eu realmente gosto de usá-lo para cronometragem. Ex:

      # time your "make" build time make # time your "Bazel" build time bazel build //path/to/some:target  

    … ou para operações git que podem ser realmente longo, para que eu possa desenvolver expectativas mentais realistas:

      # time how long it takes to pull from a massive repo when # I"m working from home during COVID-19. NB: `git pull` # is sooooo much slower than just pulling the one branch # you need with `git pull origin <branch>`, so just fetch # or pull what you need! time git pull origin master  
  2. Para necessidades de tempo mais personalizadas onde você pode precisar manipular a saída ou convertê-la em outras formas, em bash , use a variável $SECONDS interna. Aqui está uma demonstração, incluindo a conversão desses segundos em outras unidades, como minutos de ponto flutuante:

    Observe que dt_min é arredondado de 0.01666666666... (1 segundo = tantos minutos) para 0.017 neste caso, já que estou usando a função printf para arredondar. A sleep 1; parte abaixo é onde você chamaria seu script para executar e cronometrar, mas estou apenas dormindo por 1 segundo por causa desta demonstração.

    Comando:

      start=$SECONDS; sleep 1; end=$SECONDS; dt_sec=$(( end - start )); \ dt_min=$(printf %.3f $(echo "$dt_sec/60" | bc -l)); \ echo "dt_sec = $dt_sec; dt_min = $dt_min"  

    Resultado:

      dt_sec = 1; dt_min = 0.017  

Relacionado:

  1. Leia mais sobre bc e printf em minha resposta aqui: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Não me lembro mais onde aprendi sobre o comando time, mas pode ter sido de Resposta de @Trudbert aqui .

Resposta

Usando somente bash também é possível medir e calcular a duração de uma parte do script de shell (ou o tempo decorrido para todo o script):

start=$SECONDS ... # do time consuming stuff end=$SECONDS 

agora você pode apenas imprimir a diferença:

echo "duration: $((end-start)) seconds." 

se você precisar apenas da duração incremental, faça:

echo "duration: $((SECONDS-start)) seconds elapsed.." 

Você também pode armazenar a duração em uma variável:

let diff=end-start 

Comentários

  • ^ ESTA é a resposta, pessoal. Ou mais simplesmente: $ SECONDS no final. É ‘ uma variável Bash INTEGRADA. Todas as outras respostas estão apenas fazendo um trabalho extra para reinventar esta roda …
  • Esta é apenas uma repetição da resposta anterior de Marcos unix.stackexchange .com / a / 340156/155362 .

Resposta

Função de tempo baseada em SECONDS, limitado à granularidade de segundo nível apenas, não usa nenhum comando externo:

time_it() { local start=$SECONDS ts ec printf -v ts "%(%Y-%m-%d_%H:%M:%S)T" -1 printf "%s\n" "$ts Starting $*" "$@"; ec=$? printf -v ts "%(%Y-%m-%d_%H:%M:%S)T" -1 printf "%s\n" "$ts Finished $*; elapsed = $((SECONDS-start)) seconds" # make sure to return the exit code of the command so that the caller can use it return "$ec" } 

Por exemplo:

time_it sleep 5 

2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished sleep 5; elapsed = 5 seconds 

Resposta

Usar bash time builtin?

time: time [-p] PIPELINE Execute PIPELINE and print a summary of the real time, user CPU time, and system CPU time spent executing PIPELINE when it terminates. The return status is the return status of PIPELINE. The `-p" option prints the timing summary in a slightly different format. This uses the value of the TIMEFORMAT variable as the output format. 

Exemplo:

TIMEFORMAT="The command took %Rs" time { sleep 0.1 } 

Resultado:

The command took 0.108s 

Resposta

Aqui “uma variação de Alex “s resposta. Eu só me importo com minutos e segundos, mas também queria formatado de forma diferente. Então fiz isso:

start=$(date +%s) end=$(date +%s) runtime=$(python -c "print "%u:%02u" % ((${end} - ${start})/60, (${end} - ${start})%60)") 

Comentários

  • Por que o voto negativo? Tenho um bug?
  • Usar python para esse cálculo também parece um bug para mim, desculpe cara. Por que as pessoas podem ‘ t apenas ligar para time está além da minha compreensão.

Resposta

#!/bin/bash begin=$(date +"%s") Script termin=$(date +"%s") difftimelps=$(($termin-$begin)) echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution." 

Resposta

A solução aceita usando time grava em stderr.
A solução usando times grava em stdout.
As soluções que usam $SECONDS não têm precisão de sub-segundos.
As outras soluções envolvem chamar programas externos como date ou perf que não é eficiente.
Se você estiver satisfeito com algum desses, use-o.

Mas se você precisar de uma solução eficiente para obter os tempos com precisão de milissegundos e precisa deles em variáveis de modo que a saída original permanece inalterada você pode combinar a substituição do processo com alguns redirecionamentos em torno de time, o que é muito mais rápido do que chamar programas externos e permite redirecionamentos em torno do script de wrapper de tempo como no comando / script original.

# Preparations: Cmd=vgs # example of a program to be timed which is writing to stdout and stderr Cmd="eval { echo stdout; echo stderr >&2; sleep 0.1; }" # other example; replace with your own TIMEFORMAT="%3R %3U %3S" # make time output easy to parse 

Selecione uma das seguintes variantes analisando a saída de time apropriado para suas necessidades:
Variante mais curta em que stdout de $Cmd é gravado em stderr e nada a stdout:

read Elapsed User System < <({ time $Cmd 2>&3; } 3>&2 2>&1 >&3) 

Variante mais longa que mantém originais stdout e stderr separados uns dos outros:

{ read Elapsed User System < <({ time $Cmd 2>&4; } 4>&2 2>&1 >&3); } 3>&1 

A variante mais complicada inclui o fechamento dos descritores de arquivo extras, de forma que $Cmd seja chamado sem este wrapper de tempo em torno dele e lvm comandos como vgs não reclamam sobre descritores de arquivo vazados:

{ read Elapsed User System < <({ time $Cmd 2>&4 4>&-; } 4>&2 2>&1 >&3 3>&-); } 3>&1 

Você pode até simular uma adição de ponto flutuante em bash sem chamar bc, o que seria muito mais lento:

CPU=`printf %04d $((10#${User/.}+10#${System/.}))` # replace with your own postprocessing echo CPU ${CPU::-3}.${CPU: -3} s, Elapsed $Elapsed s >&2 # redirected independent of $Cmd 

Possíveis saídas com os dois exemplos de $Cmd em uma CPU lenta:

File descriptor 3 (/dev/pts/1) leaked on vgs invocation. Parent PID 10756: bash File descriptor 4 (/dev/pts/1) leaked on vgs invocation. Parent PID 10756: bash VG #PV #LV #SN Attr VSize VFree b3 3 24 0 wz--n- 1.31t 1.19t CPU 0.052 s, Elapsed 0.056 s 

Ou:

stdout stderr CPU 0.008 s, Elapsed 0.109 s 

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *