Como obter tempo de execução de um script efetivamente?

341

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 do início e do fim do script. Seria possível exibir uma saída refinada, como tempo do processador / tempo io, etc?

mtk
fonte
2
stackoverflow.com/questions/385408/…
Ciro Santilli escreveu
Eu acho que a resposta atual aceita não é realmente suficiente por causa das flutuações temporais.
Léo Léopold Hertz # 25/16
1
@CiroSantilli 事件 事件 2016 六四 事件 法轮功 Acho que o tópico proposto ainda não é suficiente para eliminar o efeito de eventos temporais.
Léo Léopold Hertz # 25/16
Existe um comando que você pode executar e ele cronometra todos os comandos unix automaticamente, esqueceu como ativar?
precisa saber é o seguinte

Respostas:

450

Basta usar timequando você chamar o script:

time yourscript.sh
Trudbert
fonte
61
Isso gera três vezes: real, usere sys. Para os significados destes, veja aqui .
Garrett
27
e "real" é provavelmente o que as pessoas querem saber - "Real é a hora do relógio de parede - hora do início ao fim da ligação"
Brad Parks
3
Existe uma maneira de capturar o stdout em um arquivo? Por exemplo, algo comotime $( ... ) >> out.txt
Jon
1
Você também pode fazer isso com seqüências de comandos na linha de comando, por exemplo:time (command1 && command2)
meetar
1
Ou ele poderia, em seu script, simplesmente fazer: echo $ SECONDS assumindo que é festança ....
Crossfit_and_Beer
170

Se timenão for uma opção,

start=`date +%s`
stuff
end=`date +%s`

runtime=$((end-start))
Rob Bos
fonte
30
Observe que isso só funciona se você não precisar de precisão de segundos. Para alguns usos que podem ser aceitáveis, para outros não. Para uma precisão um pouco melhor (você ainda está invocando dateduas vezes, por exemplo, para obter, na melhor das hipóteses, precisão de milissegundos na prática e provavelmente menos), tente usar date +%s.%N. ( %Nsão nanossegundos desde o segundo inteiro.)
um CVn
Bom ponto. Pensei nisso logo após sair do teclado, mas não voltei. ^^ Lembre-se também, OP, que a própria "data" adicionará alguns milissegundos ao tempo de execução.
Rob Bos
2
@ChrisH Oh. É bom apontar; a expansão aritmética do bash é somente número inteiro. Eu vejo duas opções óbvias; ou abandone o período na cadeia de formato de data (e trate o valor resultante em nanossegundos desde a época), use-o date +%s%Nou use algo mais capaz, como bccalcular o tempo de execução real a partir dos dois valores sugeridos por jwchew. Ainda assim, acho que essa abordagem é uma maneira subótima de fazer isso; timeé consideravelmente melhor se disponível, pelas razões descritas acima.
a CVn 23/01
10
Basta instalar bce, em seguida, fazer o seguinte:runtime=$( echo "$end - $start" | bc -l )
redolent
10
Se o seu script demorar vários minutos, use:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77
75

Basta ligar timessem argumentos ao sair do seu script.

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

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

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 for 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 bash, kshe zshtem uma $SECONDSvariável especial que automaticamente é incrementado a cada segundo. Em ambos zshe ksh93, essa variável também pode ser feita de ponto flutuante (com typeset -F SECONDS) para obter mais precisão. Esta é apenas a hora do relógio de parede, não da CPU.

Stéphane Chazelas
fonte
12
Essa variável $ SECONDS é muito útil, obrigado!
andybuckley
Sua abordagem elimina o efeito de eventos temporais? - - Acho que sua abordagem é quase uma timeabordagem apresentada anteriormente. - - Acho que a timeitabordagem apresentada no código MATLAB pode ser útil aqui.
Léo Léopold Hertz # 25/16
2
Olá, @ Stéphane Chazelas. Descobri timesque não dá tempo para a parede, certo? por exemplo,bash -c 'trap times EXIT;sleep 2'
user15964 13/06/19
32

Estou um pouco atrasado para o movimento, mas queria postar minha solução (para precisão de menos de um segundo), caso outras pessoas encontrassem esse tópico através da pesquisa. A saída 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)

printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds

Espero que alguém ache isso útil!

jwchew
fonte
1
Eu acho que não há outra maneira (apenas bash), exceto usando 'bc' para fazer os cálculos. BTW realmente bom script;)
tvl
1
Cuidado que o FreeBSD datenão suporta precisão de sub-segundo e apenas anexa literal “ N” ao registro de data e hora.
Anton Samsonov
1
Um script muito bom, mas o meu bash não lida com a parte do subsegundo. Também aprendi que / 1 em bc remove efetivamente isso, então adicionei @ / 1 @ ao cálculo de $ ds e ele exibe coisas muito legais agora!
Daniel Daniel
1
bc suporta modulo: dt2=$(echo "$dt%86400" | bc). Só estou dizendo ... BTW, prefiro o formulário, dt2=$(bc <<< "${dt}%86400")mas isso é totalmente pessoal.
21918 Dani_l
16

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"
Marca
fonte
Isso mostra o tempo decorrido, mas não mostra "saída refinada, como tempo do processador, tempo de E / S", conforme solicitado na pergunta.
roaima
3
Aqueles que buscam uma resposta para a pergunta apresentada, provavelmente acharão essa solução útil. Seu comentário seria melhor direcionado à pergunta dos OPs.
Mark
5
#!/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, é uma solução concisa e agradável.

Alex
fonte
5
Lembre-se de que executar esse pythoncomando em um subshell e ler sua saída levará vários milhões de nanossegundos na maioria dos sistemas atuais. O mesmo para correr date.
Stéphane Chazelas
7
"Vários milhões de nanossegundos" são vários milissegundos. As pessoas que criam scripts bash geralmente não estão muito preocupadas com isso (a menos que executem vários bilhões de scripts por megassegundo).
Zilk
2
Observe também que, mesmo que o cálculo seja feito dentro de um processo python, os valores foram totalmente coletados antes que o python fosse chamado. O tempo de execução do script será medido corretamente, sem a sobrecarga de "milhões de nanossegundos".
Victor Schröder
5

Esta pergunta é bastante antiga, mas, ao tentar encontrar minha maneira favorita de fazer isso, esta discussão surgiu ... e estou surpreso que ninguém tenha mencionado:

perf stat -r 10 -B sleep 1

'perf' é uma ferramenta de análise de desempenho incluída no kernel em 'tools / perf' e frequentemente 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 do 'perf stat' fornece vários detalhes, incluindo o tempo médio de execução no final:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
fonte
2
O que é perf?
Camada B
@ Camada B - editei minha resposta para fornecer uma breve descrição de 'perf'.
Zaahid
1
perf statagora reclama sobre "Você pode não ter permissão para coletar estatísticas". a menos que você execute alguns comandos sudo, o que o torna inútil em todos os cenários em que você não possui completamente a máquina de destino.
Alamar
4

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

tm() {
  s=$(date +%s)
  $@
  rc=$?
  echo "took $[$(date +%s)-$s] seconds. return code $rc" >&2
  return $rc
}

Em seguida, use-o no seu script ou na sua linha de comando da seguinte maneira:

tm the_original_command with all its parameters
Evgeny
fonte
Vale a pena adicionar milissegundos, tentei o ilke s=$(date +%s000), não tenho certeza se funciona.
loretoparisi 15/03
3

Aqui está uma variação da resposta de Alex. Eu me importo apenas com minutos e segundos, mas também queria que ele fosse formatado de maneira diferente. Então eu fiz isso:

start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")
mpontillo
fonte
1
Por que o voto negativo? Eu tenho um bug?
mpontillo
3
#!/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..."
Shicheng Guo
fonte
7
Como isso é realmente diferente das outras respostas já dadas, que usam dateantes e depois do script e produzem a diferença entre elas?
Eric Renouf
2

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

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

agora você pode simplesmente imprimir a diferença:

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

se você precisar apenas de 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
gauteh
fonte
^ Esta é a resposta pessoal. Ou, mais simplesmente: $ SEGUNDOS no final. É uma variável Bash incorporada. Todas as outras respostas estão apenas fazendo um trabalho extra para reinventar esta roda ...
Crossfit_and_Beer
2

Pessoalmente, eu gosto de agrupar todo o meu 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 timecomando neste cenário. Obviamente, você não está medindo o tempo exato, incluindo o tempo de análise de script, mas acho preciso o suficiente na maioria das situações.

LeZuse
fonte
1
#!/bin/bash
begin=$(date +"%s")

Script

termin=$(date +"%s")
difftimelps=$(($termin-$begin))
echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution."
Arun Binoy
fonte
1

A função de tempo baseada em SECONDS, limitada apenas à granularidade de segundo nível, 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
codeforester
fonte