Całe życie byłem przekonany, że wypluwanie danych do stdout jest najszybszym sposobem prezentowania postępu działania aplikacji działającej w CLI. I dopiero parę dni temu, przez przypadek, dowiedziałem się, że grubo się myliłem.
Weźmy na warsztat prosty skrypt:
$start = microtime(true); for($i = 0; $i < 1000000; $i++) { echo 'New line' . PHP_EOL; } $stop = microtime(true); $totalTime = $stop - $start; echo 'Execution time: ' . $totalTime . PHP_EOL;
Wykonanie w moim przypadku zajmuje ~6,5 s.
Podobny skrypt uruchomiłem niedawno przez przypadek w pracy, jednak zapomniałem odkomentować wyświetlania linii tekstu. Skrypt wykonał się… praktycznie od razu.
Zakomentujmy więc linię zawierającą echo 'New line’ . PHP_EOL; . Po tym zabiegu wykonanie skryptu trwa ok. 0,16 s. Różnica jest więc ogromna.
Po chwilowym szukaniu informacji na ten temat, pomocny okazuje się jak zwykle wątek na stackoverflow. W skrócie: stdout nie jest przystosowany do przetwarzania dużej ilości informacji, natomiast „fenomen” zauważony przez pytającego (zapis na dysk jest szybszy od wyplucia danych do stdout) wynika z buforowania. Dodatkowo, są duże różnice pomiędzy poszczególnymi emulatorami terminala.
Na przyszłość będę więc oszczędniejszy w korzystaniu z stdout, zwłaszcza, gdy zależy na czasie.
Jeśli zależy na czasie, można przekierować STDOUT do /dev/null. Oczywiście jest to wolniejsze, niż brak wyświetlania, ale znacznie szybsze od wyświetlania na ekran:
time perl -e 'for (1..100000){print „$_\n”}’
real 0m1.280s
time perl -e 'for (1..100000){print „$_\n”}’ > /dev/null
real 0m0.056s
Z innych tricków, które lubię – warunkowe wyświetlanie. Zmienna $debug lub $verbose, która określa, czy skrypt ma być gadatliwy, a potem:
print „komunikat” if $debug;
Dzięki temu przez zmianę jednej zmiennej można ograniczyć ilość danych pisanych na STDOUT.
Huh, widzę, że cudzysłowy pojedyncze i podwójne są psute w komentarzach. Znaczy otwierające są zamieniane na dolne, co – jak widać – ssie.