Comando time in Bash e GNU Time: misurare tempi e risorse

time misura il tempo trascorso e il tempo di utilizzo della CPU (unità centrale di elaborazione) per un comando o una pipeline. In Bash è una parola riservata che può misurare anche funzioni e builtin, cioè comandi interni; il programma esterno GNU Time offre inoltre statistiche sulle risorse, come memoria e operazioni di ingresso e uscita (I/O).

Usate la parola riservata di Bash per misurare una sequenza della shell. Per statistiche dettagliate o un rapporto separato dall’errore standard del programma, scegliete GNU Time, verificando prima che sia installato e quale sia il suo percorso.

Quale time state eseguendo?

Bash riconosce time nella propria grammatica. Per questo può misurare un’intera pipeline o un gruppo di comandi, anziché limitarsi ad avviare un singolo eseguibile. Non è un builtin ordinario, anche se spesso viene descritto genericamente come un comando della shell.

I primi due controlli seguenti descrivono come la shell interpreta il nome. Il terzo controlla un percorso comune su Linux: se il file manca, GNU Time può non essere installato oppure trovarsi altrove. Verificate il programma esterno con il suo percorso e l’opzione --version; non attribuite automaticamente le opzioni GNU alla parola riservata di Bash.

type -a time
command -V time
ls -l /usr/bin/time

Sintassi di base del comando time in Bash

time [-p] command [arguments...]
time [-p] command1 | command2

command, arguments, command1 e command2 nella sintassi sono segnaposto. Nell’esempio eseguibile seguente, sleep 1 attende circa un secondo; la seconda misurazione riguarda la ricerca di file sotto la directory corrente, scartandone l’elenco. Il contenuto della directory influisce sul risultato.

time sleep 1
time find . -type f >/dev/null

Con il formato predefinito, al termine compaiono real, user e sys sullo standard error. Per sleep 1, real è normalmente vicino a un secondo e il consumo di CPU è ridotto; non aspettatevi valori numerici identici su sistemi diversi. Se avete personalizzato TIMEFORMAT, le etichette possono cambiare o il rapporto può essere disattivato.

Il significato di real, user, sys

Voce Significato
real Il tempo effettivo trascorso dall'inizio alla fine. Include anche il tempo trascorso in attesa a causa di disco, ritardi di rete e altri processi.
user Il tempo cumulativo in cui il processo ha utilizzato la CPU in modalità utente.
sys Il tempo cumulativo della CPU utilizzato in modalità kernel per il processo.

I tempi di CPU sono cumulativi: con più processi o thread attivi in parallelo, user + sys può superare real. Se invece il lavoro attende disco, rete o altre risorse, il tempo trascorso può essere molto maggiore del consumo di CPU. Una singola misura non dimostra da sola quale risorsa sia il collo di bottiglia.

Misurare l'intera pipeline

La parola riservata di Bash misura la pipeline seguente fino al suo completamento. Questo esempio Linux richiede find, xargs con supporto per -0, wc e permessi di lettura sui file interessati:

time find /var/log -type f -print0 | xargs -0 wc -l >/dev/null

-print0 e -0 delimitano i nomi con un byte nullo, così gli spazi nei nomi non li separano. wc -l conta i caratteri di fine riga dei file e il risultato viene scartato; eventuali errori di accesso restano visibili. Il rapporto comprende l’intera pipeline, non soltanto find. Un programma esterno posto davanti al primo comando non misurerebbe automaticamente anche quelli successivi: per quel caso avviate esplicitamente una shell con la pipeline desiderata.

Misurare un gruppo di comandi

Un gruppo tra parentesi graffe viene eseguito nella shell corrente. Questo esempio completo e indipendente stampa un messaggio, attende circa un secondo e poi mostra il tempo totale:

time {
  printf '%s\n' 'Inizio della prova'
  sleep 1
}
time (
  cd /srv/app && ./build.sh
)

Il secondo blocco è invece un esempio per un progetto esistente: richiede /srv/app e uno script eseguibile build.sh in quella directory. && evita di avviare lo script se cd fallisce. Le parentesi tonde creano una subshell, quindi il cambio di directory non rimane nella shell chiamante; con le graffe, le modifiche alla shell corrente possono rimanere.

Usare il formato -p

time -p sleep 1

-p seleziona le tre righe real, user e sys, con tempi in secondi, nel formato previsto da POSIX, lo standard per le interfacce dei sistemi di tipo Unix. Per analizzare i numeri automaticamente fissate anche la lingua, per esempio avviando una sessione di prova con LC_ALL=C. Precisione e risoluzione restano legate all’implementazione.

Personalizzare il rapporto con TIMEFORMAT

TIMEFORMAT controlla il rapporto della parola riservata di Bash; non quello del programma GNU esterno. Qui %3R, %3U e %3S richiedono tre cifre decimali:

TIMEFORMAT='elapsed=%3R s user=%3U s sys=%3S s'
time sleep 1
Formato Significato
%R Tempo trascorso in secondi.
%U Tempo di CPU in modalità utente.
%S Tempo di CPU in modalità kernel.
%P Rapporto (user + sys) / real espresso come percentuale.
%% Un carattere % letterale.

Il valore assegnato resta nella shell corrente; un valore vuoto sopprime il rapporto. Per provare senza cambiare la configurazione della sessione, usate una subshell. Il blocco seguente crea anche job.sh, usato negli esempi successivi: lavorate in una directory di prova nuova e vuota, così i nomi dei file non coincidono con lavori esistenti. Richiede Bash, cat, chmod e sleep.

cat > job.sh <<'EOF'
#!/usr/bin/env bash
printf '%s\n' 'risultato'
printf '%s\n' 'diagnostica di prova' >&2
sleep 0.1
EOF
chmod +x job.sh

(
  TIMEFORMAT='elapsed=%R s'
  time ./job.sh
)

Eseguire il programma GNU Time

Dopo il blocco precedente, job.sh stampa risultato sullo standard output e un messaggio di prova sullo standard error, quindi attende brevemente. Gli esempi GNU seguenti richiedono che /usr/bin/time sia il programma GNU installato sul vostro sistema; se manca, consultate il gestore dei pacchetti della distribuzione per il pacchetto time. Il percorso esplicito evita che Bash interpreti la parola riservata.

/usr/bin/time ./job.sh
/usr/bin/time -v ./job.sh

-v produce un rapporto dettagliato: memoria residente massima, page fault, operazioni di I/O e cambi di contesto, quando disponibili. La memoria residente riguarda pagine presenti nella memoria fisica; un page fault richiede al sistema di gestire una pagina non immediatamente disponibile al processo. Una voce a zero può riflettere sia il lavoro eseguito sia limiti delle statistiche del sistema: non aspettatevi dati identici su tutte le piattaforme.

Opzioni utili di GNU Time

Opzione Descrizione
-p Usa il formato POSIX real/user/sys.
-v Mostra le statistiche disponibili delle risorse.
-f FORMAT Sceglie il formato con i codici di GNU Time.
-o FILE Scrive il rapporto nel file indicato, sovrascrivendolo se esiste.
-a Aggiunge al file indicato con -o, senza svuotarlo.

Mostrare tempo e memoria nel formato scelto

/usr/bin/time -f 'elapsed=%e max_rss=%MKB' ./job.sh

Nel formato GNU, %e è il tempo trascorso in secondi e %M la memoria residente massima, espressa in kilobyte secondo il manuale. Le lettere distinguono maiuscole e minuscole: non trasferite i codici di TIMEFORMAT al formato GNU. I valori dipendono anche dal sistema operativo e dal lavoro misurato.

Salvare i rapporti in un file

/usr/bin/time -v -o time-report.txt ./job.sh
/usr/bin/time -v -a -o time-report.txt ./job.sh

La prima riga crea o sovrascrive time-report.txt; la seconda aggiunge un altro rapporto allo stesso file. Usate questi nomi solo nella directory di prova. Il messaggio diagnostico di job.sh rimane sullo standard error del programma e il suo risultato sullo standard output; il rapporto delle risorse va nel file scelto con -o.

Separare standard output e standard error

Il rapporto temporale viene scritto sullo standard error per impostazione predefinita. Così non si mescola con i dati che il programma misurato invia sullo standard output, per esempio verso una pipe o un file.

time ./job.sh >job-output.txt

Il file job-output.txt contiene risultato; il messaggio diagnostico e il rapporto di Bash rimangono sullo standard error. Per catturare anche il rapporto di Bash, applicate la redirezione al gruppo esterno come nel blocco seguente: il file conterrà sia i tempi sia lo standard error di job.sh.

{ time ./job.sh >job-output.txt; } 2>time-and-errors.txt

Leggete job-output.txt e time-and-errors.txt per verificare la separazione. Per ottenere un file contenente soltanto il rapporto delle risorse, l’opzione -o di GNU Time esprime meglio l’intento. I file di prova possono poi essere rimossi singolarmente, dopo aver controllato che siano quelli creati per l’esercizio.

Confrontare misure ripetute

  • La cache dei file, la compilazione JIT (Just-In-Time, eseguita durante l’avvio o l’esecuzione) e il riuso delle connessioni possono cambiare le misure: decidete se confrontare avvii a freddo o esecuzioni successive.
  • Mantenete uguali dati di ingresso, versione del programma, opzioni, numero di lavoratori e destinazione dell’output.
  • Annotate carico di fondo, risparmio energetico e condivisione delle risorse nelle macchine virtuali.
  • Ripetete le prove e confrontate mediana e distribuzione; per comandi molto brevi considerate la risoluzione dell’orologio e il costo della misura.
  • Controllate lo stato di uscita e che i risultati siano equivalenti: un programma che termina subito per errore può sembrare più veloce senza aver svolto il lavoro.

Confronto tra time di Bash e GNU Time

Categoria Bash time GNU /usr/bin/time
Tipo Parola riservata di Bash. Programma esterno.
Pipeline Misura direttamente la pipeline seguente. Avvia il programma specificato; per una pipeline serve una shell esplicita.
Funzioni e builtin Li misura nella sintassi di Bash. Non conosce le funzioni della shell chiamante; occorre una shell che le definisca.
Statistiche dettagliate Tempo trascorso e tempi di CPU. Con -v può riportare anche memoria, I/O e altre risorse.
Formato e destinazione TIMEFORMAT e redirezioni del gruppo. -f, -o e -a.

Domande frequenti

Il tempo utente può essere maggiore del tempo reale?

Sì. Il tempo di CPU accumulato su più core, thread o processi può superare il tempo effettivamente trascorso. Per interpretarlo, considerate il numero di lavoratori in parallelo e il tipo di carico; non sommate real ai tempi di CPU.

time -v non funziona.

Verificate con type -a time. Bash riconosce normalmente la propria parola riservata, che non offre l’opzione GNU -v. Se GNU Time è installato, usate il suo percorso verificato, per esempio /usr/bin/time -v ./job.sh. Controllate anche eventuali differenze quando Bash è in modalità POSIX.

time cambia lo stato di uscita di un comando?

Bash restituisce lo stato della pipeline misurata: normalmente quello dell’ultimo comando, oppure il risultato previsto da pipefail se attivo. Non invertite il risultato con ! quando volete conservarlo. GNU Time restituisce normalmente lo stato del programma, ma errori propri dello strumento o terminazioni per segnale richiedono una gestione specifica. Un rapporto temporale non indica da solo che il lavoro sia riuscito.

Documentazione di riferimento