Ich war immer erstaunt / frustriert darüber, wie lange es dauert, einfach mit einer Druckanweisung an das Terminal auszugeben. Nach einigen kürzlich schmerzhaft langsamen Protokollierungen entschied ich mich, mich damit zu befassen, und war ziemlich überrascht, dass fast die gesamte aufgewendete Zeit darauf wartet, dass das Terminal die Ergebnisse verarbeitet.
Kann das Schreiben an stdout irgendwie beschleunigt werden?
Ich habe ein Skript geschrieben (' print_timer.py
' am Ende dieser Frage), um das Timing beim Schreiben von 100.000 Zeilen in stdout, in die Datei und mit stdout zu vergleichen /dev/null
. Hier ist das Timing-Ergebnis:
$ 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
Beeindruckend. Um sicherzustellen, dass Python nicht hinter den Kulissen etwas tut, wie zu erkennen, dass ich stdout zu / dev / null oder so neu zugewiesen habe, habe ich die Umleitung außerhalb des Skripts durchgeführt ...
$ 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
Es ist also kein Python-Trick, sondern nur das Terminal. Ich wusste immer, dass das Dumping der Ausgabe in / dev / null die Dinge beschleunigte, aber ich hätte nie gedacht, dass es so wichtig ist!
Es wundert mich, wie langsam die tty ist. Wie kann es sein, dass das Schreiben auf eine physische Festplatte viel schneller ist als das Schreiben auf den "Bildschirm" (vermutlich eine All-RAM-Operation) und effektiv so schnell wie das einfache Abladen in den Müll mit / dev / null?
Dieser Link beschreibt, wie das Terminal E / A blockiert, damit es "[die Eingabe] analysieren, seinen Bildspeicher aktualisieren, mit dem X-Server kommunizieren kann, um durch das Fenster zu scrollen usw." ... aber ich nicht voll verstanden. Was kann so lange dauern?
Ich gehe davon aus, dass es keinen Ausweg gibt (abgesehen von einer schnelleren Implementierung?), Aber ich würde trotzdem fragen.
UPDATE: Nachdem ich einige Kommentare gelesen hatte, fragte ich mich, welchen Einfluss meine Bildschirmgröße tatsächlich auf die Druckzeit hat und welche Bedeutung sie hat. Die wirklich langsamen Zahlen oben sind mit meinem Gnome-Terminal auf 1920x1200 gesprengt. Wenn ich es sehr klein reduziere, bekomme ich ...
-----
timing summary (100k lines each)
-----
print : 2.920 s
write to file (+fsync) : 0.121 s
print with stdout = /dev/null : 0.048 s
Das ist sicherlich besser (~ 4x), ändert aber nichts an meiner Frage. Dies fügt meiner Frage nur hinzu , da ich nicht verstehe, warum das Rendern des Terminalbildschirms das Schreiben einer Anwendung auf stdout verlangsamen sollte. Warum muss mein Programm warten, bis das Rendern des Bildschirms fortgesetzt wird?
Sind nicht alle Terminal- / Tty-Apps gleich? Ich muss noch experimentieren. Mir scheint wirklich, dass ein Terminal in der Lage sein sollte, alle eingehenden Daten zu puffern, sie unsichtbar zu analysieren / zu rendern und nur den neuesten Block zu rendern, der in der aktuellen Bildschirmkonfiguration mit einer vernünftigen Bildrate sichtbar ist. Wenn ich also + fsync in ~ 0,1 Sekunden auf die Festplatte schreiben kann, sollte ein Terminal in der Lage sein, denselben Vorgang in dieser Reihenfolge auszuführen (mit möglicherweise ein paar Bildschirmaktualisierungen, während es dies tat).
Ich hoffe immer noch, dass es eine tty-Einstellung gibt, die von der Anwendungsseite aus geändert werden kann, um dieses Verhalten für Programmierer zu verbessern. Wenn es sich ausschließlich um ein Problem mit der Terminalanwendung handelt, gehört dies möglicherweise nicht einmal zu StackOverflow?
Was vermisse ich?
Hier ist das Python-Programm, mit dem das Timing generiert wird:
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