2009-09-01 15 views
20

Después de proporcionar el mismo programa que lee un archivo de entrada generado aleatoriamente y hace eco de la misma cadena que lee en una salida. La única diferencia es que, por un lado, proporciono los métodos de lectura y escritura de las llamadas de sistema de Linux, y por otro lado, estoy usando fread/fwrite.¿Por qué la función fwrite libc es más rápida que la función de escritura syscall?

Programando mi aplicación con una entrada de 10Mb de tamaño y haciéndola eco de/dev/null, y asegurándome de que el archivo no está en caché, he encontrado que libc's fwrite es más rápido en escala GRANDE al usar memorias muy pequeñas (1 byte en el caso).

Aquí está mi salida de tiempo, usando fwrite:

real 0m0.948s 
user 0m0.780s 
sys  0m0.012s 

Y el uso de la llamada al sistema de escritura:

real 0m8.607s 
user 0m0.972s 
sys  0m7.624s 

La única posibilidad que se me ocurre es que internamente libc ya está buffering mi entrada ... Desafortunadamente no pude encontrar tanta información en la web, así que tal vez los gurús aquí podrían ayudarme.

+4

"internamente libc ya está almacenando mi entrada en el búfer". Esto es exactamente lo que está haciendo. Probablemente incluso pueda leer el código fuente de libc si lo desea y ver exactamente cómo lo está haciendo. – kquinn

Respuesta

29

Timing mi solicitud con una entrada de 10Mb de tamaño y haciendo eco a /dev/null, y asegurarse de que el archivo en no almacena en caché, he encontrado que frwite de libc es más rápido por un gran escala cuando usando buffers muy pequeños (1 byte en el caso ).

fwrite funciona en transmisiones, que se almacenan en el búfer. Por lo tanto, muchos búferes pequeños serán más rápidos porque no ejecutará una costosa llamada al sistema hasta que el búfer se llene (o lo vacíe o cierre la transmisión). Por otro lado, los buffers pequeños que se envían al write ejecutarán una llamada al sistema costosa para en cada memoria intermedia - ahí es donde está perdiendo velocidad. Con un búfer de secuencia de 1024 bytes y escribir búferes de 1 byte, está mirando 1024 write llamadas para cada kilobyte, en lugar de 1024 fwrite llamadas convirtiéndose en una write - ver la diferencia?

Para los almacenamientos intermedios grandes, la diferencia será pequeña, ya que habrá menos almacenamiento en búfer y, por lo tanto, un número más consistente de llamadas al sistema entre fwrite y write.

En otras palabras, fwrite(3) es solo una rutina de biblioteca que recopila la salida en fragmentos, y luego llama al write(2). Ahora, write(2), es una llamada al sistema que atrapa en el kernel. Ahí es donde realmente sucede la E/S. Hay algo de sobrecarga para simplemente llamar al kernel, y luego está el tiempo que lleva escribir algo. Si usa buffers grandes, encontrará que write(2) es más rápido porque eventualmente tiene que ser llamado de todos modos, y si está escribiendo una o más veces por fwrite, entonces la sobrecarga del búfer fwrite es solo eso: más sobrecarga.

Si quiere leer más sobre esto, puede echarle un vistazo a this document, que explica los flujos de E/S estándar.

14

write (2) es la operación fundamental del núcleo.

fwrite (3) es una función de biblioteca que agrega almacenamiento en búfer en la parte superior de write (2).

Para conteos de bytes pequeños (por ejemplo, línea a la vez), fwrite (3) es más rápido, debido a la sobrecarga por solo hacer una llamada al kernel.

Para conteos de bytes grandes (E/S de bloques), write (2) es más rápido, porque no molesta con el almacenamiento en búfer y tiene que llamar al kernel en ambos casos.

Si mira la fuente a cp (1), no verá ningún almacenamiento en búfer.

Finalmente, hay una última consideración: ISO C vs Posix. Las funciones de la biblioteca en búfer como fwrite se especifican en ISO C, mientras que las llamadas al kernel como write son Posix. Si bien muchos sistemas afirman que es compatible con Posix, especialmente cuando se trata de calificar para contratos gubernamentales, en la práctica es específico de los sistemas tipo Unix. Entonces, las operaciones con buffer son más portátiles. Como resultado, un Linux cp ciertamente usará write pero un programa C que tiene que funcionar multiplataforma puede tener que usar fwrite.

+0

Hace poco tuve una entrevista y di el mismo razonamiento sobre la diferencia b/w write y fwrite, y la respuesta que obtuve fue: "¡tu conocimiento sobre esta diferencia es completamente falso!". El entrevistador me pareció muy arrogante. Aún así, solo quería confirmar si hay alguna otra diferencia entre las llamadas realizadas a través de glibc y las llamadas realizadas directamente a kernel. –

+0

@PK, actualicé mi respuesta ... – DigitalRoss

Cuestiones relacionadas