C: ¿Por qué es tan lento un fprintf (stdout, …)?

Sigo utilizando la salida de la consola para obtener ideas de lo que ocurre en mi código. Sé que esto puede ser un poco antiguo, pero también lo uso para “canalizar” la salida estándar a los archivos de registro, etc.

Sin embargo, resulta que la salida a la consola se ralentiza por alguna razón. Me preguntaba si alguien podría explicar por qué un fprintf () en una ventana de consola parece estar bloqueando.

Lo que he hecho / diagnosticado hasta ahora:

  1. fprintf(stdout,"quick fprintf\n"); la hora en que un fprintf(stdout,"quick fprintf\n"); simple fprintf(stdout,"quick fprintf\n"); Necesita: 0.82ms (en promedio). Esto se considera demasiado largo ya que un vsprintf_s(...) escribe la misma salida en una cadena en tan solo unos pocos microsegundos. Por lo tanto, debe haber algún locking específicamente a la consola.

  2. Para evitar el locking, he usado vsprintf_s(...) para copiar mi salida en una estructura de datos similar a quince. La estructura de datos está protegida por un objeto de sección crítica. A continuación, un subproceso separado está ejecutando la estructura de datos colocando la salida en cola en la consola.

  3. Otra mejora que pude obtener con la introducción de servicios de tuberías. La salida de mi progtwig (que se supone que termina en una ventana de consola) es la siguiente:

    • A vsprintf_s(...) formatea la salida a cadenas simples.
    • Las cadenas se ponen en cola en una estructura de datos similar a la de quince, por ejemplo, una estructura de lista vinculada. Esta estructura de datos está protegida por un objeto de sección crítica.
    • Un segundo hilo extrae la estructura de datos en cola enviando las cadenas de salida a una tubería con nombre.
    • Un segundo proceso lee la tubería con nombre y coloca las cadenas nuevamente en una estructura de datos similar a quince. Esto es necesario para mantener la lectura alejada de la salida de locking a la consola. El proceso de lectura es rápido en la lectura de la tubería con nombre y supervisa el nivel de llenado de la memoria intermedia de tuberías de forma continua.
    • Un segundo hilo en ese segundo proceso finalmente pone en cola la estructura de datos mediante fprintf(stdout,...) a la consola.

Así que tengo dos procesos con al menos dos subprocesos cada uno, una tubería con nombre entre ellos y quince estructuras de datos similares en ambos lados de la tubería para evitar el locking en el caso de que el buffer de tubería esté lleno.

Eso es un montón de cosas para asegurarse de que la salida de la consola sea “sin locking”. Pero el resultado no es tan malo. Mi progtwig principal puede escribir fprintf complejo (stdout, …) en unos pocos microsegundos.

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

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

Puede cambiar este comportamiento predeterminado cambiando la estrategia de almacenamiento en búfer al almacenamiento en búfer completo . 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.

Haga esta llamada antes de escribir por primera vez en la consola:

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

El tiempo de las escrituras individuales debería mejorar, pero la salida no aparecerá en la consola inmediatamente. Esto no es demasiado útil para la depuración, pero el tiempo mejorará. Si configura un subproceso que llama a fflush(stdout) en intervalos de tiempo regulares, por ejemplo, una vez por segundo, debe obtener un equilibrio razonable entre el rendimiento de las escrituras individuales y el retraso entre el progtwig que escribe la salida y el momento en que puede En realidad lo veo en la consola.