Zawsze byłem zdumiony / sfrustrowany tym, ile czasu zajmuje po prostu wyprowadzenie na terminal z instrukcją print. Po niedawnym, boleśnie powolnym logowaniu postanowiłem się temu przyjrzeć i byłem dość zaskoczony, że prawie cały czas spędzony na czekaniu, aż terminal przetworzy wyniki.
Czy można w jakiś sposób przyspieszyć pisanie na stdout?
Napisałem skrypt (' print_timer.py
' na dole tego pytania), aby porównać czasy podczas zapisywania 100 tys. Wierszy na stdout, do pliku iz przekierowaniem do stdout /dev/null
. Oto wynik pomiaru czasu:
$ python print_timer.py
this is a test
this is a test
<snipped 99997 lines>
this is a test
-----
timing summary (100k lines each)
-----
print :11.950 s
write to file (+ fsync) : 0.122 s
print with stdout = /dev/null : 0.050 s
Łał. Aby upewnić się, że Python nie robi czegoś za kulisami, jak rozpoznawanie, że przypisałem standardowe wyjście do / dev / null lub coś w tym stylu, wykonałem przekierowanie poza skryptem ...
$ python print_timer.py > /dev/null
-----
timing summary (100k lines each)
-----
print : 0.053 s
write to file (+fsync) : 0.108 s
print with stdout = /dev/null : 0.045 s
Więc to nie jest sztuczka Pythona, to tylko terminal. Zawsze wiedziałem, że zrzucanie danych wyjściowych do / dev / null przyspiesza działanie, ale nigdy nie sądziłem, że jest to aż tak istotne!
Zadziwia mnie, jak powolny jest tty. Jak to możliwe, że zapisywanie na dysku fizycznym jest DUŻO szybsze niż zapisywanie na "ekranie" (przypuszczalnie operacja all-RAM) i jest tak samo szybkie, jak po prostu zrzucanie do śmieci za pomocą / dev / null?
To łącze mówi o tym, jak terminal będzie blokował I / O, aby mógł "analizować [wejście], aktualizować bufor ramki, komunikować się z serwerem X w celu przewijania okna i tak dalej" ... ale ja tego nie robię w pełni to zrozum. Co może trwać tak długo?
Spodziewam się, że nie ma wyjścia (poza szybszą implementacją tty?), Ale i tak zapytam.
AKTUALIZACJA: po przeczytaniu kilku komentarzy zastanawiałem się, jak duży wpływ na czas drukowania ma rozmiar mojego ekranu i ma to pewne znaczenie. Naprawdę wolne liczby powyżej dotyczą mojego terminala Gnome wysadzonego do 1920x1200. Jeśli zmniejszę to bardzo małe, otrzymam ...
-----
timing summary (100k lines each)
-----
print : 2.920 s
write to file (+fsync) : 0.121 s
print with stdout = /dev/null : 0.048 s
To jest z pewnością lepsze (~ 4x), ale nie zmienia mojego pytania. Dodaje to tylko do mojego pytania, ponieważ nie rozumiem, dlaczego renderowanie ekranu terminala powinno spowolnić pisanie aplikacji na standardowe wyjście. Dlaczego mój program musi czekać na kontynuację renderowania ekranu?
Czy wszystkie aplikacje terminala / tty nie są sobie równe? Ja jeszcze nie eksperymentowałem. Naprawdę wydaje mi się, że terminal powinien być w stanie buforować wszystkie przychodzące dane, analizować / renderować je w niewidoczny sposób i renderować tylko najnowszy fragment, który jest widoczny w bieżącej konfiguracji ekranu z rozsądną liczbą klatek na sekundę. Więc jeśli mogę napisać + fsync na dysk w ~ 0,1 sekundy, terminal powinien być w stanie wykonać tę samą operację w czymś w tej kolejności (może z kilkoma aktualizacjami ekranu, gdy to robił).
Nadal mam nadzieję, że istnieje ustawienie tty, które można zmienić po stronie aplikacji, aby poprawić to zachowanie dla programisty. Jeśli jest to wyłącznie problem z aplikacją terminalową, to może nawet nie należy do StackOverflow?
czego mi brakuje?
Oto program w Pythonie używany do generowania synchronizacji:
import time, sys, tty
import os
lineCount = 100000
line = "this is a test"
summary = ""
cmd = "print"
startTime_s = time.time()
for x in range(lineCount):
print line
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
#Add a newline to match line outputs above...
line += "\n"
cmd = "write to file (+fsync)"
fp = file("out.txt", "w")
startTime_s = time.time()
for x in range(lineCount):
fp.write(line)
os.fsync(fp.fileno())
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
cmd = "print with stdout = /dev/null"
sys.stdout = file(os.devnull, "w")
startTime_s = time.time()
for x in range(lineCount):
fp.write(line)
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
print >> sys.stderr, "-----"
print >> sys.stderr, "timing summary (100k lines each)"
print >> sys.stderr, "-----"
print >> sys.stderr, summary