2012-06-26 30 views
10

Después de hacer la siguiente prueba:escribe o imprime, ¿qué es más rápido?

for(i = 0; i < 3000000; i++) { 
    printf("Test string\n"); 
} 

for(i = 0; i < 3000000; i++) { 
    write(STDOUT_FILENO, "Test string\n", strlen("Test string\n")); 
} 

resulta que las llamadas a printf tomar un gran total de 3 segundos, mientras que las llamadas a escribir tener la friolera de 46 segundos. ¿Cómo, con toda la magia de formato de lujo que printf hace, y el hecho de que printf mismo llama a write, es esto posible? ¿Hay algo que me estoy perdiendo?

Todos y cada uno de los pensamientos y comentarios son apreciados.

+0

depende de su sistema – JMBise

+3

printf hace el almacenamiento en búfer. –

+9

¿De verdad? ¿Estás calculando la longitud de la cuerda cada vez y luego midiendo eso como parte de los tiempos? –

Respuesta

22

¿Cómo, con ... el hecho de que printf llame a escritura, es esto posible? ¿Hay algo que me estoy perdiendo?

Sí, hay algo que te hace falta. printf no necesariamente llama a writecada vez. Por el contrario, printf almacena su salida. Es decir, a menudo almacena su resultado en un búfer de memoria, solo llamando al write cuando el búfer está lleno, o en algunas otras condiciones.

write es una llamada bastante caro, mucho más caro que la copia de datos en el búfer printf 's, lo que reduce el número de llamadas write proporciona una ganancia neta de rendimiento.

Si su stdout está dirigida a un dispositivo terminal, entonces printf llama a cada vez que ve un \n - en su caso, cada vez que se invoca. Si su stdout se dirige a un archivo (o al /dev/null), las llamadas a printf solo se escribirán cuando su búfer interno esté lleno.

Suponiendo que está redireccionando su salida, y que el búfer interno printf es 4Kbytes, entonces el primer bucle invoca write 3000000/(4096/12) == 8780 veces. Sin embargo, su segundo ciclo invoca write 3000000 veces.

Más allá del efecto de un menor número de llamadas a write, es el tamaño de las llamadas a write. La cantidad de almacenamiento en un disco duro es un sector, a menudo 512 bytes. Escribir una cantidad menor de datos que un sector puede implicar leer los datos originales en el sector, modificarlos y volver a escribir el resultado. Invocar write con un sector completo, sin embargo, puede ir más rápido ya que no tiene que leer en los datos originales. El tamaño del buffer printf se elige para que sea un múltiplo del tamaño de sector típico. De esta forma, el sistema puede escribir de manera más eficiente los datos en el disco.

Espero que su primer bucle sea mucho más rápido que el segundo.

+1

Esto explica todo bastante bien. ¡Gracias! En cuanto a tu comentario sobre el tamaño de los datos ...¿Por qué los datos originales no deberían leerse solo porque encajan perfectamente en el sector? ¿La llamada de escritura aún no necesita saber los datos que se deben escribir? – Ataraxia

4

No está comparando manzanas con manzanas, porque el bucle con write carreras strlen3000000 veces, mientras que printf no hace nada de eso; tampoco formatea, por lo que no se aplica la "magia de formato elegante".

size_t len = strlen("Test string\n"); 
for(i = 0; i < 3000000; i++) { 
    write(STDOUT_FILENO, "Test string\n", len); 
} 

Otra diferencia importante es que printf rubores cada vez cuando pasan \n, mientras write no. Debe eliminar \n de ambas cadenas para que sus puntos de referencia sean más equitativos.

+3

En mi sistema, gcc-4.5.1 evalúa 'strlen' en tiempo de compilación incluso sin optimizaciones. La descarga/almacenamiento en búfer parecen ser lo que hace la diferencia. –

+0

@DanielFischer ¡Gracias! Es bueno saber que 'gcc' es lo suficientemente inteligente como para doblar la expresión' strlen' en una constante. – dasblinkenlight

+2

'printf' does ** not ** vaciar en cada' \ n' si la salida se redirige a un archivo. Además, es más exacto decir que 'write' se vacía cada vez, sin contenido, después de todo," flush "en este contexto significa simplemente" llamadas "write'." –

Cuestiones relacionadas