¿Es fwrite más rápido que WriteFile en Windows?

Siempre he pensado que WriteFile es más eficiente que fwrite, porque fwrite invoca internamente a WriteFile, pero el siguiente código de prueba me muestra que fwrite es mucho más rápido que WriteFile.

fwrite cuesta 2 milisegundos, mientras que WriteFile necesita 27000 (FILE_ATTRIBUTE_NORMAL), ambos se vacían después de cada llamada de escritura. Si llamo a WriteFile con FILE_FLAG_WRITE_THROUGH, y comento la línea FlushFileBuffers (wfile), WriteFile será más rápido y costará 800.

Entonces, ¿es realmente que fwrite llama a WriteFile? ¿Qué hace una gran diferencia? ¿Cómo funciona fwrite internamente? ¿Cómo puedo escribir datos en un archivo con API de manera más eficiente que fwrite? (Sin búfer, sincrónico).

   #include <Windows.h>
   #include <stdio.h>
   #include <iostream>

   int main() {
     FILE* cfile = fopen("file1.txt", "w");
     HANDLE wfile = CreateFile("file2.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 
           /*FILE_ATTRIBUTE_NORMAL*/FILE_FLAG_WRITE_THROUGH, NULL);
     DWORD written = 0;

     DWORD start_time, end_time;
     char * text = "test message ha ha ha ha";
     int size = strlen(text);
     int times = 999;

     start_time = timeGetTime();
     for(int i = 0; i < times; ++i) {
       fwrite(text, 1, size, cfile);
       fflush(cfile);
     }
     end_time = timeGetTime();
     std::cout << end_time - start_time << '\n';

     start_time = timeGetTime();
     for(int i = 0; i < times; ++i) {
         WriteFile(wfile, text, size, &written, NULL);
         //FlushFileBuffers(wfile);
     }
     end_time = timeGetTime();
     std::cout << end_time - start_time << std::endl;

     system("pause");
     return 0;
   }

Actualizar: Gracias por las respuestas, aquí está la respuesta: consulte el directorio VS \ VS \ crt \ src \ fflush.c:

    //fflush.c
    int __cdecl _fflush_nolock (FILE *str) {
        //irrelevant codes
        if (str->_flag & _IOCOMMIT) {
                return (_commit(_fileno(str)) ? EOF : 0);
        }
        return 0;
    }

así que aquí hay un indicador _IOCOMMIT, luego vea ... \ src \ fdopen.c

    FILE * __cdecl _tfdopen (int filedes, const _TSCHAR *mode) {
      //irrelevant codes
        while(*++mode && whileflag)
          switch(*mode) {
      //...
              case _T('c'):
                if (cnflag)
                    whileflag = 0;
                else {
                    cnflag = 1;
                    fileflag |= _IOCOMMIT;
                }
               break;
     //...
    }

_tfopen es llamado por fopen internamente, refiérase a los documentos de fopen, encuentro esto:

"modo: 'c'

Active el indicador de confirmación para el nombre de archivo asociado para que el contenido del búfer del archivo se escriba directamente en el disco si se llama a fflush o _flushall. "Por lo tanto, se llama a _commit solo si se establece el indicador 'c' al llamar a fopen.

la función _commit finalmente llama a FlushFileBuffers.

Además de estos, encuentro que cuando escribo solo unos pocos datos en el archivo (no excede el tamaño del búfer), si se fwrite sin fflush, el texto no se escribirá aparentemente, mientras que para API, después de WriteFile incluso si no llamo a FlushFileBuffers , cuando abro el archivo (el programa está en suspensión), el contenido se escribe en el archivo automáticamente, esa fue una de las razones por las que estaba confundido acerca de la descarga, esta operación puede ser realizada por el sistema operativo, WriteFile copia los datos a la caché del sistema, y su búfer de archivos está administrado por el sistema operativo, por lo que es razonable que fflush () solo llame a WriteFile internamente sin un lavado real, el sistema sabe cuándo vaciarlos, tal vez cuando el identificador de archivo está cerrado o cuando se produce otro acceso de E / S a este archivo. Así que modifiqué el punto de referencia como este:

      start_time = timeGetTime();
for(int i = 0; i < times; ++i) {
    fwrite(text, 1, size, cfile);
    fflush(cfile);
}
end_time = timeGetTime();
std::cout << end_time - start_time << '\n';

start_time = timeGetTime();
for(int i = 0; i < times; ++i) {
    WriteFile(wfile, text, size, &written, NULL);
}
end_time = timeGetTime();
std::cout << end_time - start_time << std::endl;

el resultado es veces: 99999 fwrite: 217 WriteFile: 171

Así que, en conclusión, para acelerar la operación de escritura de archivos API:

No llame a FlushFileBuffers explícitamente, los datos en el caché del sistema se vaciarán en el disco cuando sea necesario.

Obtenga un búfer para WriteFile, al igual que fwrite, ya que la llamada a la API cuesta más tiempo que simplemente memcpy, llame a WriteFile cuando se llena el búfer.

Respuestas a la pregunta(2)

Su respuesta a la pregunta