Come misurare il tempo medio di esecuzione di uno script?


23

Ho due script che calcolano ciascuno il fattoriale di un numero. Vorrei sapere quale è più veloce. Il timecomando mi dà millisecondi e il risultato è diverso di volta in volta:

piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.052s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.091s
user    0m0.048s
sys 0m0.036s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.040s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.087s
user    0m0.064s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.068s
sys 0m0.016s
piousbox@piousbox-laptop:~/projects/trash$ 

Come posso impiegare il tempo medio impiegato per eseguire lo script? Potrei analizzare e fare una media della produzione di un 100 timema immagino che ci sia una soluzione migliore?


Risposte:


4

No, la tua idea di media è corretta.

L'esecuzione dello script dipende da molti fattori, e tuttavia deve essere suddivisa tra tempo di installazione (caricamento dell'interprete in memoria, impostazione e possibilmente compilazione di codice in bytecode o codice macchina) e tempo di esecuzione reale.

Per focalizzarsi meglio sul tempo di esecuzione interno, esegui il ciclo nello script stesso (ovvero invece di calcolare un fattoriale, lo calcoli 100 volte in un'esecuzione dello script. Lo script verrà impostato una volta e la routine interna eseguirà 100 volte).

Per concentrarti sul tempo totale, esegui lo script cento volte e calcola la media dei risultati. Idealmente, dovresti separare quelle esecuzioni in modo che il sistema ritorni sempre in uno "stato di riferimento" (o in uno stato non correlato agli script). Ad esempio, l'interprete stesso verrà memorizzato nella cache, in modo che la prima esecuzione dello script sarà notevolmente più lenta di quelle successive.

Per avere una visione migliore dell'algoritmo, penso che il modo migliore sia qualcosa di simile (su una macchina altrimenti inattiva):

  • avvolgere l'algoritmo in una singola funzione.
  • nell'applicazione di controllo:
    • chiama la funzione una volta
    • ottenere il tempo di sistema ("orologio da parete") e aggiungere 10 (o una ragionevole N) secondi
    • entra nel loop e inizia a contare le iterazioni
    • dopo ogni chiamata alla funzione, incrementare il contatore
    • se l'ora di sistema è inferiore al tempo risparmiato, eseguire un altro ciclo
    • ottenere N esatto, possibilmente in virgola mobile, dall'ora corrente dell'orologio a muro
    • mostra il contatore diviso per N: questo è il numero di iterazioni / secondo.

L'applicazione viene eseguita una sola volta, tutta l'installazione e il priming viene eseguito dalla prima iterazione senza orario, quindi questo dovrebbe ridurre al minimo i costi generali (tranne forse per la chiamata a tempo).

Se la funzione riceve un input, faresti bene a fornirgli una sequenza casuale di input utilizzando un PRNG con seeding con un valore fisso, per garantire che entrambe le versioni della funzione testata ricevano gli stessi valori. Questo evita che una funzione funzioni apparentemente meglio a causa di "numeri fortunati" (ad esempio, ricordo una variazione dell'algoritmo Hillsort che ha funzionato misurabilmente meglio se il numero di elementi da ordinare era nella forma 2 k -1 con piccoli k s).


Bene, grazie. Ho notato che le chiamate successive sono diventate più brevi. Ora eseguo il ciclo all'interno degli script e ho scoperto che un algoritmo è decisamente più veloce dell'altro.
Victor Piousbox,

39

È possibile eseguire iterazioni del programma in un ciclo; e dividi il tempo totale per il numero di iterazioni:

time for i in {1..10}; do sleep 1; done
real    0m10.052s
user    0m0.005s
sys 0m0.018s

2
Super semplice, lo adoro. Inoltre non ho mai visto {1..10}prima e sono sconcertato dal fatto che funzioni, non riesco a trovarlo nel manuale di Bash. L'unica cosa triste è che non conosci la diffusione dei tuoi risultati (tempo minimo e massimo).
wt

@ w00t:man -P 'less +/Brace\ Expansion' bash
user2683246

Grazie @ user2683246! L'ho trovato anche su gnu.org/software/bash/manual/bash.html#Brace-Expansion - buon uso di meno btw. Ora sono anche curioso di sapere quando è apparso a Bash ...
w00t,

1
Ah, versione 3, 10 anni dopo che ho iniziato a usare bash :) tldp.org/LDP/abs/html/bashver3.html
w00t

2
Se questo non funziona per i googler in arrivo, potrebbe essere perché non sei in esecuzione bash. Prova a correre /bin/bashprima di questo.
Cory Klein,

14

esiste uno strumento chiamato multitime che fa esattamente questo: eseguire un comando più volte, misurando il tempo impiegato (reale / utente / sistema con tempo medio, minimo / massimo e mediano calcolato automaticamente)

Ad esempio, per misurare uno script simile 100 volte:

multitime -q -n 100 "fact1.sh"
===> multitime results
1: -q fact1.sh
            Mean        Std.Dev.    Min         Median      Max
real        0.122       0.032       0.086       0.116       0.171       
user        0.148       0.044       0.096       0.137       0.223       
sys         0.023       0.019       0.000       0.014       0.061 

12

Questo è vecchio ma è arrivato così in alto su Google quando stavo cercando un comando che ho usato in precedenza ma che non sono riuscito a trovare. Comunque, il mio modo preferito di farlo è:

perf stat -r 10 -B sleep 1

Ciò fornisce molti dettagli, incluso il tempo medio di esecuzione alla fine:

1.002248382 seconds time elapsed                   ( +-  0.01% )


1

Hyperfine è un'altra opzione.

Esempio di utilizzo:

hyperfine --warmup 3 'ruby fac1.rb'

1
hyperfine è molto meglio delle altre alternative che è ridicolo. Rilevamento di corse necessarie, riscaldamento, bella uscita, segnalazioni di markdown, scritte in ruggine, ecc.
Klas Mellbourn
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.