Qual è il modo più veloce per eseguire uno script?


22

Mi chiedevo quale sia il modo più veloce per eseguire uno script, sto leggendo che c'è una differenza di velocità tra mostrare l'output dello script sul terminale, reindirizzarlo su un file o forse /dev/null.

Quindi, se l'output non è importante, qual è il modo più veloce per far funzionare lo script più velocemente, anche se è minimo.

bash ./myscript.sh 
-or-
bash ./myscript.sh > myfile.log
-or-
bash ./myscript.sh > /dev/null


Il confronto tra "reindirizzamento al file normale" e "reindirizzamento a / dev / null" mi sembra così strano ...
el.pescado

Risposte:


31

Oggi i terminali sono più lenti di quanto non fossero in passato, principalmente perché le schede grafiche non si preoccupano più dell'accelerazione 2D. Pertanto, la stampa su un terminale può rallentare uno script, in particolare quando è coinvolto lo scorrimento.

Di conseguenza ./script.shè più lento di ./script.sh >script.log, che a sua volta è più lento di /script.sh >/dev/null, perché quest'ultimo comporta meno lavoro. Tuttavia, se questo fa abbastanza differenza per qualsiasi scopo pratico dipende dalla quantità di output prodotta dallo script e dalla velocità. Se lo script scrive 3 righe ed esce, o se stampa 3 pagine ogni poche ore, probabilmente non dovrai preoccuparti dei reindirizzamenti.

Modifica: alcuni benchmark rapidi (e completamente rotti):

  • In una console Linux, 240x75:

    $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done)
    real    3m52.053s
    user    0m0.617s
    sys     3m51.442s
  • In un xterm, 260x78:

    $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done)
    real    0m1.367s
    user    0m0.507s
    sys     0m0.104s
  • Reindirizzamento a un file, su un disco Samsung SSD 850 PRO da 512 GB:

     $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done >file)
     real    0m0.532s
     user    0m0.464s
     sys     0m0.068s
  • Reindirizzare a /dev/null:

     $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done >/dev/null)
     real    0m0.448s
     user    0m0.432s
     sys     0m0.016s

6
@Kingofkech che è meno di 200 linee / secondo. Non farebbe molta differenza. (Per fare un confronto, timeout 1 yes "This is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line"stampa oltre 100000 righe in un solo secondo sul mio MBP.)
muru,

4
@Kingofkech Se questo è uno script, modifica un file e commenta la riga che stampa l'output non necessario.
Guadagnerai

2
Per un'interpretazione ristretta di "usato per essere". Un terminale vt220 è molto più lento degli emulatori di terminali di oggi. Inoltre, le workstation SUN Sparc (quelle che ho usato) avevano una console molto lenta, quindi il reindirizzamento dell'output su un file durante la compilazione di un progetto più grande avrebbe velocizzato il tempo di compilazione in modo immenso.
Kusalananda

1
@Kusalananda È vero, xtermsu HP Apollo di 30 anni fa era solito strisciare rispetto a xtermsHP-UX di 20 anni fa. Tuttavia, una console Linux con una scheda video Matrox di 15 anni fa era più lenta della stessa console Linux con una scheda S3 di 20 anni fa. E una console Linux con frame buffer ad alta risoluzione su una scheda moderna è semplicemente inutilizzabile. :)
Satō Katsura,

6
@Kingofkech Sono circa 2400 bps. Alcuni di noi hanno vissuto per anni con quelle velocità. :)
Satō Katsura,

14

Avrei istintivamente concordato con la risposta di Satō Katsura; ha senso. Tuttavia, è abbastanza facile da testare.

Ho testato la scrittura di un milione di righe sullo schermo, la scrittura (aggiunta) a un file e il reindirizzamento a /dev/null. Ho testato ciascuno di questi a turno, quindi ho fatto cinque repliche. Questi sono i comandi che ho usato.

$ time (for i in {1..1000000}; do echo foo; done)
$ time (for i in {1..1000000}; do echo foo; done > /tmp/file.log) 
$ time (for i in {1..1000000}; do echo foo; done > /dev/null)

Ho quindi tracciato i tempi totali di seguito.

diagramma del tempo vs. output

Come puoi vedere, le presunzioni di Satō Katsura erano corrette. Secondo la risposta di Satō Katsura, dubito anche che il fattore limitante sarà l'output, quindi è improbabile che la scelta dell'output abbia un effetto sostanziale sulla velocità complessiva dello script.

FWIW, la mia risposta originale aveva un codice diverso, che aveva il file aggiunto e /dev/nullreindirizzato all'interno del ciclo.

$ rm /tmp/file.log; touch /tmp/file.log; time (for i in {1..1000000}; do echo foo >> /tmp/file.log; done) 
$ time (for i in {1..1000000}; do echo foo > /dev/null; done)

Come sottolinea John Kugelman nei commenti, ciò aggiunge molte spese generali. Allo stato attuale della domanda, questo non è davvero il modo giusto per testarlo, ma lo lascerò qui perché mostra chiaramente il costo di riaprire un file ripetutamente all'interno dello script stesso.

diagramma del tempo vs. output

In questo caso, i risultati sono invertiti.


FWIW Ho aggiunto un rapido benchmark alla mia risposta. In particolare, la console Linux è> 200 volte più lenta di una xterm, che a sua volta è ~ 3 volte più lenta di /dev/null.
Satō Katsura,

Dovresti anche testare con un po 'di limitazione della frequenza. L'output di OP è di circa 200 linee / s.
muru,

@muru Intendi stampare una riga, attendere 1/200 secondi, quindi ripetere? Posso provare, ma immagino che saranno risultati simili, ma impiegherò molto più tempo affinché il segnale superi il rumore. Anche se forse posso sottrarre i tempi di attesa prima dell'analisi.
Sparhawk,

@Sparhawk qualcosa del genere. Penso che a quel livello di output, la CPU avrà un sacco di tempo per aggiornare il display senza rallentare la velocità di output. Quando il programma non fa altro che vomitare linee senza pausa, i buffer dei terminali si riempiono più velocemente di quanto il display possa essere aggiornato e crea un collo di bottiglia.
muru,

3

Un altro modo per velocizzare uno script è usare un interprete di shell più veloce. Confronta le velocità di un loop occupato POSIX , esegui in bash v4.4 , ksh v93u + 20120801 e dash v0.5.8 .

  1. bash:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | bash -s > /dev/null

    Produzione:

    real    0m25.146s
    user    0m24.814s
    sys 0m0.272s
  2. ksh:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | ksh -s > /dev/null

    Produzione:

    real    0m11.767s
    user    0m11.615s
    sys 0m0.010s
  3. dash:

    time echo 'n=0;while [ $n -lt 1000000 ] ; do \
                      echo $((n*n*n*n*n*n*n)) ; n=$((n+1)); 
                   done' | dash -s > /dev/null

    Produzione:

    real    0m4.886s
    user    0m4.690s
    sys 0m0.184s

Un sottoinsieme di comandi in bashe kshsono retrocompatibili con tutti i comandi in dash. Uno bashscript che utilizza solo comandi in quel sottoinsieme dovrebbe funzionare con dash.

Alcuni bashscript che utilizzano nuove funzionalità possono essere convertiti in un altro interprete. Se lo bashscript si basa fortemente su nuove funzionalità, potrebbe non valere la pena di preoccuparsene - alcune nuove bashfunzionalità sono miglioramenti che sono sia più facili da codificare sia più efficienti (anche se bashgeneralmente più lenti), quindi l' dashequivalente (che potrebbe comportare l'esecuzione molti altri comandi), sarebbe più lento.

In caso di dubbio, esegui un test ...


quindi per velocizzare il mio script bash, devo riscriverli in bash o ksh?
Kingofkech,

@Kingofkech È possibile che il codice che hai scritto per bash sia prontamente corretto ksh o dash line. Prova a cambiare semplicemente l'interprete.
bli
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.