Siempre me ha sorprendido o frustrado el tiempo que lleva simplemente enviar a la terminal una declaración de impresión. Después de un registro lento y dolorosamente reciente, decidí investigarlo y me sorprendió bastante ver que casi todo el tiempo de espera está esperando que el terminal procese los resultados.¿Por qué la impresión en stdout es tan lenta? ¿Se puede acelerar?
¿Se puede acelerar la escritura en stdout de alguna manera?
Escribí una secuencia de comandos ('print_timer.py
' en la parte inferior de esta pregunta) para comparar el tiempo al escribir 100k líneas a stdout, al archivo y con stdout redirigido a /dev/null
. Aquí está el resultado del tiempo:
$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. Para asegurarse de que el pitón no está haciendo algo detrás de las escenas como el reconocimiento de que reasignado salida estándar a/dev/null o algo así, hice el cambio de dirección fuera del guión ...
$ 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
Así que no es una pitón truco, es solo la terminal. Siempre supe que la salida de dumping a/dev/null aceleraba las cosas, ¡pero nunca creí que fuera tan significativo!
Me sorprende lo lento que es el tty. ¿Cómo puede ser que escribir en el disco físico sea MUCHO más rápido que escribir en la "pantalla" (probablemente una opción de todas las RAM), y efectivamente es tan rápido como simplemente descargarlo a la basura con/dev/null?
This link habla de cómo bloquear el terminal de E/S para que pueda "analizar [la entrada], actualice su memoria intermedia de trama, comunicarse con el servidor X con el fin de desplazar la ventana y así sucesivamente" ... pero no lo entiendo del todo ¿Qué puede tomar tanto tiempo?
Espero que no haya escapatoria (¿menos que una implementación de tty más rápida?) Pero imagino que preguntaría de todos modos.
ACTUALIZACIÓN: después de leer algunos comentarios que he preguntado cuánto impacto el tamaño de mi pantalla tiene en realidad en el momento de la impresión, y que tiene algún significado. Los números realmente lentos arriba son con mi terminal Gnome volado hasta 1920x1200. Si lo reduzco muy pequeño, obtengo ...
----- timing summary (100k lines each) ----- print : 2.920 s write to file (+fsync) : 0.121 s print with stdout = /dev/null : 0.048 s
Eso es definitivamente mejor (~ 4x), pero no cambia mi pregunta. Solo agrega a mi pregunta, ya que no entiendo por qué la representación de la pantalla del terminal debe ralentizar la escritura de una aplicación a stdout. ¿Por qué mi programa necesita esperar a que continúe la representación de la pantalla?
¿No se crean todas las aplicaciones de terminal/tty iguales? Todavía tengo que experimentar. Realmente me parece que un terminal debe ser capaz de almacenar todos los datos entrantes, analizarlos/renderizarlos de manera invisible, y solo renderizar el fragmento más reciente que es visible en la configuración de pantalla actual a una tasa de fotogramas sensata. Entonces, si puedo escribir + fsync en el disco en ~ 0.1 segundos, un terminal debería ser capaz de completar la misma operación en algo de ese orden (con algunas actualizaciones de pantalla mientras lo hacía).
Todavía tengo la esperanza de que haya una configuración tty que se puede cambiar desde el lado de la aplicación para mejorar este comportamiento del programador. Si esto es estrictamente un problema de aplicación de terminal, ¿entonces esto quizás ni siquiera pertenece a StackOverflow?
¿Qué me estoy perdiendo?
Aquí es el programa de Python utilizada para generar la sincronización:
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
El propósito de escribir en stdout es para que un humano pueda leer la salida. Ningún ser humano en el mundo puede leer 10,000 líneas de texto en 12 segundos, entonces, ¿cuál es el punto de hacer stdout más rápido? –
@Seun Osewa: Un ejemplo (que impulsó mi pregunta) es cuando se hacen cosas como [impresión de depuración de estado de cuenta] (http://stackoverflow.com/questions/189562). Desea ejecutar su programa y ver los resultados a medida que ocurren. Obviamente tienes razón en que la mayoría de las líneas pasarán volando que no puedes ver, pero cuando ocurre una excepción (o presionas la declaración getch/raw_input/sleep condicional que colocaste con cuidado) quieres mirar la salida impresa directamente en lugar de constantemente tener que abrir o actualizar una vista de archivo. – Russ
La depuración de sentencias de impresión es una de las razones por las cuales los dispositivos tty (es decir, terminales) predeterminan el almacenamiento intermedio en lugar del almacenamiento en bloque: la salida de depuración no es muy útil si el programa se cuelga y las últimas líneas de salida de depuración están todavía en un búfer en lugar de enjuagado a la terminal. –