Gepufferter Dateizugriff

Ich bin mir sicher, dass es eine fertige Lösung zu folgendem Problem gibt, aber ich finde sie gerade nicht:

Ich möchte Daten aus großen Files lesen und schreiben und das möglichst schnell.
Am liebsten würde ich die gesamte Datei in einen C-Array oder einen Vector
laden und mit dem Arrayindex auf die Bytes einzeln zugreifen.
Bei Files, die größer als der Arbeitsspeicher sind (Größenordnung GB)
ist das keine gute Idee. Also muss man einzelne Bereiche der Datei
in den Speicher laden und alle Operationen auf diesm Bereich machen
und dann wieder mit der Datei synchronisieren.

Ich möchte aber nicht immer überprüfen, ob ich am Ende des Bereichs angekommen
bin und die Daten selbst schreiben und laden. Ich hätte gerne ein Interface,
das mir mit einem Index Zugriff auf die ganze Datei bietet. Element 0 ist dann
z.B. das erste Byte, und Element FS-1 ist das letzte Byte der Datei, wenn
FS die Dateigröße in Byte ist. Ein Teil der Datei, der gerade bearbeitet wird,
ist im Speicher und wenn ich auf Elemente zugreife, die nicht im Speicher sind,
dann wird der gepufferte Bereich in die Datei geschrieben, falls ich dort was
verändert habe und es wird ein neuer Bereich in den Speicher geladen.

Arbeiten mit großen Dateien und das mit guter Performance ist ja jetzt nicht
so was außergewöhnliches. Da müsste es doch schon eine Klasse dafür geben.

Hallo,

Ich möchte Daten aus großen Files lesen und schreiben und das
möglichst schnell.
Am liebsten würde ich die gesamte Datei in einen C-Array oder
einen Vector
laden und mit dem Arrayindex auf die Bytes einzeln zugreifen.
Bei Files, die größer als der Arbeitsspeicher sind
(Größenordnung GB)
ist das keine gute Idee. Also muss man einzelne Bereiche der
Datei
in den Speicher laden und alle Operationen auf diesm Bereich
machen
und dann wieder mit der Datei synchronisieren.

Es ist normalerweise aus Performancegründen eine sehr schlechte Idee, in einer Datei an beliebigen Stellen lesen zu wollen.
Deshalb liest/bearbeitet man die Datei überlicherweise in Blöcken, und lässt den Programmier auch spüren, dass er seine Dateizugriffe auf den aktuellen Block beschränken soll.

Was willst du denn für eine Aufgabe erledigen, die random access auf eine gesamte, große Datei erfordert? Vielleicht gibt es ja eine bessere Lösung.

Such mal mit Google nach „Memory Mapped I/O“ oder „memory mapped file“, vielleicht ist das ja was für dich (wobei ich keine Ahnung habe, wie gut das mit großen Dateien funktioniert).

Grüße,
Moritz

Hallo Moritz,

Es ist normalerweise aus Performancegründen eine sehr
schlechte Idee, in einer Datei an beliebigen Stellen lesen zu
wollen.
Deshalb liest/bearbeitet man die Datei überlicherweise in
Blöcken, und lässt den Programmier auch spüren, dass er seine
Dateizugriffe auf den aktuellen Block beschränken soll.

ja, das ist klar. Genau deswegen möchte ich ja einen gepufferten
Zugriff, dass automatisch der Block in den Speicher geladen wird,
auf dem gerade gearbeitet wird. Aber ich möchte mich nicht mit
Arraygrenzen und Indexoffsets herumschlagen, wenn es sowas vielleicht
doch schon irgendwo fertig gibt.

Was willst du denn für eine Aufgabe erledigen, die random
access auf eine gesamte, große Datei erfordert? Vielleicht
gibt es ja eine bessere Lösung.

Na, ganz random access ist es nicht, sonst bräuchte ich ja gar
nicht zu puffern. Die Aufgabe ist folgende:
Ich hab unter OpenSuse den qemu laufen und emuliere mittlerweile
mein Win98 für die paar wenigen Anwendungen, die ich dann doch immer
wieder mal aus der Windows-Welt brauche. Dafür hab ich das File-System
in einem iso-image. Da aber der Emulator nicht gerade besonders schnell
ist, möchte ich mein Win möglichst schlank halten und alles, was das
System langsam macht draußen halten. Das erfordert viel probieren und
immer mal wieder ein altes image zurückspielen. Jetzt sind aber so 2GB
iso-Image recht unhandlich.

Also wollte ich mir ein binäres diff und patch-utility schreiben, das nur
jeweils die Differenz zu einem „Referenz-image“ speichert.
Ok, das ist jetzt weit ausgeholt. Aber für das diff muss ich über 2
files mit je 2GB laufen. Für das Patch muss ich das ganze wieder
rückwärts machen. Einfach wäre ein XOR bitweise und dann lauflängen-Codieren,
was mir ewig lange Ketten von Nullen rausschmeißt. Übrig bleiben im
Wesentlichen die Änderungen gegenüber dem Referenz-Image.

Was aber, wenn sich die Position von Dateien innerhalb des Images um ein Stück
verschiebt? dann muss ich ein Stück vor und zurück und vergleichen, ob die
passende Zeichenkette wo anders (aber immer noch etwa in der Nähe) in dem
Image vorkommt. I.d.R. wird sich das nicht weit verschieben, aber halt ein
Stückchen. Dafür brauche ich den Buffer, der dann für die eine und für die
andere Datei u.U. auch etwas versetzt läuft, eben je nach dem, wo der
Algorithmus die beste Übereinstimmung findet.

Beide Images komplett im Speicher zu haben ist ausgeschlossen.
Natürlich kann ich das blockweise Laden auch selbst machen. Ich dachte nur,
das ist doch eine Standard-Aufgabe, die bestimmt auch beim Brennen oder
Rippen von CDs/DVDs vorkommt und da müsste es doch wohl eine Library oder
etwas geben.

Such mal mit Google nach „Memory Mapped I/O“ oder „memory
mapped file“, vielleicht ist das ja was für dich (wobei ich
keine Ahnung habe, wie gut das mit großen Dateien
funktioniert).

Das nimmt einem das Laden und Speichern ab. Wenn die Datei aber nicht in den
RAM passt, dann muss man sich auch hier selbst mit der Unterteilung in Blöcke
auseinander setzen und die Array-Indizes entsprechend mit einem Offset in
Positionen im File umrechnen. Naja, aber wenn es das wohl doch nicht
fertig gibt, dann muss ich dafür eben doch eine eigene Klasse schreiben.

Grüße, Martin

PS: Ich hab auch schon nach binärem diff gesucht. Das gibt es, aber der
Haken ist bei den meisten Implementierungen gerade der Speicherbedarf.
Auch mit wine usw. hab ich schon rumprobiert. Da hätte man nicht das
Problem mit dem Image - man könnte einfach mit rsync und gzip usw. arbeiten -
nur zu dumm, dass eben nur die gängigsten Anwendungen von wine voll
unterstützt werden

Ich bin mir sicher, dass es eine fertige Lösung zu folgendem
Problem gibt, aber ich finde sie gerade nicht:

Hallo,

selber puffern war Steinzeit. Heute (Windows,Linux) ist dringend davon abzuraten, das kann das Betriebssystem besser und vor allem hat nur und auschliesslich das BS den vollen Überblick über den Einsatz des Speichers. Dein Stichwort heisst unter Windows Memory Mapped File.

Nebenbei bemerkt, eine volle Pufferung mit Random-Zugriff ist auch nicht so ganz trivial.

Gruss Reinhard

Hallo Reinhard,

selber puffern war Steinzeit. Heute (Windows,Linux) ist
dringend davon abzuraten, das kann das Betriebssystem besser
und vor allem hat nur und auschliesslich das BS den vollen
Überblick über den Einsatz des Speichers. Dein Stichwort
heisst unter Windows Memory Mapped File.

…und unter Linux auch (sys/mman.h). Das trifft aber meine
Frage nicht ganz, weil das eben auch nicht so trivial ist,
wenn das File größer ist als der RAM. Dann muss man sich auch
wieder darum kümmern, den passenden Bereich zu mappen und
die Array-Grenzen zu überwachen und, und, und…

Nebenbei bemerkt, eine volle Pufferung mit Random-Zugriff ist
auch nicht so ganz trivial.

ich hab es ja versucht, zu beschreiben, was ich suche.
Ganz random ist der Zugriff auch nicht. Man kann sich schon
zu Nutze machen, dass der Zugriff nicht wild im File hin und her
springt, sondern meist in der Nähe des letzten Zugriffs erfolgt.
…womit wir wieder bei dem Punkt wären, dass es gelegentlich
schon Sinn macht, selbst über die Pufferung nachzudenken. Sonst
könnte ich ja auch einfach mit fseek() irgendwo im Stream zugreifen.

Zum Hintergrund des ganzen habe ich heute Mittag schon in diesem Thread
mehr geschrieben.

Grüße, Martin