manejo - ¿Por qué la función fwrite libc es más rápida que la función de escritura syscall?
guardar y leer datos en un archivo.txt en c (3)
Después de proporcionar el mismo programa que lee un archivo de entrada generado aleatoriamente y repite la misma cadena que leyó en una salida. La única diferencia es que en un lado estoy proporcionando los métodos de lectura y escritura de Linux syscalls, y en el otro lado estoy usando fread / fwrite.
Al sincronizar mi aplicación con una entrada de 10Mb de tamaño y enviarla a / dev / null, y asegurándome de que el archivo no se almacene en la caché, descubrí que el código de libc es más rápido en una escala GRANDE cuando se utilizan buffers muy pequeños caso).
Aquí está mi salida del tiempo, usando fwrite:
real 0m0.948s
user 0m0.780s
sys 0m0.012s
Y usando el syscall escriba:
real 0m8.607s
user 0m0.972s
sys 0m7.624s
La única posibilidad en la que puedo pensar es que libc internamente ya está almacenando en búfer mi entrada ... Desafortunadamente, no pude encontrar mucha información en la web, así que tal vez los gurús de aquí puedan ayudarme.
Al sincronizar mi aplicación con una entrada de 10Mb de tamaño y enviarla a / dev / null, y asegurándome de que el archivo no esté en caché, he descubierto que el frwite de libc es más rápido en una escala GRANDE cuando se utilizan buffers muy pequeños (1 byte en caso).
fwrite
funciona en secuencias, que están en búfer. Por lo tanto, muchos búferes pequeños serán más rápidos porque no ejecutarán una llamada de sistema costosa hasta que el búfer se llene (o lo vacíe o cierre el flujo). Por otro lado, los búferes pequeños que se envían para write
ejecutarán una costosa llamada al sistema para cada búfer ; ahí es donde se pierde la velocidad. Con un búfer de flujo de 1024 bytes y escritura de búferes de 1 byte, usted está viendo 1024 llamadas de write
por cada kilobyte , en lugar de 1024 llamadas de letra de escritura que se convierten en una write
; ¿ve la diferencia?
Para los búferes grandes, la diferencia será pequeña, porque habrá menos almacenamiento en búfer y, por lo tanto, habrá un número más consistente de llamadas al sistema entre write
y write
.
En otras palabras, fwrite(3)
es solo una rutina de biblioteca que recopila resultados en fragmentos y luego llama a write(2)
. Ahora, write(2)
, es una llamada al sistema que atrapa el kernel . Ahí es donde realmente ocurre la E / S. Hay algo de sobrecarga por simplemente llamar al kernel, y luego está el tiempo necesario para escribir algo. Si usa búferes 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 escritura, la sobrecarga de almacenamiento en búfer de Fwrite es solo eso: más sobrecarga.
Si desea obtener más información al respecto, puede consultar este documento , que explica las secuencias de E / S estándar.
También puede deshabilitar el búfer con la función setbuf (). Cuando el almacenamiento en búfer está deshabilitado, fwrite () será tan lento como write () si no es más lento.
Puede encontrar más información sobre este tema aquí: http://www.gnu.org/s/libc/manual/html_node/Controlling-Buffering.html
write
(2) es la operación fundamental del kernel.
fwrite
(3) es una función de biblioteca que agrega almacenamiento en búfer en la parte superior de write
(2).
Para los recuentos de bytes pequeños (p. Ej., Línea a la vez), fwrite
(3) es más rápido, debido a la sobrecarga por solo hacer una llamada al kernel.
Para grandes recuentos de bytes (bloque de E / S), write
(2) es más rápido, ya que no se preocupa por el almacenamiento en búfer y tiene que llamar al núcleo en ambos casos.
Si observa la fuente de cp
(1), no verá ningún búfer.
Finalmente, hay una última consideración: ISO C vs Posix. Las funciones de la biblioteca con búfer como fwrite
se especifican en ISO C, mientras que las llamadas del kernel como write
son Posix. Si bien muchos sistemas afirman ser compatibles con Posix, especialmente cuando se trata de calificar para contratos gubernamentales, en la práctica es específico de los sistemas similares a Unix. Por lo tanto, las operaciones con búfer son más portátiles. Como resultado, un cp
Linux ciertamente usará la write
pero un programa de C que tiene que funcionar en varias plataformas puede tener que usar fwrite.