WriteFile richtig benutzen



  • Hallo an alle,

    ich möchte die WinAPI (CreateFile , WriteFile , ...) nutzen um Daten in einer Datei zu speichern.
    Nun habe ich dank Google erfahren, dass beim naiven benutzen von WriteFile die Performance, im Vergleich zu der C API (stdio), rapide abnimmt.

    // Beispiel für typische Nutzung der C API
    FILE* pFile;
    char buffer[size];
    pFile = fopen( "file" , "wb" );
    
    while( condition )
    {
        // Fülle Puffer
        fwrite( buffer , sizeof(char) , size , pFile );
    }
    fclose (pFile);
    
    // Naiver Ansatz mit WriteFile, wenn man C API gewohnt ist
    HANDLE pFile = CreateFile( "file" , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL );
    char buffer[size];
    
    while( condition )
    {
        // Fülle Puffer
        WriteFile( pFile , buffer , size , &written_bytes , NULL );
    }
    CloseHandle( pFile );
    

    Der Grund für den Geschwindigkeitsverlust scheint die fehlende Pufferung von WriteFile gegenüber fwrite zu sein. Die Frage ist also nun, was ich bei der WinAPI beachten muss (Puffergröße o.Ä.) um dieselbe (oder bessere) Performance gegenüber fwrite zu bekommen?

    MfG

    shft

    PS: Ich habe den Performanceverlust selber noch nicht getestet, es handelt sich hierbei um eine präventive Maßnahme (das Fragen im Forum), um die API von Beginn an richtig zu nutzen.

    PPS: Die Benutzung von stdio.h oder fstream ist leider ausgeschlossen!



  • WriteFile tut schon selbst puffern. Dass viele WriteFile Aufrufe mit wenig Daten langsamer sind als viele äquivalente fwrite Aufrufe liegt daran, dass WriteFile ein Kernel-Call ist.
    fwrite ist dagegen in der Runtime Library des Compilers implementiert. Dadurch kann es mehrere fwrite Aufrufe puffern und dann mit nur einem WriteFile Aufruf rausschreiben.
    Dadurch muss nicht so oft zwischen Kernelmode und Usermode hin und her gewechselt werden, und dadurch wird die Sache schneller.

    Wenn du verschiedene Puffergrössen vergleichst, wirst du eine Kurve bekommen wo der Throughput bei grösser werdender Puffergrösse erst stark ansteigt und sich dann asymptotisch einem Maxmialwert nähert.
    Wo der "Knick" in dieser Kurve ist, bzw. wie gross du den Puffer machen musst um auf sagen-wir-mal 80% oder 90% des Maxmialwerts zu kommen kann ich dir nicht sagen. Ist auch sicher von der Hardware abhängig.

    Du kannst es aber sehr einfach selbst austesten. Mach ein kleines Testprogramm das Stücke einer bestimmten (konfigurierbaren) Grösse mit WriteFile in ein File schreibt, bis dieses eine bestimmte (immer gleiche) Grösse erreicht hat.
    Und dann führst du das Testprogramm mit verschiedenen "Stückgrössen" aus. z.B. einfach die Zweierpotenzen, also beginnend mit 1, dann 2, 4, 8, 16 ... bis vielleicht 16777216 (=16 MiB).
    Aus der benötigten Zeit rechnest du dir einen MiB/Sekunde Wert aus, kopierst die Ergebnisse in ein Excel-Sheet und lässt dir ein Diagramm davon anzeigen.

    Dann weisst du es 🙂

    Das ist ne gute Übung und ich persönlich finde solche Sachen auch immer interessant.

    Ansonsten... wenn eine grosse Puffergrösse keine besonderen Nachteile mitsich bringt (=genug RAM da, dass die Daten erst verspätet im File landen ist egal etc.), dann kannst du natürlich auch einfach einen Wert von ein paar MiB wählen und es damit gut sein lassen. Denn dass die Kurve oberhalb von 1 MiB sehr flach sein wird (=wenig rauszuholen mit grösseren Puffern), davon kannst du mal ausgehen.



  • Super, dann teste ich verschiedene Größen 🙂

    Danke dir!


Log in to reply