J'ai toujours été étonné / frustré par le temps qu'il faut pour simplement sortir sur le terminal avec une instruction d'impression. Après une récente journalisation douloureusement lente, j'ai décidé de l'examiner et j'ai été assez surpris de constater que presque tout le temps passé est à attendre que le terminal traite les résultats.
L'écriture sur stdout peut-elle être accélérée d'une manière ou d'une autre?
J'ai écrit un script (' print_timer.py
' au bas de cette question) pour comparer le timing lors de l'écriture de 100k lignes dans stdout, dans un fichier et avec stdout redirigé vers /dev/null
. Voici le résultat du timing:
$ 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
Sensationnel. Pour m'assurer que python ne fait pas quelque chose dans les coulisses comme reconnaître que j'ai réaffecté stdout à / dev / null ou quelque chose, j'ai fait la redirection en dehors 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
Ce n'est donc pas une astuce python, c'est juste le terminal. J'ai toujours su que le dumping de la sortie vers / dev / null accélérait les choses, mais je n'ai jamais pensé que c'était si important!
Je suis étonné de voir à quel point le tty est lent. Comment se fait-il que l'écriture sur le disque physique soit BIEN plus rapide que l'écriture sur «l'écran» (vraisemblablement une opération tout-RAM), et est effectivement aussi rapide que simplement vider à la poubelle avec / dev / null?
Ce lien explique comment le terminal bloquera les E / S afin qu'il puisse "analyser [l'entrée], mettre à jour son frame buffer, communiquer avec le serveur X afin de faire défiler la fenêtre et ainsi de suite" ... mais je ne le fais pas l'obtenir pleinement. Qu'est-ce qui peut prendre si longtemps?
Je pense qu'il n'y a aucun moyen de sortir (à part une implémentation tty plus rapide?) Mais je suppose que je demanderais quand même.
MISE À JOUR: après avoir lu certains commentaires, je me suis demandé quel impact la taille de mon écran avait réellement sur le temps d'impression, et cela a une certaine importance. Les nombres vraiment lents ci-dessus sont avec mon terminal Gnome explosé jusqu'à 1920x1200. Si je le réduis très petit, j'obtiens ...
-----
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 cela ne change pas ma question. Cela ne fait qu'ajouter à ma question car je ne comprends pas pourquoi le rendu de l'écran du terminal devrait ralentir l'écriture d'une application sur stdout. Pourquoi mon programme doit-il attendre que le rendu de l'écran se poursuive?
Toutes les applications de terminal / tty ne sont-elles pas créées égales? Je n'ai pas encore expérimenté. Il me semble vraiment qu'un terminal devrait être capable de mettre en mémoire tampon toutes les données entrantes, de les analyser / les rendre de manière invisible et de ne rendre que le morceau le plus récent qui est visible dans la configuration actuelle de l'écran à une fréquence d'images raisonnable. Donc, si je peux écrire + fsync sur le disque en ~ 0,1 seconde, un terminal devrait pouvoir effectuer la même opération dans quelque chose de cet ordre (avec peut-être quelques mises à jour d'écran pendant qu'il le faisait).
J'espère toujours qu'il existe un paramètre tty qui peut être modifié du côté de l'application pour améliorer ce comportement pour le programmeur. S'il s'agit strictement d'un problème d'application de terminal, cela n'appartient peut-être même pas à StackOverflow?
Qu'est-ce que je rate?
Voici le programme python utilisé pour générer le timing:
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