saber ralentizan que programas programa procesos problemas lento hace encendido eliminar como alenta c performance linux-kernel stdout glibc

c - ralentizan - printf ralentiza mi programa



procesos que ralentizan mi pc (9)

Tengo un pequeño programa en C para calcular hashes (para tablas hash). El código parece bastante limpio, espero, pero hay algo que no está 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 (comparados con / usr / bin / time). Sin embargo, cuando estoy 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 quizás?
  3. ¿Cómo está diseñado stdlibc en relación con esto, y cómo puede mejorarse?
  4. ¿Cómo podría el kernel soportarlo mejor? ¿Cómo debería modificarse para que el rendimiento de los "archivos" locales (sockets, tuberías, etc.) REALMENTE sea rápido?

Espero respuestas interesantes y detalladas. Gracias.

PD: esto es para un conjunto de herramientas de construcción de compiladores, así que no te preocupes por entrar en detalles. Si bien eso no tiene nada que ver con el problema en sí, solo quería señalar que los detalles me interesan.

Apéndice

Estoy buscando enfoques más programáticos para soluciones y explicaciones. De hecho, las tuberías hacen 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 realizada por "usuarios normales". PERO eso no cambia el hecho de que un 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 asombrosos

El tiempo de referencia es para llamadas a printf () dentro de un TTY y toma aproximadamente 4 minutos y 20 segundos.

Las pruebas bajo un / dev / pts (por ejemplo, Konsole) aceleran la salida a unos 5 segundos.

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

setbuffer () aparentemente no tiene efecto cuando lo usa: toma la misma cantidad de tiempo (en un TTY unos 4 minutos, en un PTS unos 5 segundos).

Lo sorprendente es que si comienzo la prueba en TTY1 y luego cambio a otro TTY , tomará exactamente lo mismo que en un PTS: aproximadamente 5 segundos.

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

Normalmente, debería ser igual de lento, sin importar si mira al TTY mientras está activo, o si cambia a otro TTY.

Lección : cuando se ejecutan programas de alto rendimiento, cambie a otro TTY.


  1. ¿Por qué no crear las cadenas a pedido y no 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 simplemente 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 invierte en la conversión de formato y cuánto se gasta en mostrar el resultado en la consola y cambiar el código de manera adecuada.

  4. La salida de la consola es lenta por definición, 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 procesos / subprocesos, etc. una vez que llegue al controlador de pantalla, ¡tal vez un dispositivo de 9600 baudios! o una gran pantalla de mapa de bits, funciones simples como desplazarse por la pantalla pueden implicar la manipulación de megabytes de memoria.


  1. La E / S siempre es lenta en comparación con el cálculo directo. El sistema tiene que esperar a que haya más componentes disponibles para usarlos. Luego tiene que esperar la respuesta antes de que pueda continuar. A la inversa, si se trata simplemente de computación, entonces solo se moverán realmente datos entre la RAM y los registros de la CPU.

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

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


Como la E / S siempre es mucho más lenta que el cálculo 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.

La impresión de los valores ahora se puede hacer después o en paralelo por otro hilo. Por lo tanto, es posible que los hilos de cálculo no tengan que esperar hasta que se devuelva printf.


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

Además, ¿quién los va a leer? No tiene sentido imprimirlos todos en un formato legible para las personas si nadie necesita leerlos todos.


La salida no almacenada es muy lenta.

Por defecto, la stdout estándar está completamente en búfer, sin embargo, cuando se adjunta al terminal, la stdout tiene búfer o está en búfer en línea.

Intente activar el almacenamiento en búfer para stdout usando setvbuf() , como esto:

char buffer[8192]; setvbuf(stdout, buffer, _IOFBF, sizeof(buffer));


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

Si se envía a una consola, el desplazamiento suele ser un asesino.


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


Si está impreso en la consola, por lo general es extremadamente lento. No estoy seguro de por qué, pero creo que no vuelve hasta que la consola muestre gráficamente la cadena generada. Además, no puedes mmap () a la salida estándar.

Escribir en un archivo debería ser mucho más rápido (pero aún así los órdenes de magnitud son más lentos que el cálculo de un hash, toda la E / S es lenta).


Supongo que el tipo de terminal está utilizando algunas operaciones de salida en búfer, de modo que cuando realiza una impresión no se produce una salida en microsegundos divididos, se almacena en la memoria intermedia del subsistema del terminal.

Esto podría verse afectado por otras cosas que podrían causar una ralentización, tal vez se esté ejecutando una operación con gran cantidad de memoria que no sea su programa. En resumen, hay demasiadas cosas que podrían estar sucediendo al mismo tiempo, paginación, intercambio, I / O pesado por otro proceso, configuración de la memoria utilizada, tal vez actualización de la memoria, etc.

Podría ser mejor concatenar las cadenas hasta que se alcance un cierto límite, luego, cuando lo esté, escríbalo todo de una 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 sí conozco y he jugado con Solaris. Puede haber una opción del kernel para usar un tty virtual. Admito que ha pasado un tiempo desde que se metió con las configuraciones del kernel y lo recompilé. . Como tal, mi memoria puede no ser muy buena 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, haga una búsqueda para ver la sección de configuración de video debajo del subárbol de dispositivos.

Editado: pero hay una modificación que usted pone en el kernel agregando un archivo al sistema de archivos proc (si tal cosa existe), o posiblemente un interruptor pasado al kernel, algo como esto (esto es imaginativo y no implica que realmente existe), fastio

Espero que esto ayude, Saludos cordiales, Tom.