Alternativa universal de referência não-bash `time`? [fechadas]


10

Para comparar tempos de execução de scripts de entre diferentes conchas, algumas respostas SE sugerir o uso bashde alto- time comando, assim:

time bash -c 'foo.sh'
time dash -c 'foo.sh'

... etc , para cada shell testar. Esses benchmarks falham em eliminar o tempo necessário para cada shell carregar e inicializar a si próprio . Por exemplo, suponha que ambos os comandos acima foram armazenados em um dispositivo lento com a velocidade de leitura de um disquete inicial (124KB / s), dash(um executável de ~ 150K ) carregaria cerca de 7x mais rápido que bash( ~ 1M ), o shell o tempo de carregamento distorceria os timenúmeros - os tempos de pré-carregamento dessas conchas eram irrelevantes para medir os tempos de execução foo.shsob cada concha após o carregamento das conchas.

Qual é a melhor util portátil e geral para correr para o sincronismo script que pode ser executado a partir de dentro de cada shell? Portanto, o código acima seria algo como:

bash -c 'general_timer_util foo.sh'
dash -c 'general_timer_util foo.sh'

NB: no shell built-in time comandos, uma vez que nenhum são portáteis ou geral.


Melhor ainda, se o utilitário também puder avaliar o tempo gasto pelos comandos e pipelines internos de um shell, sem que o usuário precise primeiro envolvê-los em um script. Sintaxe artificial como esta ajudaria:

general_timer_util "while read x ; do echo x ; done < foo"

Algumas conchas timepodem gerenciar isso. Por exemplo bash -c "time while false ; do : ; done"funciona. Para ver o que funciona (e não funciona) no seu sistema, tente:

tail +2 /etc/shells | 
while read s ; do 
    echo $s ; $s -c "time while false ; do : ; done" ; echo ----
done

6
Basta usar /usr/bin/time?
Kusalananda

1
Eu não entendo como qualquer não-interno poderia "eliminar o tempo necessário para cada shell carregar e inicializar a si mesmo" e executar um script independente enquanto "portátil e geral".
Michael Homer

1
Isso não é uma resposta para a pergunta, é um prompt para você esclarecer o que deseja.
Michael Homer

1
Publiquei minha melhor tentativa, mas acho que a pergunta ainda está subespecificada sobre exatamente o que realmente está tentando alcançar.
Michael Homer

2
O que você quer dizer com "portátil ou geral"? Os componentes internos do shell são tão portáteis (funcionam em tantos sistemas) e mais gerais (funcionam em mais circunstâncias, pois podem cronometrar algo diferente da execução de um arquivo) como o comando externo. Que problema você está tentando resolver?
Gilles 'SO- stop be evil' em

Respostas:


10

Você deve observar que timeé especificado pelo POSIX e AFAICT, a única opção mencionada pelo POSIX ( -p) é suportada corretamente por vários shells:

$ bash -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ dash -c 'time -p echo'

real 0.01
user 0.00
sys 0.00
$ busybox sh -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ ksh -c 'time -p echo'       

real 0.00
user 0.00
sys 0.00

1
O problema é que, para poder comparar horários, o resultado deve ser programado pela mesma implementação de time. É comparável a deixar os velocistas medirem seu próprio tempo em 100m, individualmente, em vez de fazê-lo com um relógio ao mesmo tempo. Esta é, obviamente, picuinhas, mas ainda assim ...
Kusalananda

@Kusalananda Eu pensei que o problema era que o OP timenão era portátil; parece ser portátil. (Eu concordo com o seu ponto sobre a comparabilidade, embora)
Muru

@muru, no meu sistema dash -c 'time -p while false ; do : ; done'retorna "time: não pode ser executado enquanto: Não existe nenhum arquivo ou diretório <cr> Comando encerrado com erros de status diferente de zero 127" .
agc 01/01

1
@agc POSIX também diz: "O termo utilitário é usado, em vez de comando, para destacar o fato de que comandos compostos de shell, pipelines, built-ins especiais etc. não podem ser usados ​​diretamente. No entanto, o utilitário inclui programas de aplicativos do usuário e scripts de shell, não apenas os utilitários padrão ". (consulte a seção RACIONAL)
muru


7

Eu uso o comando GNU date , que suporta um timer de alta resolução:

START=$(date +%s.%N)
# do something #######################

"$@" &> /dev/null

#######################################
END=$(date +%s.%N)
DIFF=$( echo "scale=3; (${END} - ${START})*1000/1" | bc )
echo "${DIFF}"

E então eu chamo o script assim:

/usr/local/bin/timing dig +short unix.stackexchange.com
141.835

A unidade de saída está em milissegundos.


1
Supondo que o tempo (período da época) não seja alterado. Não consigo pensar em um caso na prática em que isso causaria um problema, mas ainda vale a pena mencionar.
Phk # 1/17

1
Você deve adicionar que isso requer o GNU dateespecificamente.
Kusalananda

@phk por favor explique?
Rabin

1
@ Rabin Digamos que seu cliente NTP emita e atualize e altere seu relógio entre onde STARTe ENDestá definido; isso obviamente afetaria seu resultado. Não faço ideia do quão preciso você precisa e se importa no seu caso, mas como eu disse, algo para se ter em mente. (História engraçada: Eu conheço um software onde exatamente ele levou a um resultado inesperadamente negativos - foi usado para rendimento cálculos - que, em seguida, quebrou algumas coisas.)
PHK

1
Além disso, alguns clientes NTP não diminuem a velocidade e aceleram o relógio, em vez de fazer "saltos" na hora do sistema? Se você tem um cliente NTP como esse e fez alguns horários ontem à noite, eles provavelmente são distorcidos pelo cliente NTP "antecipando" o segundo salto. (Ou o relógio do sistema simplesmente executar a 61, nesse caso?)
Jörg W Mittag

6

O timeutilitário geralmente é incorporado ao shell, como você notou, o que o torna inútil como um temporizador "neutro".

No entanto, o utilitário geralmente também está disponível como um utilitário externo /usr/bin/time, que pode muito bem ser usado para executar as experiências de tempo que você propõe.

$ bash -c '/usr/bin/time foo.sh'

Como isso "elimina o tempo necessário para cada shell carregar e inicializar a si próprio"?
Michael Homer

1
Se foo.shé executável e tem um shebang, isso sempre o executa com o mesmo shell e conta o tempo de inicialização desse shell, portanto, não é isso que o OP deseja. Se foo.shestiver faltando um desses, isso não funcionará.
Kevin

@ Kevin Muito verdade. Eu só levei "sem shell embutido time" em consideração, parece. O tempo de inicialização do shell pode precisar ser medido separadamente.
Kusalananda

1
Não conheço nenhum shell que possua um timecomando interno. No entanto, muitas conchas, incluindo bashuma timepalavra - chave, podem ser usadas para cronometrar pipelines. Para desativar essa palavra-chave para que o timecomando (no sistema de arquivos) seja usado, você pode citá-la como "time" foo.sh. Veja também unix.stackexchange.com/search?q=user%3A22565+time+keyword
Stéphane Chazelas

6

Aqui está uma solução que:

  1. elimina [s] o tempo necessário para cada shell carregar e inicializar a si próprio

  2. pode ser executado dentro de cada shell

  3. Usos

    sem comandos internos do shell time, pois nenhum é portátil ou geral

  4. Funciona em todos os shells compatíveis com POSIX.
  5. Funciona em todos os sistemas compatíveis com POSIX e XSI com um compilador C , ou onde você pode compilar um executável C com antecedência.
  6. Usa a mesma implementação de tempo em todos os shells.

Há duas partes: um pequeno programa C que termina gettimeofday, que é preterido, mas ainda mais portátil que um clock_gettime, e um pequeno script de shell que usa esse programa para obter um relógio de microssegundo lendo os dois lados da fonte de um script. O programa C é a única maneira portátil e com sobrecarga mínima para obter uma precisão de menos de um segundo em um registro de data e hora.

Aqui está o programa C epoch.c:

#include <sys/time.h>
#include <stdio.h>
int main(int argc, char **argv) {
    struct timeval time;
    gettimeofday(&time, NULL);
    printf("%li.%06i", time.tv_sec, time.tv_usec);
}

E o script de shell timer:

#!/bin/echo Run this in the shell you want to test

START=$(./epoch)
. "$1"
END=$(./epoch)
echo "$END - $START" | bc

Este é o padrão linguagem de comandos shell e bce deve funcionar como um script em qualquer shell compatível com POSIX.

Você pode usar isso como:

$ bash timer ./test.sh
.002052
$ dash timer ./test.sh
.000895
$ zsh timer ./test.sh
.000662

Ele não mede o tempo do sistema ou do usuário, apenas o tempo decorrido do relógio de parede não monotônico. Se o relógio do sistema mudar durante a execução do script, isso fornecerá resultados incorretos. Se o sistema estiver sob carga, o resultado não será confiável. Eu não acho que nada melhor possa ser portátil entre conchas.

Um script de timer modificado pode ser usado evalpara executar comandos fora de um script.


Por coincidência, pouco antes de ler isso (e a última linha sobre eval), eu estava ajustando o script na resposta de Rabin para incluir eval "$@", para que ele pudesse executar os shell shell rapidamente .
agc 01/01

4

Solução revisada várias vezes usando /proc/uptimee dc/ bc/ awkem grandes partes, graças à entrada da agc :

#!/bin/sh

read -r before _ < /proc/uptime

sleep 2s # do something...

read -r after _ < /proc/uptime

duration=$(dc -e "${after} ${before} - n")
# Alternative using bc:
#   duration=$(echo "${after} - ${before}" | bc)
# Alternative using awk:
#   duration=$(echo "${after} ${before}" | awk '{print $1 - $2}')

echo "It took $duration seconds."

Supõe obviamente que /proc/uptimeexiste e tem uma determinada forma.


3
Isso o tornaria portátil entre os shells, mas não entre as implementações do Unix, já que alguns simplesmente não possuem o /procsistema de arquivos. Se isso é uma preocupação ou não, eu não sei.
Kusalananda

1
Para enfatizar, este Q sugere velocidades de disquete ou piores. Nesse caso, a sobrecarga do carregamento awkpode ser significativa. Talvez b=$(cat /proc/uptime)antes, em seguida, a=$(cat /proc/uptime)depois de, em seguida, analisar $ a e $ b e subtrair.
agc

@agc Boa entrada, obrigado, eu adicionei algumas soluções alternativas em conformidade.
Phd

1
Não pensar nisso antes, mas se builtins gosto readsão melhores do que cat, isso seria mais limpa (e um pouco mais rápido): read before dummyvar < /proc/uptime ;sleep 2s;read after dummyvar < /proc/uptime; duration=$(dc -e "${after} ${before} - n");echo "It took $duration seconds."
AGC
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.