Frage

Ich habe schon immer mit erstaunt / frustriert wie lange es dauert einfach Ausgabe an das Terminal mit einer print-Anweisung. Nach einigen letzten quälend langsam Protokollierung habe ich mich entschlossen, diese zu prüfen und war ganz, dass fast finden überrascht alle verbrachte die Zeit wartet auf das Terminal die Ergebnisse zu verarbeiten.

Can zu schreiben irgendwie stdout werden beschleunigt?

Ich schrieb ein Skript ( ‚print_timer.py‘ am unteren Rand dieser Frage) Zeitpunkt zu vergleichen, wenn 100k Zeilen an stdout zu schreiben, zu Datei und mit stdout /dev/null umgeleitet. 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

Wow. Um sicherzustellen, dass Python ist etwas hinter den Kulissen nicht tun, wie zu erkennen, dass ich neu zugewiesen stdout / dev / null oder etwas, habe ich die Umleitung außerhalb des Skripts ...

$ 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

So ist es kein Python Trick ist, ist es nur das Terminal. Ich wusste immer, Ausgabe Dumping auf / dev / null Dinge beschleunigt, aber nie dachte, es war, dass signifikante!

Es erstaunt mich, wie langsam die tty ist. Wie kann es, dass das Schreiben auf physische Datenträger sein ist viel schneller als auf den „Bildschirm“ zu schreiben (vermutlich ein All-RAM op), und wirksam ist, so schnell wie einfach in den Müll Dumping mit / dev / null?

diesen Link spricht darüber, wie das Terminal I blockiert / O, so dass es kann „Parst [die Eingabe], dessen Rahmenpuffer aktualisieren, mit dem X-Server kommunizieren, um das Fenster zu rollen und so weiter“ ... aber ich weiß nicht voll es. Was kann so lange nehmen?

Ich erwarte, dass es keinen Ausweg (kurz von einer schnelleren Implementierung tty?), Aber Figur, die ich sowieso fragen würde.


UPDATE: nachdem ich einige Kommentare zu lesen Ich fragte mich, wie viel Einfluss meine Bildschirmgröße auf die Druckzeit tatsächlich hat, und es hat einige Bedeutung haben. Die wirklich langsam Zahlen oben sind mit meinem Gnome-Terminal zu 1920x1200 Luft gesprengt. Wenn ich es sehr klein reduzieren 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 (~ 4 x), aber nicht meine Frage ändern. Es ist nur fügt auf meine Frage, wie ich verstehe nicht, warum die Terminal-Bildschirm-Wiedergabe, eine Anwendung zu stdout Schreiben verlangsamen sollte. Warum wird mein Programm Notwendigkeit für Bildschirmwiedergabe warten fortfahren?

Sind alle Terminal / tty Apps nicht gleich? Ich habe noch zu experimentieren. Es scheint mir wirklich wie ein Terminal sollte alle eingehenden Daten, Parst / macht es unsichtbar puffern kann, und nur den letzten Brocken machen, die in der aktuellen Bildschirmkonfiguration zu einem vernünftigen Framerate sichtbar ist. Also, wenn ich + fsync auf der Festplatte in ~ 0,1 Sekunden schreiben kann, ein Terminal sollte die gleiche Operation in etwas von dieser Reihenfolge in der Lage zu vervollständigen (mit vielleicht ein paar Bildschirm-Updates, während er es tat).

Ich bin immer noch irgendwie gehofft es eine tty Einstellung ist, die von der Anwendungsseite geändert werden kann dieses Verhalten besser für Programmierer zu machen. Wenn dies streng ein Terminal-Anwendung Problem ist, dann vielleicht dies nicht einmal, gehört auf Stackoverflow?

Was bin ich?


Hier ist das Python-Programm verwendet, um das Timing zu erzeugen:

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
War es hilfreich?

Lösung 2

Danke für alle Kommentare! Ich habe es selbst mit Ihrer Hilfe beantworten endete. Es fühlt sich schmutzig Ihre eigene Frage zu beantworten, though.

Frage 1: Warum druckt langsam nach stdout

Antwort: Druck auf der Standardausgabe ist nicht von Natur aus langsam. Es ist das Terminal Sie damit arbeiten langsam. Und es hat so ziemlich Null mit E / A-Pufferung auf der Anwendungsseite zu tun (zB Python-Datei Pufferung). Siehe unten.

Frage 2: Kann es beschleunigt werden

?

Antwort: Ja, es kann, aber scheinbar nicht von der Programmseite (die Seite tut die ‚Druck‘ auf der Standardausgabe). Zur Beschleunigung es, verwenden Sie einen schnelleren anderen Terminal-Emulator.

Erklärung ...

habe ich versucht, ein selbstbeschreibende 'leichtes' Terminal-Programm namens wterm und bekam deutlich bessere Ergebnisse. Unten ist der Ausgang meines Testskript (am unteren Rand der Frage), wenn bei 1920x1200 in auf dem gleichen System in wterm läuft, wo die Grunddruckoption nahm 12s mit gnome-terminal:

-----
timing summary (100k lines each)
-----
print                         : 0.261 s
write to file (+fsync)        : 0.110 s
print with stdout = /dev/null : 0.050 s

0.26s ist viel besser als 12s! Ich weiß nicht, ob wterm intelligentere, ist, wie es macht auf Bildschirm entlang der Linien, wie ich vorschlagen (macht den ‚sichtbar‘ Schwanz zu einer vernünftigen Framerate), oder ob es nur „tut weniger“ als gnome-terminal. Für die Zwecke meiner Frage habe ich die Antwort bekam, though. gnome-terminal ist langsam.

So - Wenn Sie ein langes Skript ausgeführt haben, dass Sie langsam das Gefühl, und es spuckt große Mengen an Text zu stdout ... versuchen Sie einen anderen Terminal und sehen, ob es besser ist

Beachten Sie, dass ich ziemlich zufällig gezogen wterm von der ubuntu / debian-Repositories. Dieser Link könnte das gleiche Terminal sein, aber ich bin mir nicht sicher. Ich habe keine andere Terminal-Emulatoren testen.


Update: Weil ich den Juckreiz zu kratzen hatte, testete ich einen ganzen Haufen anderer Terminalemulatoren mit dem gleichen Skript und Vollbild (1920x1200). Meine Hand gesammelt Statistiken sind hier:

wterm           0.3s
aterm           0.3s
rxvt            0.3s
mrxvt           0.4s
konsole         0.6s
yakuake         0.7s
lxterminal        7s
xterm             9s
gnome-terminal   12s
xfce4-terminal   12s
vala-terminal    18s
xvt              48s

Die erfassten Zeiten werden manuell gesammelt, aber sie waren ziemlich konsistent. Ich nahm den besten (ish) Wert. YMMV, offensichtlich.

Als Bonus es war eine interessante Tour durch einige der verschiedenen Terminalemulatoren zur Verfügung da draußen! Ich bin erstaunt, mein erster ‚alternativer‘ Test die besten der Gruppe entpuppte.

Andere Tipps

  

Wie kann es, dass das Schreiben auf physische Datenträger sein ist viel schneller als auf den „Bildschirm“ zu schreiben (vermutlich ein All-RAM op) und effektiv so schnell wie einfach in den Müll Dumping mit / dev / null?

Herzlichen Glückwunsch, Sie haben gerade die Bedeutung der E / A-Pufferung entdeckt. : -)

Die Scheibe erscheint , schneller sein, weil es sehr gepuffert: Alle write() Anrufe des Python zurückkehren, bevor irgendetwas tatsächlich zu physischen Datenträger geschrieben wird. (Das Betriebssystem funktioniert noch in diesem, viele Tausende von einzelnen schreibt in einem großen kombiniert, effizient Brocken.)

Das Terminal, andererseits wenig oder keine Pufferung. Jeder einzelne print / write(line) wartet auf die Voll write (d.h. Anzeige zu Ausgabegerät) zu vervollständigen

Um den Vergleich Messe zu machen, müssen Sie die Datei Test verwenden die gleiche Ausgabepufferung wie das Terminal, die Sie sich Ihr Beispiel durch Modifizieren tun können:

fp = file("out.txt", "w", 1)   # line-buffered, like stdout
[...]
for x in range(lineCount):
    fp.write(line)
    os.fsync(fp.fileno())      # wait for the write to actually complete

Ich lief die Dateien Schreibtest auf meinem Rechner, und mit Puffern, es 0,05s auch hier für 100.000 Zeilen.

jedoch mit den obigen Änderungen ungepufferte zu schreiben, dauert es 40 Sekunden nur 1000 Zeilen auf der Festplatte zu schreiben. Ich habe für 100.000 Zeilen zu schreiben warten, aber von den vorherige Extrapolation es dauern würde, über eine Stunde .

Das bringt das 11 Sekunden des Terminals in der richtigen Perspektive, nicht wahr?

So Ihre ursprüngliche Frage zu beantworten, zu einem Terminal zu schreiben schnell tatsächlich blazingly, alles in allem, und es gibt nicht viel Platz es viel schneller zu machen (aber einzelne Anschlüsse variieren in wie viel Arbeit, die sie tun, siehe Russ Kommentar zu dieser Antwort).

(Sie könnten mehr Schreibpuffer schreiben, mit Disk-I / O, aber dann würden Sie nicht sehen, was bis zu Ihrem Terminal geschrieben wurde, nachdem der Puffer gespült wird Es ist ein Trade-off:.. Interaktivität im Vergleich zu Bulk-Effizienz)

Ihre Umleitung wahrscheinlich ist nichts als Programme, ob ihr Ausgang FD zeigt auf einen tty bestimmen kann.

Es ist wahrscheinlich, dass stdout Linie gepuffert wird, wenn an einem Terminal zeigt (das gleiche wie C stdout Stream-Verhalten).

Als amüsantes Experiment versuchen, die Ausgabe zu cat kochend.


Ich habe mein eigenes amüsantes Experiment versucht, und hier ist die Ergebnisse.

$ python test.py 2>foo
...
$ cat foo
-----
timing summary (100k lines each)
-----
print                         : 6.040 s
write to file                 : 0.122 s
print with stdout = /dev/null : 0.121 s

$ python test.py 2>foo |cat
...
$ cat foo
-----
timing summary (100k lines each)
-----
print                         : 1.024 s
write to file                 : 0.131 s
print with stdout = /dev/null : 0.122 s

Ich kann nicht über die technischen Details sprechen, weil ich sie nicht wissen, aber das überrascht mich nicht: das Terminal nicht für den Druck vieler Daten wie folgt entwickelt wurde. Tatsächlich bieten Sie auch einen Link zu einer Belastung von GUI Zeug, dass es jedes Mal, wenn Sie etwas ausdrucken möchten zu tun hat! Beachten Sie, dass, wenn Sie das Skript mit pythonw stattdessen aufrufen, ist es nicht 15 Sekunden dauert; das ist ganz eine GUI-Ausgabe. Redirect stdout in einer Datei diese zu vermeiden:

import contextlib, io
@contextlib.contextmanager
def redirect_stdout(stream):
    import sys
    sys.stdout = stream
    yield
    sys.stdout = sys.__stdout__

output = io.StringIO
with redirect_stdout(output):
    ...

Druck zum Terminal geht langsam sein. Leider kurz eine neue Terminal Implementierung des Schreibens kann ich nicht wirklich sehen, wie Sie dies auf deutlich beschleunigen würden.

Neben den Ausgang wahrscheinlich zu einem Linie gepufferte Modus Verzugs Ausgang mit einem Anschluß verursacht auch die Daten in ein Terminal und die Serienleitung fließt mit einem maximalen Durchsatz oder einem Pseudo-Terminal und einem separaten Prozess, ist eine Anzeige Ereignisschleife Handling, eine Scroll-Anzeige zu implementieren Zeichen von einer Schriftart, bewegliche Anzeige Bits zu machen. Letzteres Szenario ist wahrscheinlich die sich über mehrere Prozesse (z Telnet-Server / Client, Terminal-Anwendung, X11-Display-Server), so gibt es Kontextwechsel und Latenzprobleme zu.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top