Pourquoi est-impression sur la sortie standard si lent? Peut-il être accéléré?
J'ai toujours été étonné/frustré avec combien de temps cela prend simplement la sortie vers le terminal avec une instruction print. Après un récent lente et douloureuse de journalisation j'ai décidé de le regarder et a été très surpris de constater que presque tous le temps passé est en attente pour le terminal de traiter les résultats.
Peut écrire sur la sortie standard stdout être accéléré en quelque sorte?
J'ai écrit un script ('print_timer.py
"au bas de cette question) pour comparer le timing lors de l'écriture de 100k lignes vers stdout vers un fichier, et avec stdout redirigé vers /dev/null
. Voici le calendrier résultat:
$ 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. Pour assurez-vous que python n'est pas de faire quelque chose de derrière les scènes, comme la reconnaissance que j'ai réassigné stdout vers /dev/null ou quelque chose, j'ai fait la redirection à l'extérieur du script...
$ 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
Donc ce n'est pas un python truc, c'est juste le terminal. J'ai toujours su que le dumping de sortie vers /dev/null accéléré les choses, mais jamais pensé que c'était que d'importants!
Il me stupéfie comment ralentir le tty est. Comment se peut-il que l'écriture sur le disque physique est plus rapide que d'écrire sur le "écran" (sans doute un RAM op), et il est effectivement aussi vite que de simplement le dumping à la poubelle avec /dev/null?
Ce lien parle de la façon dont le terminal block I/O de sorte qu'il peut "parse [entrée], mise à jour de son mémoire d'image, de communication avec le serveur X pour faire défiler la fenêtre et ainsi de suite"... mais je ne suis pas entièrement l'obtenir. Ce peut être aussi long?
Je m'attends il n'y a pas moyen d'en sortir (à moins de un plus rapide ats mise en œuvre?) mais la figure que je vais demander de toute façon.
Mise à JOUR: après lecture de certains commentaires, je me demandais comment beaucoup d'impact sur ma taille de l'écran en fait sur le temps d'impression, et il ne ont leur importance. La très lente chiffres ci-dessus sont avec mon Gnome terminal soufflé jusqu'à 1920x1200. Si je le réduire très petit je obtenir...
-----
timing summary (100k lines each)
-----
print : 2.920 s
write to file (+fsync) : 0.121 s
print with stdout = /dev/null : 0.048 s
C'est certainement mieux (~4x), mais ne change pas ma question. Il ne ajoute à ma question que je ne comprends pas pourquoi l'écran du terminal de rendu devrait ralentir une application d'écriture sur la sortie standard stdout. Pourquoi mon programme besoin d'attendre pour le rendu à l'écran pour continuer?
Sont tous en phase terminale/ats apps pas créés égaux? Je n'ai pas encore l'expérience. Il semble vraiment à moi comme un terminal devrait être en mesure de tampon toutes les données entrantes, analyser/rendre invisible, et que rendre la plus récente morceau qui est visible dans l'écran de configuration à un bon taux de trame. Donc, si je peux écrire+fsync à disque en de ~0,1 secondes, un terminal devrait être en mesure d'effectuer la même opération dans quelque chose de cet ordre (avec peut-être quelques mises à jour d'écran alors qu'il l'a fait).
Je suis encore un peu en espérant que il y a un ats paramètre qui peut être modifié à partir de l'application côté pour faire de ce comportement de mieux pour le programmeur. Si c'est strictement une application de terminal problème, alors ce peut-être n'a même pas sa place sur StackOverflow?
Ce qui me manque?
Voici le programme en python utilisé pour générer le calendrier:
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
- Le but entier de l'écriture sur la sortie standard est donc un homme peut lire le résultat. Aucun être humain dans le monde peut lire les 10 000 lignes de texte en 12 secondes, quel est donc le point de faire stdout plus vite???
- Osewa: Un exemple (qui a conduit à ma question), c'est quand faisant des choses comme imprimer instruction de débogage. Vous souhaitez lancer votre programme et de voir les résultats comme ils se produisent. Vous êtes évidemment raison que la plupart des lignes à la mouche que vous ne pouvez pas voir, mais quand une exception se produit (ou vous frappez le conditionnel getch/raw_input/instruction de mise en veille vous soigneusement placé) vous voulez être à la recherche à la sortie d'impression directement, plutôt que de constamment avoir à ouvrir ou à l'actualisation d'un fichier de point de vue.
- Imprimer instruction de débogage est l'une des raisons pour lesquelles les périphériques tty (c'est à dire les bornes) par défaut à la ligne de mise en mémoire tampon à la place du bloc de mise en mémoire tampon: la sortie de débogage n'est pas grande utilité si le programme se bloque et le dernier quelques lignes de sortie de débogage sont encore dans un tampon au lieu d'aboutir dans le terminal.
- C'est pourquoi je n'ai pas pris la peine beaucoup à la poursuite d'énormes améliorations un intervenant revendiquée par manivelle jusqu'à la taille de la mémoire tampon. Entièrement défait le but de l'impression de débogage! J'ai fait l'expérience un peu lors de l'enquête, mais ne voit pas la nette amélioration. Je suis toujours curieux de connaître la différence, mais pas vraiment.
- Parfois pour très longtemps programmes en cours d'exécution, je vais juste l'impression de la ligne actuelle stdout toutes les n secondes -- semblable à avoir un temps de rafraîchissement dans un malédictions app. Il n'est pas parfait, mais donne une idée de ce qui est où je suis à la fois dans un tout.
Vous devez vous connecter pour publier un commentaire.
Félicitations, vous venez de découvrir l'importance de l'I/O tampon. 🙂
Le disque apparaît pour être plus rapide, car il est très tampon: toutes Python
write()
les appels sont de retour avant que quelque chose est effectivement écrit sur le disque physique. (Le système d'exploitation ne présente plus tard, en combinant plusieurs milliers de écrit dans un grand, efficace morceaux.)Le terminal, d'autre part, n'a que peu ou pas de mise en mémoire tampon: chaque individu
print
/write(line)
attend le plein écrire (c'est à dire l'affichage de sortie de l'appareil) pour valider.Que la comparaison soit juste, vous devez créer le fichier de test à utiliser le même tampon de sortie comme le terminal, qui vous pouvez le faire en modifiant votre exemple:
J'ai couru votre fichier test écrit sur ma machine, et avec mise en mémoire tampon, il a également 0,05 s ici pour 100 000 lignes.
Cependant, avec les modifications ci-dessus pour écrire sans tampon, il faut 40 secondes pour écrire que 1 000 lignes sur le disque. Je me gave d'attendre pour 100 000 lignes à écrire, mais l'extrapolation à partir de la précédente, il faudrait plus d'une heure.
Qui met le terminal de 11 secondes en perspective, n'est-ce pas?
Donc, pour répondre à votre question de départ, l'écriture d'un terminal est en fait extrêmement rapide, toutes choses considérées, et il n'y a pas beaucoup de place pour le rendre beaucoup plus rapide (mais chacun des terminaux ne varient dans la quantité de travail qu'ils font; voir Russ commentaire à cette réponse).
(Vous pouvez ajouter plus d'écrire de mise en mémoire tampon, comme avec les I/O disque, mais alors vous ne serait pas voir ce qui était écrit à votre terminal jusqu'à ce que après le buffer est vidé. C'est un compromis: l'interactivité contre vrac efficacité.)
os.fdopen(sys.stdout.fileno(), 'w', BIGNUM)
. Ce serait presque jamais être utile, bien que presque toutes les demandes devront rappeler explicitement de le rincer après chaque ligne de l'utilisateur de sortie prévue.fp = os.fdopen(sys.__stdout__.fileno(), 'w', 10000000)
) python côté tampons. L'Impact a été de néant. c'est à dire: encore long ats retards. Cela m'a fait penser/réaliser que vous venez de reporter la lente ats problème... quand python tampon enfin bouffées de chaleur de l'ats semble toujours faire la même quantité totale de traitement sur le cours d'eau avant de retourner.fdopen
tampon (2 MO) certainement fait une énorme différence: il a pris le temps d'impression vers le bas à partir de plusieurs secondes à 0,05 s, même en tant que fichier de sortie (à l'aide degnome-terminal
).Merci pour tous les commentaires! J'ai fini de répondre moi-même avec votre aide. Il se sent sale répond à votre question, si.
Question 1: Pourquoi est-impression sur stdout lent?
Réponse: Impression sur stdout est pas par nature lent. C'est le terminal vous travaillez avec qui est lent. Et il a à peu près nulle à faire avec les e/S de mise en mémoire tampon de l'application (par exemple: python fichier de mise en mémoire tampon). Voir ci-dessous.
Question 2: Peut-il être accéléré?
Réponse: Oui il peut, mais apparemment pas du programme (le côté faisant le '"impression" sur la sortie standard stdout). Pour l'accélérer, utiliser plus rapidement les différents émulateur de terminal.
Explication...
J'ai essayé un auto-proclamé 'léger' programme de terminal appelé
wterm
et a obtenu significativement de meilleurs résultats. Ci-dessous est la sortie de mon script de test (au fond de la question) lors de l'exécution danswterm
à 1920x1200 sur le même système où l'option d'impression pris 12s à l'aide de gnome-terminal:De 0,26 s est BEAUCOUP mieux que de 12 ans! Je ne sais pas si
wterm
est plus intelligent sur la façon dont il rend à l'écran le long de la lignes de la façon dont j'ai été suggérant (rendre le "visible" de la queue, à un taux de trame), ou si c'est juste "ne le moins" quegnome-terminal
. Pour les besoins de ma question, j'ai eu la réponse.gnome-terminal
est lente.Si - Si vous avez une longue course script qui vous vous sentez est lent et il crache des quantités massives de texte sur la sortie standard stdout... essayez un autre terminal, et voir si c'est mieux!
Remarque que j'ai à peu près au hasard tiré
wterm
d'ubuntu/debian dépôts. Ce lien peut-être le même terminal, mais je ne suis pas sûr. Je n'ai pas tester les autres émulateurs de terminal.Mise à jour: Parce que j'ai eu à gratter la démangeaison, j'ai testé tout un tas d'autres émulateurs de terminal avec le même script et en plein écran (1920 x 1200). Mon manuellement recueillies stats sont ici:
Les temps enregistrés sont collectées manuellement, mais ils étaient assez cohérentes. J'ai enregistré la meilleure(ish) de la valeur. YMMV, évidemment.
En prime, il était intéressant, la visite de certains des différents émulateurs de terminaux disponibles là-bas! Je suis étonné de voir que mon premier "remplaçant" le test s'est avéré être le meilleur du groupe.
wterm
l'appui du texte UTF-8? Sur mon Ubuntu 9.10, il semble qu'il ne l'est pas.screen
, (le programme) devrait être inclus sur la liste! (Oubyobu
, qui est un wrapper pourscreen
avec des améliorations) Cet utilitaire permet d'avoir plusieurs terminaux, comme les onglets dans les terminaux X. Je présume que l'impression de l'actuelscreen
's terminal est le même que l'impression d'un simple, mais ce que sur l'impression dans l'un desscreen
's terminal et ensuite passer à une autre sans activité?print: 0.587 s, write to file (+fsync): 0.034 s, print with stdout = /dev/null : 0.041 s
. Et avec "l'écran" en cours d'exécution dans iTerm2:print: 1.286 s, write to file (+fsync): 0.043 s, print with stdout = /dev/null : 0.033 s
Votre redirection probablement ne fait rien comme les programmes peuvent déterminer si leur sortie FD points ats.
Il est probable que stdout est en ligne mises en mémoire tampon lorsque pointant vers un terminal (le même que C est
stdout
flux de comportement).Comme une amusante expérience, essayez de la tuyauterie de sortie de
cat
.J'ai essayé ma propre expérience amusante, et voici les résultats.
-u
option forcesstdin
,stdout
etstderr
être sans tampon, ce qui sera plus lent que d'être bloc de mémoire tampon (en raison de frais généraux)Je ne peux pas parler de la technique détails, car je ne les connais pas, mais cela ne m'étonne pas: le terminal n'a pas été conçue pour l'impression des lots de données comme cela. En effet, même vous fournir un lien à une charge de GUI, des choses que il a à faire à chaque fois que vous souhaitez imprimer quelque chose! Notez que si vous appelez le script avec
pythonw
au lieu de cela, il ne prend pas 15 secondes; c'est tout à fait un GUI question. Redirigerstdout
à un fichier pour éviter cela:L'impression que le terminal est lente. Malheureusement court de l'écriture d'un nouveau terminal de mise en œuvre, je ne vois pas vraiment comment vous pouvez accélérer de manière significative.
En plus de la sortie sans doute par défaut d'une ligne-mode mémoire tampon, la sortie d'un terminal est également à l'origine de vos données de flux dans un terminal et la ligne série avec un débit maximum, ou un pseudo-terminal et un processus distinct qui est de la manipulation d'un affichage de boucle d'événements, le rendu des personnages de certains de police, le déplacement de l'affichage des bits à mettre en œuvre un défilement de l'affichage. Le dernier scénario est probablement réparties sur plusieurs processus (par exemple, telnet, client/serveur, le terminal de l'application, serveur d'affichage X11), alors il existe un changement de contexte et les problèmes de latence trop.
cat big_file | tail
ou mêmecat big_file | tee big_file.cpy | tail
très souvent pour cette vitesse.