Pregunta

Siempre he sido sorprendido / frustrado con el tiempo que se necesita para la producción simplemente a la terminal con una declaración de impresión. Después de algunos recientes tala dolorosamente lento decidí buscar en ella y se sorprendió al encontrar que casi todos el tiempo está a la espera para el terminal para procesar los resultados.

Puede escribir en la salida estándar puede acelerar de alguna manera?

Me escribió un guión ( 'print_timer.py' en la parte inferior de esta pregunta) para comparar el tiempo al escribir 100 mil líneas a la salida estándar, a su archivo, y con la salida estándar redirigido a /dev/null. Este es el resultado de 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 un truco de pitón, es sólo la terminal. Siempre supe vertido de salida a / dev / null acelerado las cosas, pero nunca imaginé que era tan significativo!

Me sorprende cómo la lenta TTY es. ¿Cómo puede ser que la escritura en el disco físico es mucho más rápido que escribir a la "pantalla" (presumiblemente un artículo todo-RAM), y es efectivamente tan rápido como simplemente descargando a la basura con / dev / null?

este enlace habla de cómo bloquear el terminal de E / S por lo que puede "análisis sintáctico [la entrada], actualice su memoria intermedia de trama, se comunican con el servidor X con el fin de desplazar la ventana y así sucesivamente" ... pero no consiguen plenamente. Lo que se puede tomar tanto tiempo?

espero que no hay manera de salir (menos que una implementación más rápida TTY?), Pero la figura que le pediría de todos modos.


ACTUALIZACIÓN: después de leer algunos comentarios me preguntaba 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 son muy lentos anteriores con mi terminal de Gnome soplado de hasta 1920x1200. Si reduzco muy pequeña me sale ...

-----
timing summary (100k lines each)
-----
print                         : 2.920 s
write to file (+fsync)        : 0.121 s
print with stdout = /dev/null : 0.048 s

Esto es ciertamente mejor (~ 4x), pero no cambia mi pregunta. Sólo añade a mi pregunta, ya que no entiendo por qué la representación de pantalla del terminal debe retrasar una solicitud por escrito a la salida estándar. ¿Por qué la necesidad del programa que esperar para la representación en pantalla para continuar?

¿Todos Terminal / TTY aplicaciones no crean de la misma? Todavía tengo que experimentar. Realmente me parece como un terminal debe ser capaz de amortiguar todos los datos entrantes, análisis sintáctico / hacerla invisible, y sólo hacer que el trozo más reciente que es visible en la configuración actual de la pantalla a una velocidad razonable. Así que si puedo escribir + fsync en el disco en ~ 0,1 segundos, un terminal debe ser capaz de completar la misma operación en algo de ese orden (con tal vez un par de actualizaciones de pantalla, mientras que lo hizo).

sigo siendo una especie de esperanza hay un ajuste TTY que se puede cambiar desde el lado de la aplicación para que este mejor comportamiento para el programador. Si esto es estrictamente una cuestión de aplicación de terminal, entonces este puede que ni siquiera pertenecen en StackOverflow?

¿Qué me falta?


Este es el programa pitón utilizado para generar la temporizació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
¿Fue útil?

Solución 2

Gracias por todos los comentarios! He terminado de contestar yo mismo con su ayuda. Se siente sucia responder a su propia pregunta, sin embargo.

Pregunta 1: ¿Por qué está imprimiendo a la salida estándar lenta

Respuesta: Prensa a la salida estándar es no inherentemente lento. Es el terminal se trabaja con esto es lento. Y tiene casi cero que ver con E / S de búfer en el lado de la aplicación (por ejemplo: Pitón de archivo de almacenamiento temporal). Véase más abajo.

Pregunta 2: ¿Puede ser acelerado

?

Respuesta: Sí se puede, pero al parecer no del lado de programa (el lado haciendo la 'impresión' a la salida estándar). Para acelerarlo, utilizar un emulador de terminal más rápido diferente.

Explicación ...

He intentado un programa de auto-describe 'ligero' terminal llamado wterm y consiguió significativamente mejores resultados. A continuación se muestra la salida de mi escritura de la prueba (en el fondo de la cuestión) cuando se ejecuta en wterm a 1920x1200 en el mismo sistema en el que la opción de impresión básica tomó 12s usando 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 es mucho mejor que 12 años! No sé si wterm es más inteligente acerca de cómo se presta a la pantalla a lo largo de las líneas de cómo me estaba sugiriendo hacer la cola ( 'visible' a una velocidad razonable), o si es simplemente "hace menos" que gnome-terminal. A los efectos de mi pregunta Tengo la respuesta, sin embargo. gnome-terminal es lento.

Por lo tanto - Si usted tiene un script de larga duración que se siente es lento y se arroga grandes cantidades de texto a la salida estándar ... tratar un terminal diferente y ver si es mejor

Tenga en cuenta que yo más o menos al azar sacó wterm del Ubuntu / Debian repositorios. Este enlace podría ser el mismo terminal, pero no estoy seguro. No he probado otros emuladores de terminal.


Actualización: Porque tenía que rascarse la picazón, he probado todo un montón de otros emuladores de terminal con el mismo guión y de pantalla completa (1920x1200). Mis estadísticas recogidas manualmente aquí:

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

Los tiempos registrados se recogen manualmente, pero eran bastante consistente. Grabé el mejor valor (más o menos). Tu caso es distinto, obviamente.

Como beneficio adicional, que era un interesante recorrido por algunos de los diferentes emuladores de terminal out disponible allí! Estoy sorprendido de mi primera prueba 'alternativo' resultó ser el mejor del grupo.

Otros consejos

  

¿Cómo puede ser que la escritura en el disco físico es mucho más rápido que escribir a la "pantalla" (presumiblemente un artículo todo-RAM), y es efectivamente tan rápido como simplemente descargando a la basura con / dev / null?

Las felicitaciones, usted acaba de descubrir la importancia de buffer de E / S. : -)

El disco aparece a ser más rápido, ya que es muy amortiguada: llamadas write() todo de Python están regresando antes de que algo está realmente escrito en el disco físico. (El sistema operativo lo hace más tarde, la combinación de muchos miles de escrituras individuales en un grande, trozos eficientes.)

El terminal, por otra parte, hace poco o no de tamponamiento:. Cada uno espera print / write(line) individuales para el full escribir (display es decir, para el dispositivo de salida) para completar

Para hacer la comparación equitativa, debe hacer que el uso de la prueba del archivo el mismo búfer de salida como el terminal, lo que se puede hacer mediante la modificación de su ejemplo a:

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

Me encontré con su examen de escritura de archivos en mi máquina, y con búfer, también 0.05s aquí para 100.000 líneas.

Sin embargo, con las modificaciones anteriores para escribir sin búfer, se tarda 40 segundos para escribir sólo 1.000 líneas en el disco. Renuncié a la espera de 100.000 líneas de escribir, pero la extrapolación de la anterior, se necesitarían más de una hora .

que pone a 11 segundos de la terminal en perspectiva, ¿verdad?

Así que para responder a su pregunta original, escrito a un terminal es en realidad extraordinariamente rápido, considerando todas las cosas, y no hay mucho espacio para que sea mucho más rápido (pero terminales individuales varían en la cantidad de trabajo que hacen; ver Russ de comentario a esta respuesta).

(Se podría añadir más almacenamiento temporal de escritura, al igual que con el disco I / O, pero entonces no podría ver lo que estaba escrito a su terminal hasta después de que el buffer se vacía Es una disyuntiva:.. Interactividad frente a la eficiencia mayor)

Su redirección probablemente no hace nada como los programas puede determinar si los puntos de su producción FD a un TTY.

Es probable que stdout está tamponada línea cuando se apunta a un terminal (la misma que la de C stdout comportamiento stream).

Como un experimento divertido, intente tubería de la salida a cat.


He intentado mi propio experimento divertido, y aquí están los resultados.

$ 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

No se puede hablar de los detalles técnicos porque yo no los conozco, pero esto no me sorprende: el terminal no fue diseñado para la impresión de una gran cantidad de datos como estos. De hecho, incluso se proporcionará un enlace a una carga de GUI cosas que tiene que hacer cada vez que desea imprimir algo! Tenga en cuenta que si se llama a la secuencia de comandos con pythonw en cambio, no se necesita 15 segundos; esto es totalmente un asunto de interfaz gráfica de usuario. stdout redirección a un archivo de evitar esto:

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

La impresión en el terminal va a ser lento. Desafortunadamente corta de escribir una nueva aplicación de terminal que no puedo ver cómo le gustaría acelerar este proceso de manera significativa.

Además de la salida probablemente por defecto a un modo de línea-tamponada, la salida a un terminal está también causando sus datos a fluir en una línea terminal y en serie con un rendimiento máximo, o una pseudo-terminal y un proceso separado que es la manipulación de un bucle de eventos de visualización, lo que hace los caracteres de algunos de fuente, mover bits de pantalla para poner en práctica una pantalla de desplazamiento. El último escenario es, probablemente, repartidas en varios procesos (por ejemplo telnet servidor / cliente, aplicación de terminal, X11 servidor de pantalla) por lo que hay cambio de contexto y los problemas de latencia también.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top