Bash script; ottimizzazione della velocità di elaborazione


10

Mi chiedevo se ci sono linee guida generali per l'ottimizzazione degli script Bash.

  • Ad esempio, è più conveniente scrivere loop che righe di comandi, ma è anche più veloce elaborare per il sistema? Esempio:

    for i in a b c; do echo $i; done
    
    echo a
    echo b
    echo c
  • A volte le persone presentano soluzioni diverse per lo stesso problema. Ad esempio, sed, cut, awk, e echosono tutti in grado di togliere le cifre da una stringa. Mi chiedevo se puoi dire che il numero di cifre minore ha, più veloce è se usi:

    1. lo stesso comando, ad es

      STRING=abc.def
      echo ${STRING} | sed 's/.def//g'
      echo ${STRING} | sed '$s/....$//'
    2. comandi diversi, ad es

      STRING=abc.def
      echo ${STRING} | cut -d . -f 1
      echo ${STRING} | sed 's/.def//g'

2
Nella maggior parte dei casi preferirei la leggibilità alla velocità.
Bernhard

1
a) Sì, c'è un sovraccarico nell'uso di un ciclo for ma non importa b) provare a minimizzare i comandi esterni e sostituire semplicemente le cose da bash. E se devi ottimizzare il tuo script di shell stai facendo qualcosa di sbagliato e dovresti considerare di usare una lingua con un migliore supporto per il profiler.
Ulrich Dangel

Le espressioni regolari sono generalmente meno efficienti dal punto di vista delle prestazioni rispetto a qualsiasi altra scelta, quindi se vedi un modo per fare qualcosa senza uno (ad esempio, tagliare), usalo. Naturalmente, ciò potrebbe influire sulla leggibilità se la tua capacità di lettura è limitata alle espressioni regolari;)
goldilocks

Risposte:


8

Le conchiglie non eseguono alcuna riorganizzazione del codice che ricevono, ma vengono semplicemente interpretate una riga dopo l'altra (nient'altro ha molto senso in un interprete di comandi). Gran parte del tempo impiegato dalla shell è dedicato all'analisi lessicale / analisi / avvio dei programmi chiamati.

Per operazioni semplici (come quelle che mungono le stringhe negli esempi alla fine della domanda), sarei sorpreso se il tempo per caricare i programmi non sommergesse minuscole differenze di velocità.

La morale della storia è che se hai davvero bisogno di più velocità, stai meglio con un linguaggio (semi) compilato come Perl o Python, che è più veloce per iniziare, in cui puoi scrivere molte delle operazioni menzionate direttamente e non deve chiamare programmi esterni e ha la possibilità di invocare programmi esterni o chiamare in moduli C (o quant'altro) ottimizzati per svolgere gran parte del lavoro. Questo è il motivo per cui in Fedora lo "zucchero di amministrazione del sistema" (GUI, in sostanza) è scritto in Python: può aggiungere una bella interfaccia grafica senza troppi sforzi, abbastanza veloce per tali applicazioni, avere accesso diretto alle chiamate di sistema. Se la velocità non è sufficiente, prendi C ++ o C.

Ma non andare lì, a meno che tu non possa provare che il guadagno in termini di prestazioni vale la perdita di flessibilità e tempo di sviluppo. Gli script Shell non sono poi così male da leggere, ma rabbrividisco quando ricordo alcuni script usati per installare Ultrix che una volta ho provato a decifrare. Ho rinunciato, era stata applicata troppa "ottimizzazione degli script di shell".


1
+1, ma molte persone sostengono che è più probabile che ci sia un guadagno in termini di flessibilità e tempo di sviluppo usando qualcosa come Python o Perl vs. Shell, non una perdita. Direi che usa uno script di shell solo se è necessario, o quello che stai facendo comporta una quantità abbondante di comandi specifici della shell.
Riccioli d'oro

22

La prima regola di ottimizzazione è: non ottimizzare . Prima prova. Se i test mostrano che il tuo programma è troppo lento, cerca possibili ottimizzazioni.

L'unico modo per essere sicuri è eseguire il benchmark per il tuo caso d'uso. Esistono alcune regole generali, ma si applicano solo a volumi tipici di dati in applicazioni tipiche.

Alcune regole generali che possono o meno essere vere in qualsiasi circostanza particolare:

  • Per l'elaborazione interna nella shell, ATT ksh è il più veloce. Se si eseguono molte manipolazioni di stringhe, utilizzare ATT ksh. Dash arriva secondo; bash, pdksh e zsh sono in ritardo.
  • Se è necessario invocare frequentemente una shell per eseguire ogni volta un'attività molto breve, il trattino vince a causa del basso tempo di avvio.
  • L'avvio di un processo esterno costa tempo, quindi è più veloce avere una tubazione con pezzi complessi rispetto a una tubazione in un ciclo.
  • echo $fooè più lento di echo "$foo", perché senza virgolette doppie, si divide $fooin parole e interpreta ogni parola come modello jolly di nome file. Ancora più importante, quel comportamento di scissione e globbing è raramente desiderato. Quindi ricorda di mettere sempre le doppie virgolette intorno alle sostituzioni variabili e sostituzioni di comando: "$foo", "$(foo)".
  • Gli strumenti dedicati tendono a conquistare strumenti di uso generale. Ad esempio, strumenti come cuto headpossono essere emulati sed, ma sedsaranno più lenti e awksaranno anche più lenti. L'elaborazione delle stringhe della shell è lenta, ma per le stringhe brevi batte in gran parte chiamando un programma esterno.
  • Linguaggi più avanzati come Perl, Python e Ruby spesso ti permettono di scrivere algoritmi più veloci, ma hanno un tempo di avvio significativamente più alto, quindi valgono solo le prestazioni per grandi quantità di dati.
  • Almeno su Linux, le pipe tendono ad essere più veloci dei file temporanei.
  • La maggior parte degli usi dello scripting della shell si basa su processi associati a I / O, quindi il consumo di CPU non ha importanza.

È raro che le prestazioni siano un problema negli script di shell. L'elenco sopra è puramente indicativo; è perfettamente bene usare metodi "lenti" nella maggior parte dei casi poiché la differenza è spesso una frazione del percento.

Di solito il punto di uno script di shell è fare qualcosa velocemente. Devi guadagnare molto dall'ottimizzazione per giustificare la spesa di altri minuti a scrivere la sceneggiatura.


2
Mentre pythone rubysono decisamente più lenti da avviare, almeno sul mio sistema, perlè altrettanto veloce da avviare come basho ksh. GNU awk è significativamente più lento di GNU sed specialmente in utf-8 locali, ma non è vero per tutti i awk e tutte le sed. ksh93> dash> pdksh> zsh> bash non è sempre così nitido come quello. Alcune conchiglie sono migliori in alcune cose rispetto ad altre, e il vincitore non è sempre lo stesso.
Stéphane Chazelas,

2
Ri "devi guadagnare molto da ..." : se "tu" include la base utente, vero. Con gli script di shell nei popolari pacchetti Linux spesso gli utenti sprecano collettivamente diversi ordini di grandezza più tempo di quanto risparmi il programmatore affrettato.
agc,

2

Espanderemo qui il nostro esempio globbing sopra per illustrare alcune caratteristiche prestazionali dell'interprete di script di shell. Confrontando gli interpreti bashe dashper questo esempio in cui viene generato un processo per ciascuno dei 30.000 file, si mostra che il trattino può fork i wcprocessi quasi due volte più veloce dibash

bash-4.2$ time dash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.238s
user    0m0.309s
sys     0m0.815s


bash-4.2$ time bash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.422s
user    0m0.349s
sys     0m0.940s

Confrontando la velocità di loop di base non invocando i wcprocessi, mostra che il loop di dash è quasi 6 volte più veloce!

$ time bash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m1.715s
user    0m1.459s
sys     0m0.252s



$ time dash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m0.375s
user    0m0.169s
sys     0m0.203s

Il looping è ancora relativamente lento in entrambe le shell, come dimostrato in precedenza, quindi per la scalabilità dovremmo provare a utilizzare tecniche più funzionali in modo che l'iterazione venga eseguita nei processi compilati.

$ time find -type f -print0 | wc -l --files0-from=- | tail -n1
    30000 total
real    0m0.299s
user    0m0.072s
sys     0m0.221s

Quanto sopra è di gran lunga la soluzione più efficiente e illustra bene il punto che si dovrebbe fare il meno possibile nello script di shell e mirare solo a usarlo per connettere la logica esistente disponibile nel ricco set di utility disponibili su un sistema UNIX.

Stolen From Common shell errori di script di Pádraig Brady.


1
Una regola generica: anche la gestione dei descrittori di file costa, quindi riduci il loro conteggio. Invece di for i in *; do wc -l "$i">/dev/null; donefare meglio for i in *; do wc -l "$i"; done>/dev/null.
arte

@manatwork annullerà anche l'output di timecmd
Rahul Patil

@manatwork Bene ... ora Per favore dammi anche l'output senza invocare wc -l, controlla di aver aggiornato in post il tuo output
Rahul Patil

Bene, le misure precedenti sono state fatte su una directory più piccola. Ora ne ho creato uno con 30000 file e ripetuto i test: pastebin.com/pCV6QKp2
manatwork

Questi benchmark non riescono a consentire i diversi orari di inizio di ogni shell. I benchmark eseguiti all'interno di ogni shell sarebbero migliori.
agc,
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.