Große Dateien sequentiell einlesen



  • Öh.
    ReadFile funktioniert ganz gut 🙂

    Ob ein File Mapping Sinn macht kommt darauf an, weiss ja nicht was du genau mit den eingelesenen Daten machen willst.

    Wenn man die Datei bloss von ganz vorne bis ganz hinten durchlesen möchte (um z.B. einen Hashcode drüber zu rechnen) wird es denke ich nichts schnelleres als einfach ReadFile mit einem grossen Puffer (100MB oder so) geben. Ggf. beim Öffnen (CreateFile) FILE_FLAG_SEQUENTIAL_SCAN mitgeben.
    Will man die Daten da drin irgendwohin kopieren genauso, oder wenn man 2 riesen Files Byte für Byte vergleichen will.

    @Jochen Kalmbach: bei einer Datei die nicht ganz in den Speicher passt wird das 2. mal einlesen mit einem File Mapping auch nicht viel schneller werden 😉

    @Brotherhood: es wäre sicherlich nicht verkehrt zu wissen was du mit den Daten dann machen willst 😉



  • Hallo,

    ersteinmal Danke für die Antworten.

    Es geht darum, dass ich einen sequentiellen Scan über die eingelesenen Daten laufen lassen muss. Dabei handelt es sich aber um Bitdaten, die nur zu Speicherzwecken in größere Dateitypen zusammengesetzt wurden.

    Um es also am Beispiel zu erklären. Ich habe eine Bitsequenz mit 64 Bit die für vier Einträge à 16 Bit steht. Diese 64 Bit kann ich jetzt zum Beispiel in zwei Integer umwandeln und diese dann speichern, dabei ist wichtig, dass wenn möglich jedes Bit genutzt wird.

    Beim Auslesen ist es jetzt eigentlich egal ob die Daten zusammenhängend kommen oder nicht. Es ist halt nur wichtig, dass es effizient mit möglichst wenig Festplattenzugriffen arbeitet.

    Um die Frage mit den Zugriffen zu beantworten: Die Daten werden nur einmal eingelesen. Die restliche Arbeit findet im Hauptspeicher statt.

    Ich hoffe ich konnte ein wenig erklären worum es geht. Ich habe leider selber bis jetzt mehr mit OO-Programmierung in C++ verbracht und nicht so nahe am System gearbeitet. Beim jetzigen Projekt geht es aber um die Effizienz und wie gesagt habe ich damit noch nicht so viel Erfahrung :).

    Vielen Dank nochmals für die Hilfe!



  • Hallo,

    ich hab das ganze jetzt einfach mal mit ReadFile implementiert. Was mir momentan aber nicht gelingt, ist die Buffergröße auf mehrere MB einzustellen.

    Ich hab auch im Internet gesucht, finde aber die Informationen zu ReadFile etc. sehr dürftig. Momentan dauert der sequentielle Scan sehr lange, was aber durch einen optimierten Buffer beschleunigt werden müsste. Nur weiss ich nicht wie ich das bewerkstelligen kann.

    Interessant wäre auch zu wissen, wieviel Bytes mit einem Festplattenzugriff gelesen werden können, gibt es eine Möglichkeit diese Information für ein System auszulesen? Also sozusagen, dass ich mit jedem ReadFile genau soviele Daten einlese, wie es mit einem Festplattenzugriff möglich ist (also im Prinzip die Seitengröße). Über Antworten würde ich mich freuen.

    Viele Dank im voraus!


  • Mod

    Ich bezweifle sehr ob Du durch vergrößern des ReadFile Puffers mehr Speed bekommst. IMHO optimiert das OS sowieso zwischen den Operationen wenn Du die Datei mit FILE_FLAG_SEQUENTIAL_SCAN.

    Große Puffer führen meistens nur wieder zu knappen Speicher und swapping.

    Was ist Dein Problem Spoeicher über mehrere MBs zu allokieren!



  • Hallo,

    das Problem ist, dass ich nicht weiß wie ich es machen kann :). Also ich habe beispielsweise folgenden Zugriff:

    hSrc = CreateFile(_T("C:\\test.dat"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN , 0);
    
    [...]
    
    char pBuffer[256];
    do
    {
        ReadFile(hSrc, pBuffer, sizeof(pBuffer), &dwRead, NULL);
    
        [...]
    
    } while (dwRead != 0);
    

    Je nachdem wie groß ich pBuffer wähle ist schon ein Unterschied in der Zugriffszeit zu messen. Am liebsten würde ich ihn ja so einstellen, dass er immer genau eine Seite liest. Nur weiß ich nicht, wie ich die Seitengröße ermitteln kann, noch ob dies wirklich die richtige Stelle ist um den Buffer einzustellen.

    Viele Dank für die Hilfe!



  • Brotherhood schrieb:

    Hallo,

    das Problem ist, dass ich nicht weiß wie ich es machen kann :). Also ich habe beispielsweise folgenden Zugriff:

    hSrc = CreateFile(_T("C:\\test.dat"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN , 0);
    
    [...]
    
    char pBuffer[256];
    do
    {
        ReadFile(hSrc, pBuffer, sizeof(pBuffer), &dwRead, NULL);
    
        [...]
    
    } while (dwRead != 0);
    

    Je nachdem wie groß ich pBuffer wähle ist schon ein Unterschied in der Zugriffszeit zu messen. Am liebsten würde ich ihn ja so einstellen, dass er immer genau eine Seite liest. Nur weiß ich nicht, wie ich die Seitengröße ermitteln kann, noch ob dies wirklich die richtige Stelle ist um den Buffer einzustellen.

    Viele Dank für die Hilfe!

    Auf dem Stack kannst du nicht viel anlegen (Standard: 1MB), du solltest also den Puffer dynamisch anlegen.
    Die Sektorgrösse der Festplatte (512 Byte) ist übrigens ziemlich egal, die Seitengrösse von Windows (normalerweise 4KB), bzw. die Clustergrösse von NTFS (normale auf 4KB) sind auch nicht wirklich wichtig. Ich habe vor ein paar Jahren (mit noch wesentlich langsameren Platten, und unter Windows 2000) mal ein paar Tests gemacht, damals war die max. Geschwindigkeit bei etwa 512KB Puffergrösse erreicht.

    Ahja, wenn dein Puffer ausreichend gross ist, und eine 2er Potenz, dann wirst du immer genau auf einer Block-Grenze landen, immer genau auf einer Page-Grenze und immer genau auf einer Cluster-Grenze. Also verdoppele deine Puffergrösse einfach immer, solange bis es nimmer schneller wird, oder aber bis du dir den Speicher nimmer leisten kannst.



  • Auf dem Stack kannst du nicht viel anlegen (Standard: 1MB), du solltest also den Puffer dynamisch anlegen.

    Das klingt sehr gut, nur wie mach ich das :). Wäre super, wenn du mir ein kleines Beispiel geben könntest. Zeiger auf Speicher setzen und dann allozieren?

    Viele Grüße und danke für die schnelle Hilfe!



  • #include <vector>
    void foo()
    {
        std::vector<char> buffer(1024*1024*8); // 8 MB buffer
        char* bufptr = &(buffer[0]);
    
        // bufptr verwenden
        // ...
    }
    

    Wichtig ist dabei nur dass du den std::vector in Ruhe lässt solange du dir einen Zeiger auf dessen Inhalt gemerkt hast -- sonst könnte es sein dass der Zeiger ungültig wird (z.B. wenn du push_back/insert/... aufrufst könnte es sein dass der vector einen neuen Speicherplatz anfordern muss).



  • Vielen Dank für den Code. Ich war das Wochenende aufgrund einer Grippe ans Bett gefesselt und werde mich jetzt voller Elan daran machen, obigen Code einmal auszuprobieren.


  • Mod

    hustbaer schrieb:

    std::vector<char> buffer(1024*1024*8); // 8 MB buffer
    }
    

    Warum nicht new oder CHeapPtr<>?



  • Martin Richter schrieb:

    hustbaer schrieb:

    std::vector<char> buffer(1024*1024*8); // 8 MB buffer
    }
    

    Warum nicht new oder CHeapPtr<>?

    Weil ich die Klasse garnicht kannte 🙂
    Und wenn ich sie gekannt hätte ... weil ich ne Abneigung gegen MFC & ATL hab' 😃
    (wobei noch lieber ATL als MFC...)


Anmelden zum Antworten