printf ralentiza mi programa


Tengo un pequeño programa C para calcular hashes (para tablas hash). El código se ve bastante limpio, espero, pero hay algo no relacionado con él que me está molestando.

Puedo generar fácilmente alrededor de un millón de hashes en aproximadamente 0.2-0.3 segundos (benchmarked con /usr/bin/time). Sin embargo, cuando estoy ingresando printf()en el bucle for, el programa se ralentiza a unos 5 segundos.

  1. ¿Por qué es esto?
  2. Cómo hacerlo más rápido? mmapp () ing stdout maybe?
  3. Cómo es stdlibc diseñado en lo que respecta a esto, y cómo se puede mejorar?
  4. ¿Cómo podría el núcleo soportarlo mejor? ¿Cómo tendría que ser modificado para hacer que el rendimiento en "archivos" locales (sockets,tuberías,etc.) REALMENTE rápido?

Espero respuestas interesantes y detalladas. Gracias.

PD: esto es para un conjunto de herramientas de construcción de compiladores, así que no sea tímido para entrar en detalles. Mientras que no tiene nada que ver con el problema en sí, sólo quería señalar que los detalles de interés me.

Adición

Estoy buscando enfoques más programáticos para soluciones y explicaciones. De hecho, la tubería hace el trabajo, pero no tengo control sobre lo que hace el "usuario".

Por supuesto, estoy haciendo una prueba en este momento, que no sería hecha por "usuarios normales". PERO eso no cambia el hecho de que una simple printf() ralentiza un proceso, que es el problema para el que estoy tratando de encontrar una solución programática óptima.


Adición - Resultados sorprendentes

El tiempo de referencia es para llamadas de printf() sin formato dentro de un TTY y tarda unos 4 minutos y 20 segundos.

La prueba bajo un /dev/pts (por ejemplo, Konsole) acelera la salida a unos 5 segundos.

Toma aproximadamente la misma cantidad de tiempo cuando uso setbuffer() en mi código de prueba a un tamaño de 16384, casi el mismo para 8192: aproximadamente 6 segundos.

Setbuffer() tiene aparentemente ningún efecto al usarlo: toma la misma cantidad de tiempo (en un TTY unos 4 minutos, en un PTS unos 5 segundos).

Lo sorprendente es, si estoy comenzando la prueba en TTY1 y luego cambiar a otro TTY, toma lo mismo que en un PTS: aproximadamente 5 segundos.

Conclusión: el núcleo hace algo que tiene que ver con la accesibilidad y facilidad de uso. HUH!

Normalmente, debe ser igualmente lento, no importa si mira fijamente el TTY mientras está activo, o cambia a otro TTY.


Lección: cuando se ejecutan programas de salida intensiva, cambiar a otro TTY!

Author: Flavius, 2009-12-02

9 answers

La salida sin búfer es muy lenta.

Por defecto stdout está completamente búfereado, sin embargo cuando está conectado a la terminal, stdout está o bien sin búfer o bien con búfer de línea.

Intenta activar el búfer para stdout usando setvbuf(), así:

char buffer[8192];

setvbuf(stdout, buffer, _IOFBF, sizeof(buffer));
 29
Author: qrdl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 13:46:32

Puede almacenar sus cadenas en un búfer y enviarlas a un archivo (o consola) al final o periódicamente, cuando el búfer esté lleno.

Si la salida a una consola, el desplazamiento suele ser un asesino.

 14
Author: edoloughlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-03 09:34:42

Si estás printf()ing en la consola suele ser extremadamente lento. No estoy seguro de por qué, pero creo que no regresa hasta que la consola muestra gráficamente la cadena de salida. Además no puedes usar mmap () para stdout.

Escribir en un archivo debería ser mucho más rápido (pero aún órdenes de magnitud más lento que calcular un hash, todas las E/S son lentas).

 9
Author: Andreas Bonini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 12:03:03

Puede intentar redirigir la salida en el shell desde la consola a un archivo. Usando esto, los registros con gigabytes de tamaño se pueden crear en cuestión de segundos.

 7
Author: corvus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 12:05:54
  1. I / O es siempre lento en comparación con cálculo directo. El sistema tiene esperar a que más componentes sean disponible con el fin de utilizarlos. Se entonces tiene que esperar la respuesta antes de que pueda continuar. Inverso si es simplemente informática, entonces es solo realmente mover datos entre el Registros de RAM y CPU.

  2. No he probado esto, pero puede ser más rápido agregar tus hashes a una cadena, y luego simplemente imprimir la cadena al final. Aunque si estás usando C, no C++, esto puede resultar ser un dolor!

3 y 4 están más allá de mí, me temo.

 6
Author: n00dle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-26 20:09:12

Como la E/S siempre es mucho más lenta que la computación de la CPU, primero puede almacenar todos los valores en la E/S más rápida posible. Así que usa RAM si tienes suficiente, usa Archivos si no, pero es mucho más lento que RAM.

Imprimir los valores ahora se puede hacer después o en paralelo por otro hilo. Por lo tanto, es posible que el(los) hilo (s) de cálculo no tenga que esperar hasta que printf haya regresado.

 4
Author: Marco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 12:24:28

Descubrí hace mucho tiempo usando esta técnica algo que debería haber sido obvio. Las E / S no solo son lentas, especialmente para la consola, sino que formatear números decimales tampoco es rápido. Si puedes poner los números en binario en grandes buffers, y escribirlos en un archivo, encontrarás que es mucho más rápido.

Además, ¿quién va a leerlos? No tiene sentido imprimirlos todos en un formato legible por humanos si nadie necesita leerlos todos.

 4
Author: Mike Dunlavey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:29
  1. ¿Por qué no crear las cadenas bajo demanda en lugar de que en el punto de construcción? No tiene sentido emitir 40 pantallas de datos en un segundo ¿cómo puede leerlo? ¿Por qué no crear la salida según sea necesario y solo mostrar la última pantalla completa y luego, según sea necesario, el usuario se desplaza???

  2. ¿Por qué no usar sprintf para imprimir en una cadena y luego construir una cadena concatenada de todos los resultados en memoria e imprimir al final?

  3. Al cambiar a sprintf puede ver claramente cuánto tiempo se pasa en la conversión de formato y cuánto se gasta mostrando el resultado en la consola y cambiar el código apropiadamente.

  4. La salida de la consola es por definición lenta, la creación de un hash solo está manipulando unos pocos bytes de memoria. La salida de la consola debe pasar por muchas capas del sistema operativo, que tendrá código para manejar el bloqueo de subprocesos / procesos, etc. una vez que finalmente llega al controlador de pantalla que tal vez un dispositivo de 9600 baudios! o pantalla de mapa de bits grande, funciones simples como desplazarse por la pantalla pueden implicar la manipulación de megabytes de memoria.

 4
Author: AnthonyLambert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 13:42:43

Supongo que el tipo de terminal está utilizando algunas operaciones de salida en búfer, por lo que cuando se hace un printf no sucede a la salida en microsegundos divididos, se almacena en la memoria de búfer del subsistema de terminal.

Esto podría verse afectado por otras cosas que podrían causar una ralentización, tal vez hay una operación intensiva en memoria que se ejecuta en él que no sea su programa. En resumen, hay demasiadas cosas que podrían estar sucediendo al mismo tiempo, paginación, intercambio, e / s pesadas por otro proceso, configuración de la memoria utilizada, tal vez actualización de memoria, y así sucesivamente.

Podría ser mejor concatenar las cadenas hasta que se alcance un cierto límite, luego cuando lo sea, escríbalo todo a la vez. O incluso usando pthreads para llevar a cabo la ejecución del proceso deseado.

Editado: En cuanto a 2,3 está más allá de mí. Para 4, No estoy familiarizado con Sun, pero conozco y me he metido con Solaris, Puede haber una opción del núcleo para usar un tty virtual.. lo admito ha pasado un tiempo desde que se mezcló con las configuraciones del núcleo y la recompiló. Como tal, mi memoria puede no ser grande en esto, tener una raíz alrededor con las opciones para ver.

user@host:/usr/src/linux $ make; make menuconfig **OR kconfig if from X**

Esto activará el menú del kernel, eche un vistazo para ver la sección de configuración de video bajo el sub-árbol de dispositivos..

Editado: pero hay un retoque que se pone en el kernel agregando un archivo en el sistema de archivos proc (si tal cosa existe), o posiblemente un switch pasado al kernel, algo como esto (esto es imaginativo y no implica que realmente exista), fastio

Espero que esto ayude, Atentamente, Tom.

 2
Author: t0mm13b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-02 13:18:59