Skrypt Bash; optymalizacja prędkości przetwarzania

10

Zastanawiałem się, czy istnieją ogólne wytyczne dotyczące optymalizacji skryptów Bash.

  • Na przykład wygodniej jest pisać pętle niż wiersze poleceń, ale czy jest też szybsze przetwarzanie w systemie? Przykład:

    for i in a b c; do echo $i; done
    
    echo a
    echo b
    echo c
  • Czasami ludzie przedstawiają różne rozwiązania tego samego problemu. Na przykład sed, cut, awk, i echosą w stanie rozebrać się z ciągu cyfr. Zastanawiałem się, czy możesz powiedzieć, że im mniej cyfr ma kod, tym szybciej jest, jeśli używasz:

    1. to samo polecenie, np

      STRING=abc.def
      echo ${STRING} | sed 's/.def//g'
      echo ${STRING} | sed '$s/....$//'
    2. różne polecenia, np

      STRING=abc.def
      echo ${STRING} | cut -d . -f 1
      echo ${STRING} | sed 's/.def//g'
Vincent
źródło
2
W większości przypadków wolę czytelność niż prędkość.
Bernhard,
1
a) Tak, korzystanie z pętli for wiąże się z narzutem, ale to nie ma znaczenia b) spróbuj zminimalizować zewnętrzne polecenia i po prostu zamień rzeczy z bash. A jeśli musisz zoptymalizować skrypt powłoki, robisz coś źle i powinieneś rozważyć użycie języka z lepszą obsługą profilowania.
Ulrich Dangel
Wyrażenia regularne są ogólnie mniej wydajne pod względem wydajności niż jakikolwiek inny wybór, więc jeśli widzisz sposób na zrobienie czegoś bez jednego (np. Cięcia), użyj tego. Oczywiście może to wpłynąć na czytelność, jeśli umiejętność czytania jest ograniczona do wyrażeń regularnych;)
goldilocks

Odpowiedzi:

8

Powłoki nie przeprowadzają żadnej reorganizacji otrzymanego kodu, jest on po prostu interpretowany jeden wiersz po drugim (nic innego nie ma większego sensu w interpretatorze poleceń). Większość czasu spędzanego przez powłokę przeznacza się na analizę leksykalną / parsowanie / uruchamianie programów o nazwie.

W przypadku prostych operacji (takich jak ciąg znaków w przykładach na końcu pytania) zdziwiłbym się, gdyby czas załadowania programów nie spowodował żadnych drobnych różnic prędkości.

Morał tej historii jest taki, że jeśli naprawdę potrzebujesz większej prędkości, lepiej jest skorzystać z (częściowo) skompilowanego języka, takiego jak Perl lub Python, który jest szybszy od samego początku, w którym możesz napisać wiele operacji wymienionych bezpośrednio i nie trzeba wywoływać zewnętrznych programów, i ma opcję wywoływania zewnętrznych programów lub wywoływania zoptymalizowanych modułów C (lub cokolwiek innego), aby wykonać większą część pracy. To jest powód, dla którego w Fedorze „cukier do administrowania systemem” (GUI) jest napisany w Pythonie: można dodać fajny GUI bez większego wysiłku, wystarczająco szybki dla takich aplikacji, mieć bezpośredni dostęp do wywołań systemowych. Jeśli to za mało, wybierz C ++ lub C.

Ale nie idź tam, chyba że możesz udowodnić, że wzrost wydajności jest wart utraty elastyczności i czasu rozwoju. Skrypty powłoki nie są takie złe, aby je odczytać, ale wzdrygam się, gdy pamiętam niektóre skrypty użyte do instalacji Ultrix, które kiedyś próbowałem rozszyfrować. Zrezygnowałem, zastosowano zbyt wiele „optymalizacji skryptu powłoki”.

vonbrand
źródło
1
+1, ale wiele osób twierdzi, że bardziej prawdopodobne jest zwiększenie elastyczności i czasu programowania przy użyciu czegoś takiego jak python lub perl vs. shell, a nie strata. Powiedziałbym, że używaj skryptu powłoki tylko wtedy, gdy jest to konieczne, lub to, co robisz, wiąże się z dużą ilością poleceń specyficznych dla powłoki.
goldilocks
22

Pierwszą zasadą optymalizacji jest: nie optymalizuj . Najpierw przetestuj. Jeśli testy wykażą, że twój program działa zbyt wolno, poszukaj możliwych optymalizacji.

Jedynym sposobem, aby się upewnić, jest przeprowadzenie testu porównawczego dla swojego przypadku użycia. Istnieje kilka ogólnych zasad, ale dotyczą one tylko typowych ilości danych w typowych aplikacjach.

Niektóre ogólne zasady, które mogą, ale nie muszą być prawdziwe, w konkretnych okolicznościach:

  • Do wewnętrznego przetwarzania w powłoce ATT ksh jest najszybszy. Jeśli wykonujesz wiele operacji na łańcuchach, użyj ATT ksh. Dash zajmuje drugie miejsce; bash, pdksh i zsh pozostają w tyle.
  • Jeśli musisz często wywoływać powłokę, aby za każdym razem wykonywać bardzo krótkie zadania, dash wygrywa z powodu niskiego czasu uruchamiania.
  • Rozpoczęcie zewnętrznego procesu kosztuje czas, więc szybciej jest mieć jeden rurociąg ze złożonymi elementami niż rurociąg w pętli.
  • echo $foojest wolniejszy niż echo "$foo", ponieważ bez podwójnych cudzysłowów dzieli się $foona słowa i interpretuje każde słowo jako wzorzec wieloznaczny nazwy pliku. Co ważniejsze, zachowanie dzielenia i globowania jest rzadko pożądane. Pamiętaj więc, aby zawsze umieszczać podwójne cudzysłowy wokół podstawień zmiennych i podstawień poleceń: "$foo", "$(foo)".
  • Dedykowane narzędzia zwykle wygrywają z narzędziami ogólnego zastosowania. Na przykład narzędzia takie jak cutlub headmogą być emulowane sed, ale sedbędą wolniejsze i awkbędą jeszcze wolniejsze. Przetwarzanie ciągów powłoki jest powolne, ale w przypadku krótkich ciągów znacznie przewyższa wywoływanie programu zewnętrznego.
  • Bardziej zaawansowane języki, takie jak Perl, Python i Ruby, często umożliwiają pisanie szybszych algorytmów, ale mają znacznie dłuższy czas uruchamiania, więc są one warte jedynie wydajności w przypadku dużych ilości danych.
  • W Linuksie potoki są zwykle szybsze niż pliki tymczasowe.
  • Większość zastosowań skryptowania powłoki dotyczy procesów związanych z operacjami we / wy, więc zużycie procesora nie ma znaczenia.

Rzadko zdarza się, że wydajność jest problemem w skryptach powłoki. Powyższa lista ma charakter wyłącznie orientacyjny; w większości przypadków doskonale jest stosować metody „powolne”, ponieważ różnica często wynosi ułamek procenta.

Zwykle celem skryptu powłoki jest szybkie wykonanie zadania. Musisz wiele zyskać na optymalizacji, aby usprawiedliwić spędzanie dodatkowych minut na pisaniu scenariusza.

Gilles „SO- przestań być zły”
źródło
2
Chociaż pythoni rubysą zdecydowanie wolniejsze, przynajmniej w moim systemie, uruchamianie perljest tak szybkie, jak bashlub ksh. GNU awk jest znacznie wolniejszy niż GNU sed, szczególnie w lokalizacjach utf-8, ale nie dotyczy to wszystkich awks i wszystkich plików. ksh93> myślnik> pdksh> zsh> bash nie zawsze są tak wyraźne. Niektóre pociski są lepsze w niektórych rzeczach niż inne, a zwycięzca nie zawsze jest taki sam.
Stéphane Chazelas
2
Re „musisz dużo zyskać od ...” : jeśli „ty” obejmuje bazę użytkowników, to prawda. Dzięki skryptom powłoki w popularnych pakietach systemu Linux użytkownicy często marnują kilka rzędów wielkości więcej czasu niż oszczędza pochopny programista.
agc
2

Rozwijamy się tutaj na powyższym przykładzie globowania, aby zilustrować niektóre cechy wydajności interpretera skryptu powłoki. Porównanie bashi dashinterpretatorów dla tego przykładu, w którym proces jest spawnowany dla każdego z 30 000 plików, pokazuje, że myślnik może rozwidlać wcprocesy prawie dwa razy szybciej niżbash

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

Porównanie podstawowej prędkości zapętlenia bez wywoływania wcprocesów pokazuje, że zapętlenie deski rozdzielczej jest prawie 6 razy szybsze!

$ 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

Zapętlanie jest nadal stosunkowo powolne w obu powłokach, jak pokazano wcześniej, więc dla skalowalności powinniśmy spróbować użyć bardziej funkcjonalnych technik, aby iteracja była przeprowadzana w skompilowanych procesach.

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

Powyższe jest zdecydowanie najbardziej wydajnym rozwiązaniem i dobrze ilustruje tę kwestię, że w skrypcie powłoki należy robić jak najmniej, a dążyć tylko do połączenia istniejącej logiki dostępnej w bogatym zestawie narzędzi dostępnych w systemie UNIX.

Skradzione z typowych błędów skryptu powłoki autorstwa Pádraiga Brady'ego.

Rahul Patil
źródło
1
Ogólna zasada: obsługa deskryptorów plików również kosztuje, więc zmniejsz ich liczbę. Zamiast for i in *; do wc -l "$i">/dev/null; donelepiej for i in *; do wc -l "$i"; done>/dev/null.
manatwork
@manatwork będzie również zerować wyjście timecmd
Rahul Patil
@manatwork Dobrze ... teraz Proszę dać mi również dane wyjściowe bez wywoływania wc -l, sprawdź, czy zaktualizowałem po opublikowaniu danych wyjściowych
Rahul Patil
Poprzednie pomiary zostały wykonane w mniejszym katalogu. Teraz utworzyłem jeden z 30000 plików i powtórzyłem testy: pastebin.com/pCV6QKp2
manatwork
Te testy porównawcze nie uwzględniają różnych czasów uruchamiania każdej powłoki. Testy porównawcze wykonane w obrębie każdej powłoki byłyby lepsze.
agc