Jaki jest najszybszy sposób uruchomienia skryptu?

22

Zastanawiałem się, jaki jest najszybszy sposób uruchomienia skryptu, czytałem, że istnieje różnica w szybkości między wyświetlaniem wyniku skryptu na terminalu, przekierowywaniem go do pliku, a może /dev/null.

Jeśli wynik nie jest ważny, jaki jest najszybszy sposób na szybsze działanie skryptu, nawet jeśli jest minimalny.

bash ./myscript.sh 
-or-
bash ./myscript.sh > myfile.log
-or-
bash ./myscript.sh > /dev/null
Kingofkech
źródło
Porównywanie „przekierowania do zwykłego pliku” i „przekierowania do / dev / null” wydaje mi się takie dziwne ...
el.pescado,

Odpowiedzi:

31

Terminale w dzisiejszych czasach są wolniejsze niż kiedyś, głównie dlatego, że karty graficzne nie dbają już o przyspieszenie 2D. Rzeczywiście, drukowanie na terminalu może spowolnić skrypt, szczególnie przy przewijaniu.

W konsekwencji ./script.shjest wolniejszy niż ten ./script.sh >script.log, który z kolei jest wolniejszy niż /script.sh >/dev/null, ponieważ te ostatnie wymagają mniej pracy. Jednak to, czy stanowi to wystarczającą różnicę dla jakiegokolwiek praktycznego celu, zależy od tego, ile danych wyjściowych tworzy skrypt i jak szybko. Jeśli skrypt zapisuje 3 wiersze i kończy pracę lub jeśli drukuje 3 strony co kilka godzin, prawdopodobnie nie musisz zawracać sobie głowy przekierowaniami.

Edycja: Niektóre szybkie (i całkowicie zepsute) testy porównawcze:

  • W konsoli Linux 240x75:

    $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done)
    real    3m52.053s
    user    0m0.617s
    sys     3m51.442s
  • W xterm260x78:

    $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done)
    real    0m1.367s
    user    0m0.507s
    sys     0m0.104s
  • Przekieruj do pliku na dysku Samsung SSD 850 PRO 512GB:

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

     $ time (for i in {1..100000}; do echo $i 01234567890123456789012345678901234567890123456789; done >/dev/null)
     real    0m0.448s
     user    0m0.432s
     sys     0m0.016s
Satō Katsura
źródło
6
@ Kingofkech, który jest mniej niż 200 linii / sekundę. Nie miałoby to większego znaczenia. (Dla porównania timeout 1 yes "This is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line"drukuje ponad 100000 linii w ciągu jednej sekundy na moim MBP.)
muru
4
@Kingofkech Jeśli jest to skrypt, edytuj plik i skomentuj wiersz, w którym drukowane są niepotrzebne dane wyjściowe.
Zyskasz
2
Za wąską interpretację „kiedyś”. Terminal v220 jest znacznie wolniejszy niż dzisiejsze emulatory terminali. Ponadto stacje robocze SUN Sparc (te, których używałem) miały bardzo powolną konsolę, więc przekierowanie wyjścia do pliku podczas kompilacji większego projektu znacznie przyspieszyłoby czas kompilacji.
Kusalananda
1
@Kusalananda To prawda, xtermże na HP Apollo od 30 lat temu czołgano się w porównaniu do xtermsHP-UX sprzed 20 lat. Jednak konsola Linux z kartą wideo Matrox sprzed 15 lat była wolniejsza niż ta sama konsola Linux z kartą S3 sprzed 20 lat. Konsola Linux z buforem klatek o wysokiej rozdzielczości na nowoczesnej karcie jest po prostu bezużyteczna. :)
Satō Katsura
6
@Kingofkech To około 2400 bps. Niektórzy z nas żyli przez lata z taką prędkością. :)
Satō Katsura
14

Instynktownie zgodziłbym się z odpowiedzią Satō Katsury; to ma sens. Jednak testowanie jest dość łatwe.

Testowałem pisanie miliona linii na ekranie, pisanie (dołączanie) do pliku i przekierowywanie do /dev/null. Przetestowałem kolejno każdy z nich, a następnie wykonałem pięć powtórzeń. To są polecenia, których użyłem.

$ 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)

Następnie narysowałem poniżej całkowite czasy.

wykres czasu a wynik

Jak widać, założenia Satō Katsury były prawidłowe. Zgodnie z odpowiedzią Satō Katsury wątpię również, że czynnikiem ograniczającym będzie wyjście, więc jest mało prawdopodobne, aby wybór wyniku miał znaczący wpływ na ogólną szybkość skryptu.

FWIW, moja pierwotna odpowiedź miała inny kod, w którym plik był dołączany i /dev/nullprzekierowywał się w pętli.

$ 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)

Jak zauważa John Kugelman w komentarzach, powoduje to znaczne obciążenie. W chwili obecnej pytanie nie jest tak naprawdę dobrym sposobem na przetestowanie go, ale zostawię go tutaj, ponieważ wyraźnie pokazuje koszt wielokrotnego otwierania pliku z poziomu samego skryptu.

wykres czasu a wynik

W takim przypadku wyniki są odwrócone.

Krogulec
źródło
FWIW Do mojej odpowiedzi dodałem szybki test porównawczy. Warto zauważyć, że konsola Linux jest> 200 razy wolniejsza od xterm, co z kolei jest ~ 3 razy wolniejsze niż /dev/null.
Satō Katsura,
Powinieneś także przetestować z pewnym ograniczeniem prędkości. Wydajność OP wynosi około 200 linii / s.
muru
@muru Czy masz na myśli wydrukowanie linii, odczekanie 1/200 sekund, a następnie powtórzenie? Mogę spróbować, ale myślę, że będą to podobne wyniki, ale po prostu dłużej potrwa, zanim sygnał pokona hałas. Chociaż może uda mi się odjąć czas oczekiwania przed analizą.
Sparhawk
@Sparhawk coś takiego. Myślę, że na tym poziomie wydajności procesor będzie miał dużo czasu na aktualizację wyświetlacza bez spowalniania wydajności. Gdy program nie robi nic poza wyrzucaniem linii bez pauzy, bufory terminali zapełniają się szybciej niż można zaktualizować wyświetlanie i tworzy wąskie gardło.
muru
3

Innym sposobem na przyspieszenie skryptu jest użycie szybszego interpretera powłoki. Porównać prędkości o POSIX zajęty pętli, działającego pod bash v4.4 , ksh v93u + 20120801 i 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

    Wydajność:

    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

    Wydajność:

    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

    Wydajność:

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

Podzbiór poleceń w bashi kshwstecznie kompatybilne do wszystkich poleceń dash. bashSkrypt, który wykorzystuje tylko polecenia w tej podgrupie powinien pracować dash.

Niektóre bashskrypty korzystające z nowych funkcji można przekonwertować na innego tłumacza. Jeśli bashskrypt w dużej mierze opiera się na nowszych funkcjach, może nie być to warte zawracania głowy - niektóre nowe bashfunkcje to ulepszenia, które są zarówno łatwiejsze do kodowania, jak i bardziej wydajne (pomimo bashogólnie wolniejszego), dzięki czemu ich dashodpowiednik (który może wymagać uruchomienia kilka innych poleceń), byłoby wolniejsze.

W razie wątpliwości uruchom test ...

agc
źródło
więc aby przyspieszyć mój skrypt bash, muszę przepisać je w bash lub ksh?
Kingofkech
@Kingofkech Może się zdarzyć, że kod napisany dla bash jest poprawnym kodem ksh lub dash. Spróbuj po prostu zmienić tłumacza.
bli