Was sind die Vorteile von Dateien mit Speicherzuordnung?

89

Ich habe nach Speicherzuordnungsdateien für ein Projekt gesucht und würde mich über Gedanken von Personen freuen, die sie entweder zuvor verwendet oder gegen deren Verwendung entschieden haben, und warum?

Insbesondere bin ich besorgt über Folgendes in der Reihenfolge seiner Bedeutung:

  • Parallelität
  • wahlfreier Zugriff
  • Performance
  • Benutzerfreundlichkeit
  • Portabilität
Robottobor
quelle

Antworten:

56

Ich denke, der Vorteil ist wirklich, dass Sie den Datenaufwand gegenüber herkömmlichen Methoden zum Lesen einer Datei reduzieren.

Wenn Ihre Anwendung die Daten "an Ort und Stelle" in einer Speicherzuordnungsdatei verwenden kann, können sie ohne Kopieren eingehen. Wenn Sie einen Systemaufruf verwenden (z. B. Linux's pread ()), kopiert der Kernel normalerweise die Daten aus seinen eigenen Puffern in den Benutzerbereich. Dieses zusätzliche Kopieren nimmt nicht nur Zeit in Anspruch, sondern verringert auch die Effektivität der CPU-Caches, indem auf diese zusätzliche Kopie der Daten zugegriffen wird.

Wenn die Daten tatsächlich von der Disc gelesen werden müssen (wie bei physischen E / A), muss das Betriebssystem sie dennoch einlesen. Ein Seitenfehler ist in Bezug auf die Leistung wahrscheinlich nicht besser als ein Systemaufruf, aber wenn dies der Fall ist nicht (dh bereits im Betriebssystem-Cache), die Leistung sollte theoretisch viel besser sein.

Auf der anderen Seite gibt es keine asynchrone Schnittstelle zu Dateien mit Speicherzuordnung. Wenn Sie versuchen, auf eine Seite zuzugreifen, die nicht zugeordnet ist, wird ein Seitenfehler generiert, und der Thread wartet auf die E / A.


Der offensichtliche Nachteil von Dateien mit Speicherzuordnung liegt in einem 32-Bit-Betriebssystem - Ihnen kann leicht der Adressraum ausgehen.

MarkR
quelle
4
Zumindest unter Windows können Sie mehrere 32-Bit-Ansichten einer größeren mmap-Datei zuordnen - was effizienter sein kann als der Versuch, mit sehr großen Dateien mit der regulären CRT-Funktion umzugehen
Martin Beckett
@MarkR Sie haben geschrieben, "sein zusätzliches Kopieren braucht nicht nur Zeit, sondern verringert auch die Effektivität der CPU-Caches, indem auf diese zusätzliche Kopie der Daten zugegriffen wird . " ( Hervorhebung von mir). Können Sie bitte erklären, wie die zusätzliche Pufferkopie im Kernel die Effektivität der CPU-Caches beeinträchtigt?
Geek
4
@Geek Zugriff auf doppelt so viel Speicher = doppelt so viel Cache verschwendet (sehr ungefähr).
user253751
49

Ich habe eine Speicherzuordnungsdatei verwendet, um eine Funktion zum automatischen Vervollständigen zu implementieren, während der Benutzer tippt. Ich habe weit über 1 Million Produktteilenummern in einer einzigen Indexdatei gespeichert. Die Datei enthält einige typische Header-Informationen, aber der Großteil der Datei besteht aus einem riesigen Array von Datensätzen fester Größe, die nach dem Schlüsselfeld sortiert sind.

Zur Laufzeit wird die Datei speicherabgebildet, in ein Array im CStil umgewandelt struct, und wir führen eine binäre Suche durch, um übereinstimmende Teilenummern zu finden, während der Benutzer sie eingibt. Nur wenige Speicherseiten der Datei werden tatsächlich von der Festplatte gelesen - unabhängig davon, welche Seiten während der binären Suche getroffen werden.

  • Parallelität - Ich hatte ein Implementierungsproblem, bei dem die Datei manchmal mehrmals im selben Prozessbereich gespeichert wurde. Wie ich mich erinnere, war dies ein Problem, da das System manchmal keinen ausreichend großen freien Block des virtuellen Speichers fand, um die Datei zuzuordnen. Die Lösung bestand darin, die Datei nur einmal zuzuordnen und alle Aufrufe an sie zu senden. Im Nachhinein wäre die Verwendung eines vollständigen Windows-Dienstes cool gewesen.
  • Direktzugriff - Die binäre Suche ist mit Sicherheit Direktzugriff und blitzschnell
  • Leistung - Die Suche ist extrem schnell. Wenn Benutzer ein Popup-Fenster eingeben, wird eine Liste mit übereinstimmenden Produktteilenummern angezeigt. Die Liste wird verkleinert, wenn sie weiter tippen. Beim Tippen ist keine Verzögerung erkennbar.
Brian Ensink
quelle
1
Wäre die binäre Suche nicht langsam, da die Seiten bei jedem Versuch eingelesen werden? Oder ist das Betriebssystem intelligent genug, um effizient damit umzugehen?
jjxtra
1
Ich nehme an, die Verwendung von speicherabgebildeten E / A ist für die binäre Suche etwas verschwenderisch, da bei der Suche nur auf wenige einzelne Schlüssel an relativ weit entfernten Speicherorten zugegriffen wird, das Betriebssystem jedoch für jede solche Anforderung 4 KB Seiten lädt. Andererseits ändert sich an der Datei mit Teilen nicht viel, sodass der Cache dabei hilft, dies zu vertuschen. Aber genau genommen glaube ich, dass traditionelles Suchen / Lesen hier besser wäre. Schließlich ist 1 mil heutzutage nicht viel. Warum nicht einfach alles im RAM behalten?
das Schwein
5
@the swine and PsychoDad Meine ursprüngliche Antwort war aus dem Jahr 2008 und die tatsächliche Implementierung dieser Funktion zur automatischen Vervollständigung von Speicherzuordnungen erfolgte zwischen 2004 und 2005 oder so. Der Verbrauch von 800-1000 MB physischem Speicher zum Laden der gesamten Datei war keine gute Lösung für unsere Benutzerbasis. Die Speicherzuordnungslösung war sehr schnell und effizient. Es hat mich umgehauen und ich erinnere mich noch gut an meine frühen Tage als Junior-Entwickler. :)
Brian Ensink
@ BrianEnsink: ok, das macht Sinn. Ich hatte nicht erwartet, dass jeder Eintrag so viel wie 1kB sein würde. dann wird der ausgelagerte Ansatz natürlich effizienter. schön :)
das Schwein
22

Speicherzugeordnete Dateien können verwendet werden, um entweder den Lese- / Schreibzugriff zu ersetzen oder die gleichzeitige Freigabe zu unterstützen. Wenn Sie sie für einen Mechanismus verwenden, erhalten Sie auch den anderen.

Anstatt in einer Datei zu suchen, zu schreiben und zu lesen, ordnen Sie sie dem Speicher zu und greifen einfach auf die Bits zu, von denen Sie erwarten, dass sie sich befinden.

Dies kann sehr praktisch sein und je nach Schnittstelle des virtuellen Speichers die Leistung verbessern. Die Leistungsverbesserung kann auftreten, weil das Betriebssystem nun diese frühere "Datei-E / A" zusammen mit all Ihren anderen programmgesteuerten Speicherzugriffen verwalten kann und (theoretisch) die Paging-Algorithmen usw. nutzen kann, die es bereits zur Unterstützung verwendet virtueller Speicher für den Rest Ihres Programms. Dies hängt jedoch von der Qualität Ihres zugrunde liegenden virtuellen Speichersystems ab. Anekdoten Ich habe gehört, dass die virtuellen Speichersysteme Solaris und * BSD möglicherweise bessere Leistungsverbesserungen aufweisen als das VM-System von Linux - aber ich habe keine empirischen Daten, um dies zu belegen. YMMV.

Parallelität kommt ins Spiel, wenn Sie die Möglichkeit in Betracht ziehen, dass mehrere Prozesse dieselbe "Datei" über den zugeordneten Speicher verwenden. Wenn im Lese- / Schreibmodell zwei Prozesse in denselben Bereich der Datei schreiben, können Sie ziemlich sicher sein, dass eine der Prozessdaten in der Datei ankommt und die Daten des anderen Prozesses überschreibt. Du würdest das eine oder das andere bekommen - aber keine seltsame Vermischung. Ich muss zugeben, dass ich nicht sicher bin, ob dies ein Verhalten ist, das von einem Standard vorgeschrieben wird, aber darauf können Sie sich so ziemlich verlassen. (Es ist eigentlich eine gute Folgefrage!)

Stellen Sie sich im Gegensatz dazu in der kartierten Welt zwei Prozesse vor, die beide "schreiben". Sie tun dies, indem sie "Speicher" speichern, was dazu führt, dass der O / S die Daten auf die Festplatte auslagert - schließlich. In der Zwischenzeit ist jedoch mit überlappenden Schreibvorgängen zu rechnen.

Hier ist ein Beispiel. Angenommen, ich habe zwei Prozesse, die beide 8 Bytes am Offset 1024 schreiben. Prozess 1 schreibt '11111111' und Prozess 2 schreibt '22222222'. Wenn sie Datei-E / A verwenden, können Sie sich vorstellen, dass sich tief im Betriebssystem ein Puffer voller 1s und ein Puffer voller 2s befinden, die beide an dieselbe Stelle auf der Festplatte geleitet werden. Einer von ihnen wird zuerst dort ankommen und der andere eine Sekunde. In diesem Fall gewinnt der zweite. Allerdings , wenn ich die Memory-Mapped - Datei Ansatz verwende, Verfahren 1 wird eine Speicherspeicher von 4 Bytes gehen, durch einen anderen Speicher Speicher von 4 Bytes gefolgt (nehmen wir an , that't die maximale Speicherspeichergröße). Prozess 2 wird dasselbe tun. Abhängig davon, wann die Prozesse ausgeführt werden, können Sie Folgendes erwarten:

11111111
22222222
11112222
22221111

Die Lösung hierfür ist der explizite gegenseitige Ausschluss - was wahrscheinlich auf jeden Fall eine gute Idee ist. Sie haben sich sowieso darauf verlassen, dass das Betriebssystem "das Richtige" im E / A-Fall der Lese- / Schreibdatei tut.

Das Grundelement der gegenseitigen Ausgrenzung ist der Mutex. Für speicherabgebildete Dateien würde ich vorschlagen, dass Sie sich einen speicherabgebildeten Mutex ansehen, der mit (z. B.) pthread_mutex_init () verfügbar ist.

Bearbeiten mit einem Punkt: Wenn Sie zugeordnete Dateien verwenden, besteht die Versuchung, Zeiger auf die Daten in die Datei in die Datei selbst einzubetten (denken Sie an die in der zugeordneten Datei gespeicherte verknüpfte Liste). Sie möchten dies nicht tun, da die Datei möglicherweise zu unterschiedlichen Zeiten oder in unterschiedlichen Prozessen an unterschiedlichen absoluten Adressen zugeordnet wird. Verwenden Sie stattdessen Offsets in der zugeordneten Datei.

Moor
quelle
1

Parallelität wäre ein Problem. Direktzugriff ist einfacher Leistung ist gut bis großartig. Benutzerfreundlichkeit. Nicht so gut. Portabilität - nicht so heiß.

Ich habe sie vor langer Zeit auf einem Sonnensystem verwendet, und das sind meine Gedanken.

Paul Nathan
quelle