Fwrite быстрее, чем WriteFile в Windows?

Я всегда думал, что WriteFile более эффективен, чем fwrite, потому что fwrite вызывает внутренне WriteFile, но следующий тестовый код показывает мне, что fwrite значительно быстрее, чем WriteFile.

fwrite стоит 2 миллисекунды, в то время как WriteFile требуется 27000 (FILE_ATTRIBUTE_NORMAL), оба сбрасываются после каждого вызова записи. Если я вызову WriteFile с FILE_FLAG_WRITE_THROUGH и прокомментирую строку FlushFileBuffers (wfile), WriteFile будет быстрее, это будет стоить 800.

Так действительно ли fwrite обращается к WriteFile? Что делает такую огромную разницу? Как работает fwrite внутри? Как я могу записать данные в файл с API более эффективно, чем fwrite? (Unbufferd, синхронно).

   #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;
   }

Обновить: Спасибо за ответы, вот ответ: см. Каталог 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;
    }

так вот флаг _IOCOMMIT, а затем посмотрите ... \ 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 вызывается внутри fopen, обратитесь к документам fopen, я нахожу это:

"mode: 'c'

Включите флаг фиксации для соответствующего имени файла, чтобы содержимое буфера файлов записывалось непосредственно на диск, если вызывается либо fflush, либо _flushall. "Таким образом, _commit вызывается, только если при вызове fopen установлен флаг 'c'.

функция _commit в конечном итоге вызывает FlushFileBuffers.

Помимо этого, я обнаружил, что когда я записываю в файл только несколько данных (не превышающих размер буфера), если fwrite без fflush, текст, очевидно, не будет записываться, а для API - после WriteFile, даже если я не вызываю FlushFileBuffers. Когда я открываю файл (программа находится в спящем режиме), содержимое записывается в файл автоматически, что стало одной из причин, по которой я запутался в сбросе, эта операция может выполняться ОС, WriteFile копирует данные в системный кеш и его файловый буфер управляется ОС, поэтому вполне разумно, чтобы fflush () вызывала WriteFile только внутренне без реальной очистки, система знает, когда их очищать, может быть, когда дескриптор файла закрыт или когда произошел другой доступ ввода / вывода к этому файлу. Таким образом, я изменил эталонный тест так:

      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;

результат умножен на: 99999 fwrite: 217 WriteFile: 171

Итак, в заключение, чтобы ускорить операцию записи файла API:

Не вызывайте FlushFileBuffers явно: данные в системном кеше будут записываться на диск при необходимости.

Получите буфер для WriteFile, как это делает fwrite, потому что вызов API стоит больше времени, чем просто memcpy, вызовите WriteFile при заполнении буфера.

Ответы на вопрос(2)

Ваш ответ на вопрос