GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi l'impression sur stdout est-elle si lente ? Peut-il être accéléré ?

Merci pour tous vos commentaires! J'ai fini par y répondre moi-même avec votre aide. Cela semble sale de répondre à votre propre question, cependant.

Question 1 :Pourquoi l'impression sur la sortie standard est-elle lente ?

Réponse : L'impression sur stdout n'est pas intrinsèquement lent. C'est le terminal avec lequel vous travaillez qui est lent. Et cela n'a pratiquement rien à voir avec la mise en mémoire tampon des E/S du côté de l'application (par exemple, la mise en mémoire tampon des fichiers python). Voir ci-dessous.

Question 2 :Peut-il être accéléré ?

Réponse : Oui, c'est possible, mais apparemment pas du côté du programme (le côté qui "imprime" sur stdout). Pour l'accélérer, utilisez un autre émulateur de terminal plus rapide.

Explication...

J'ai essayé un programme de terminal "léger" autoproclamé appelé wterm et a obtenu significativement meilleurs résultats. Vous trouverez ci-dessous la sortie de mon script de test (au bas de la question) lors de l'exécution en wterm à 1920 x 1200 sur le même système où l'option d'impression de base prenait 12 s en utilisant 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 c'est BEAUCOUP mieux que 12s ! Je ne sais pas si wterm est plus intelligent sur la façon dont il s'affiche à l'écran dans le sens de ce que je suggérais (rendre la queue "visible" à une fréquence d'images raisonnable), ou s'il "fait moins" que gnome-terminal . Aux fins de ma question, j'ai la réponse, cependant. gnome-terminal est lent.

Donc - Si vous avez un script qui s'exécute depuis longtemps et que vous pensez être lent et qu'il crache d'énormes quantités de texte sur stdout... essayez un autre terminal et voyez s'il est meilleur !

Notez que j'ai tiré à peu près au hasard wterm depuis les dépôts ubuntu/debian. Ce lien pourrait être le même terminal, mais je ne suis pas sûr. Je n'ai testé aucun autre émulateur de terminal.

Mise à jour :Parce que je devais gratter la démangeaison, j'ai testé tout un tas d'autres émulateurs de terminaux avec le même script et en plein écran (1920x1200). Mes statistiques collectées manuellement sont ici :

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

Les temps enregistrés sont collectés manuellement, mais ils étaient assez cohérents. J'ai enregistré la meilleure valeur (ish). YMMV, évidemment.

En prime, c'était une visite intéressante de certains des différents émulateurs de terminaux disponibles ! Je suis étonné que mon premier test "alternatif" se soit avéré être le meilleur du groupe.


Votre redirection ne fait probablement rien car les programmes peuvent déterminer si leur sortie FD pointe vers un tty.

Il est probable que stdout soit mis en mémoire tampon en ligne lorsqu'il pointe vers un terminal (le même que le stdout de C comportement du flux).

Comme expérience amusante, essayez de rediriger la sortie vers cat .

J'ai tenté ma propre expérience amusante, et voici les résultats.

$ 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

Comment se fait-il que l'écriture sur le disque physique soit BEAUCOUP plus rapide que l'écriture sur "l'écran" (vraisemblablement une opération entièrement RAM), et qu'elle soit effectivement aussi rapide que le simple vidage à la poubelle avec /dev/null ?

Félicitations, vous venez de découvrir l'importance de la mise en mémoire tampon des E/S. :-)

Le disque apparaît pour être plus rapide, car il est hautement tamponné :tous les write() de Python les appels sont renvoyés avant que quoi que ce soit ne soit réellement écrit sur le disque physique. (Le système d'exploitation le fait plus tard, combinant plusieurs milliers d'écritures individuelles en un gros morceau efficace.)

Le terminal, d'autre part, fait peu ou pas de mise en mémoire tampon :chaque print individuel / write(line) attend le plein écrire (c'est-à-dire afficher sur le périphérique de sortie) pour terminer.

Pour que la comparaison soit équitable, vous devez faire en sorte que le test de fichier utilise la même mise en mémoire tampon de sortie que le terminal, ce que vous pouvez faire en modifiant votre exemple en :

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

J'ai exécuté votre test d'écriture de fichier sur ma machine, et avec la mise en mémoire tampon, il s'agit également de 0,05 s ici pour 100 000 lignes.

Cependant, avec les modifications ci-dessus pour écrire sans tampon, il faut 40 secondes pour écrire seulement 1 000 lignes sur le disque. J'ai renoncé à attendre 100 000 lignes pour écrire, mais en extrapolant de la précédente, cela prendrait plus d'une heure .

Cela met en perspective les 11 secondes du terminal, n'est-ce pas ?

Donc, pour répondre à votre question initiale, écrire sur un terminal est en fait extrêmement rapide, tout bien considéré, et il n'y a pas beaucoup de place pour le rendre beaucoup plus rapide (mais les terminaux individuels varient dans la quantité de travail qu'ils effectuent; voir le commentaire de Russ à ce sujet réponse).

(Vous pouvez ajouter plus de mémoire tampon en écriture, comme avec les E/S de disque, mais vous ne verrez pas ce qui a été écrit sur votre terminal tant que la mémoire tampon n'aura pas été vidée. C'est un compromis :interactivité contre efficacité globale.)


Je ne peux pas parler des détails techniques car je ne les connais pas, mais cela ne m'étonne pas :le terminal n'a pas été conçu pour imprimer une telle quantité de données. En effet, vous fournissez même un lien vers un tas de trucs d'interface graphique qu'il doit faire chaque fois que vous voulez imprimer quelque chose ! Notez que si vous appelez le script avec pythonw au lieu de cela, cela ne prend pas 15 secondes; c'est entièrement un problème d'interface graphique. Rediriger stdout dans un fichier pour éviter cela :

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):
    ...

Linux
  1. Pourquoi la machine virtuelle Windows10 est-elle lente sur OpenStack ?

  2. Pourquoi Rm peut-il supprimer les fichiers en lecture seule ?

  3. Pourquoi ne puis-je pas diviser un fichier .ape ?

  4. Seule la racine peut monter, pourquoi ?

  5. Pourquoi les fichiers ne peuvent-ils pas être manipulés par inode ?

Pourquoi j'utilise rxvt comme terminal

Linux pourquoi ne puis-je pas diriger le résultat vers rm?

Pourquoi ne puis-je pas chown un dossier partagé VirtualBox ?

Pourquoi ne puis-je pas faire défiler dans le terminal ?

Pourquoi ne puis-je pas supprimer ce fichier en tant que root ?

Pourquoi ma rsync est-elle si lente ?