2012-07-19 30 views
9

Todavía uso a menudo el resultado de la consola para obtener ideas sobre lo que está sucediendo en mi código. Sé que esto puede ser un poco pasada de moda, pero también usar esto para "tubo" stdout en los archivos de registro, etc.C: ¿Por qué un fprintf (stdout, ....) es tan lento?

Sin embargo, resulta que la salida de la consola se ralentiza por alguna razón . Me preguntaba si alguien puede explicar por qué una ventana de fprintf() a una consola parece ser una especie de bloqueo.

Lo que he hecho/diagnosticado hasta el momento:

  1. Medí el tiempo de un simple fprintf(stdout,"quick fprintf\n"); Se necesita: 0.82ms (en promedio). Esto se considera por mucho tiempo ya que un vsprintf_s(...) escribe el mismo resultado en una cadena en solo unos pocos microsegundos. Por lo tanto, debe haber algún bloqueo específicamente para la consola.

  2. Para escapar del bloqueo, he usado vsprintf_s(...) para copiar mi salida en una estructura de datos igual a fifo. La estructura de datos está protegida por un objeto de sección crítico. Luego, un subproceso independiente recupera la estructura de datos colocando el resultado en cola en la consola.

  3. Otra mejora que pude obtener con la introducción de los servicios de tuberías. La salida de mi programa (se supone que terminan en una ventana de consola) va de la siguiente manera:

    • Un vsprintf_s(...) formatea la salida a cadenas simples.
    • Las cadenas están en cola en una estructura de datos similar a fifo, una estructura de lista enlazada, por ejemplo. Esta estructura de datos está protegida por un objeto de sección crítico.
    • Un segundo hilo dequeue la estructura de datos enviando las cadenas de salida a un conducto con nombre.
    • Un segundo proceso lee la tubería con nombre y vuelve a poner las cadenas en una estructura de datos similar a fifo . Esto es necesario para mantener la lectura lejos de la salida de bloqueo de la consola. El proceso de lectura es rápido al leer la tubería con nombre y monitorea el nivel de llenado de la memoria intermedia de tuberías continuamente.
    • Un segundo hilo en ese segundo proceso finalmente dequeues la estructura de datos por fprintf(stdout,...) a la consola.

Así que tienen dos procesos con al menos dos hilos cada uno, una tubería con nombre entre ellos, y estructuras de datos FIFO por igual en ambos lados de la tubería para evitar el bloqueo en caso de tampón tubería llena.

Eso es un montón de cosas para asegurarse de que la salida de la consola sea "no bloqueante". Pero el resultado es no está mal. Mi programa principal puede escribir fprintf complejo (stdout, ...) en tan solo unos pocos microsegundos.

Tal vez debería haber preguntado antes: ¿Hay alguna otra forma (más fácil) de tener salida de consola sin bloqueo?

+6

Sospecho que es la consola la que está lenta. Los emuladores de terminal tienden a ser lentos (porque su velocidad en baudios es parte de sus especificaciones). ¿Mide el tiempo cuando redirecciona * stdout * a algún archivo? Y podría enviar información de depuración a otro 'FILE' (y usar' setvbuf' con un gran buffer). –

+0

¿Con qué sistema operativo, bibliotecas, compilador está trabajando? –

+0

La cantidad de tiempo que toma 'fprintf' no es muy interesante si no sabe cuánto de ese tiempo se usa para esperar al canal de salida. –

Respuesta

12

Creo que el problema de tiempo tiene que ver con el hecho de que la consola es línea de memoria por defecto.Esto significa que cada vez que se escribe un caracter '\n', se envía todo el búfer de salida a la consola, lo cual es una operación bastante costosa. Este es el precio que paga por que la línea aparezca en la salida de inmediato.

Puede cambiar este comportamiento predeterminado cambiando la estrategia de almacenamiento en búfer a completa de almacenamiento en memoria. La consecuencia es que la salida se enviará a la consola en fragmentos que son iguales al tamaño de su búfer, pero las operaciones individuales se completarán más rápido.

hacer esta llamada antes de escribir a la primera consola:

char buf[10000]; 
setvbuf(stdout, buf, _IOFBF, sizeof(buf)); 

El momento de escrituras individuales debe mejorar, pero la salida no aparecerá en la consola de inmediato. Esto no es demasiado útil para la depuración, pero el tiempo mejorará. Si configura un hilo que llama al fflush(stdout) en intervalos de tiempo regulares, por ejemplo, una vez por segundo, debe obtener un equilibrio razonable entre la ejecución de escrituras individuales y la demora entre el programa que escribe el resultado y el momento en que realmente puede verlo en la consola.

+0

Probado, confirmado, excelente respuesta. Esperaba que hubiera algún truco para acelerar esto. – Arno

+0

Esto debería usar 'sizeof buf' en lugar de repetir la constante mágica, por supuesto. :) – unwind

+0

@unwind Por supuesto, ¡tienes razón! Fue una omisión obvia de mi parte, puedes editar mis respuestas cuando me veas haciendo algo tan tonto como este :) ¡Muchas gracias! – dasblinkenlight