Ist fwrite in Windows schneller als WriteFile?

Ich habe immer gedacht, dass WriteFile effizienter ist als fwrite, weil fwrite intern WriteFile aufruft, aber der folgende Testcode zeigt mir, dass fwrite bedeutend schneller ist als WriteFile.

fwrite kostet 2 Millisekunden, während WriteFile 27000 (FILE_ATTRIBUTE_NORMAL) benötigt. Beide werden nach jedem Schreibaufruf gelöscht. Wenn ich WriteFile mit FILE_FLAG_WRITE_THROUGH aufrufe und die Zeile FlushFileBuffers (wfile) kommentiere, ist WriteFile schneller und kostet 800.

Ist es wirklich so, dass fwrite WriteFile aufruft? Was macht einen so großen Unterschied aus? Wie arbeitet fwrite intern? Wie kann ich Daten mit API effizienter in eine Datei schreiben als mit fwrite? (Ungepuffert, synchron).

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

Aktualisieren: Vielen Dank für die Antworten, hier ist die Antwort: Siehe VS-Verzeichnis \ 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;
    }

Hier ist also ein _IOCOMMIT-Flag, siehe ... \ 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 wird von fopen intern aufgerufen, siehe die Dokumente von fopen, ich finde das:

"mode: 'c'

Aktivieren Sie das Festschreibungsflag für den zugeordneten Dateinamen, damit der Inhalt des Dateipuffers direkt auf die Festplatte geschrieben wird, wenn entweder fflush oder _flushall aufgerufen wird. "_Commit wird also nur aufgerufen, wenn beim Aufruf von fopen das Flag 'c' gesetzt ist.

Die Funktion _commit ruft schließlich FlushFileBuffers auf.

Außerdem finde ich, dass, wenn ich nur ein paar Daten in eine Datei schreibe (die Puffergröße nicht überschreitet), wenn f ohne fflush geschrieben wird, der Text anscheinend nicht geschrieben wird, während für API nach WriteFile, auch wenn ich FlushFileBuffers nicht aufrufe Wenn ich die Datei öffne (Programm befindet sich im Energiesparmodus), wird der Inhalt automatisch in die Datei geschrieben. Dies war einer der Gründe, warum ich über das Löschen verwirrt war. Dieser Vorgang kann vom Betriebssystem ausgeführt werden. WriteFile kopiert Daten in den Systemcache und Der Dateipuffer wird vom Betriebssystem verwaltet, daher ist es vernünftig, dass fflush () WriteFile nur intern ohne echte Leerung aufruft. Das System weiß, wann sie zu leeren sind, möglicherweise, wenn das Dateihandle geschlossen ist oder wenn ein anderer E / A-Zugriff auf diese Datei erfolgt. Also habe ich den Benchmark folgendermaßen modifiziert:

      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;

das ergebnis ist times: 99999 fwrite: 217 WriteFile: 171

Um den Schreibvorgang für API-Dateien zu beschleunigen, gehen Sie wie folgt vor:

Rufen Sie FlushFileBuffers nicht explizit auf. , Daten im Systemcache werden bei Bedarf auf die Festplatte geschrieben.

Holen Sie sich einen Puffer für WriteFile, genau wie fwrite, da API-Aufrufe mehr Zeit kosten als nur memcpy. Rufen Sie WriteFile auf, wenn der Puffer voll ist.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage