2008-10-10 8 views
69

Ich habe Memory-Mapped-Dateien für ein Projekt recherchiert und würde mich über die Gedanken von Menschen freuen, die sie entweder vorher benutzt haben oder sich dagegen entschieden haben, und warum?Was sind die Vorteile von Memory-Mapped-Dateien?

Insbesondere Ich bin besorgt über die folgenden, in der Reihenfolge ihrer Bedeutung:

  • Gleichzeitigkeit
  • random access
  • Leistung
  • einfache Bedienung
  • Portabilität

Antwort

44

Ich denke, der Vorteil ist wirklich, dass Sie die Menge der erforderlichen Daten kopieren gegenüber herkömmlichen Methoden zum Lesen einer Datei reduzieren.

Wenn Ihre Anwendung die Daten "in Place" in einer Memory-Mapped-Datei verwenden kann, kann sie kommen, ohne kopiert zu werden; Wenn Sie einen Systemaufruf verwenden (z. B. Linux pread()), bedeutet dies normalerweise, dass der Kernel die Daten aus seinen eigenen Puffern in den Benutzerbereich kopiert. Dieses zusätzliche Kopieren benötigt nicht nur Zeit, sondern verringert auch die Effektivität der Caches der CPU, indem auf diese zusätzliche Kopie der Daten zugegriffen wird.

Wenn die Daten tatsächlich von der Platte gelesen werden müssen (wie bei physischen E/A), muss das Betriebssystem sie trotzdem einlesen, ein Seitenfehler ist wahrscheinlich nicht leistungsmäßig besser als ein Systemaufruf , aber wenn nicht (dh bereits im OS-Cache), sollte die Leistung theoretisch viel besser sein.

Auf der anderen Seite gibt es keine asynchrone Schnittstelle zu Memory-Mapped-Dateien - wenn Sie versuchen, auf eine Seite zuzugreifen, die nicht zugeordnet ist, erzeugt es einen Seitenfehler und dann wartet der Thread auf die E/A.

Der offensichtliche Nachteil bei speicherabgebildeten Dateien liegt bei einem 32-Bit-Betriebssystem - Sie können den Adressraum problemlos nutzen.

+2

Unter Windows mindestens können Sie mehrere 32bit Ansichten einer größeren mmap-Datei zuordnen - die effizienter sein kann als der Versuch, mit sehr großen Dateien mit regulären CRT-Funktion zu behandeln –

+0

@MarkR Sie schrieb "seine zusätzliche Kopie braucht nicht nur Zeit , ** verringert jedoch die Effektivität der Caches der CPU durch Zugriff auf diese zusätzliche Kopie der Daten. ** ". (** Betonung ** meins). Können Sie bitte erklären, wie die zusätzliche Pufferkopie im Kernel die Effektivität der Caches der CPU verhindert? – Geek

+2

@Geek Zugriff doppelt so viel Speicher = doppelt so viel Cache verschwendet (sehr ungefähr). – immibis

1

Nebenläufigkeit wäre ein Problem. Zufälliger Zugang ist einfacher Leistung ist gut zu großartig. Benutzerfreundlichkeit. Nicht so gut. Portabilität - nicht so heiß.

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

18

Mit Speicherkarten verknüpfte Dateien können entweder den Lese-/Schreibzugriff ersetzen oder die gleichzeitige Freigabe unterstützen. Wenn Sie sie für einen Mechanismus verwenden, erhalten Sie auch den anderen.

Anstatt zu suchen und zu schreiben und in einer Datei zu lesen, ordnen Sie sie in den Speicher ein und greifen einfach auf die Bits zu, wo Sie sie erwarten.

Dies kann sehr praktisch sein, und abhängig von der virtuellen Speicherschnittstelle kann die Leistung verbessern. Die Leistungsverbesserung kann auftreten, weil das Betriebssystem jetzt diese frühere "Datei-I/O" zusammen mit all Ihren anderen programmatischen Speicherzugriffen verwaltet und (theoretisch) die Paging-Algorithmen usw. einsetzen kann, die es bereits zur Unterstützung verwendet virtueller Speicher für die Rest Ihres Programms. Es hängt jedoch von der Qualität Ihres zugrunde liegenden virtuellen Speichersystems ab. Anekdoten, die ich gehört habe, sagen, dass die Solaris und * BSD virtuellen Speichersysteme möglicherweise bessere Leistungsverbesserungen zeigen als das VM-System von Linux - aber ich habe keine empirischen Daten, um dies zu untermauern. YMMV.

Nebenläufigkeit kommt ins Bild, wenn Sie die Möglichkeit mehrerer Prozesse betrachten, die dieselbe "Datei" über zugeordneten Speicher verwenden. Wenn beim Lesen/Schreiben-Modell zwei Prozesse in denselben Bereich der Datei geschrieben werden, kann man ziemlich sicher sein, dass eine der Daten des Prozesses in der Datei eintrifft und die Daten des anderen Prozesses überschreibt. Du würdest den einen oder den anderen bekommen - aber nicht eine seltsame Vermischung. Ich muss zugeben, dass ich mir nicht sicher bin, ob dies ein Verhalten ist, das von irgendeinem Standard vorgeschrieben wird, aber darauf kann man sich ziemlich verlassen. (Es ist eigentlich eine gute Folge Frage!)

In der abgebildeten Welt, im Gegensatz, stellen Sie sich zwei Prozesse "Schreiben". Sie tun dies, indem sie "Speicherspeicher" ausführen, was dazu führt, dass das Betriebssystem die Daten auf die Festplatte auslagert - schließlich. In der Zwischenzeit kann jedoch mit überlappenden Schreibvorgängen gerechnet werden.

Hier ist ein Beispiel. Angenommen, ich habe zwei Prozesse, die jeweils 8 Bytes bei Offset 1024 schreiben. Prozess 1 schreibt '11111111' und Prozess 2 schreibt '22222222'. Wenn sie Datei-I/O benutzen, dann können Sie sich vorstellen, dass tief im O/S ein Puffer voll von 1s und ein Puffer voll von 2s ist, beide auf die gleiche Stelle auf der Platte geleitet. Einer von ihnen wird zuerst da sein, und der andere eine Sekunde. In diesem Fall gewinnt der zweite. Allerdings, wenn ich die Speicher-Mapped-Datei Ansatz verwenden, wird Prozess 1 gehen zu einem Speicher von 4 Bytes, gefolgt von einem anderen Speicher von 4 Bytes (nehmen wir an, dass nicht die maximale Speichergröße).Prozess 2 wird dasselbe tun. Basierend auf, wenn die Prozesse ausführen, können Sie eine der folgenden Aktionen zu erwarten:

11111111 
22222222 
11112222 
22221111 

Die Lösung hierfür ist ausdrücklich gegenseitigen Ausschluss verwenden - das ist wahrscheinlich eine gute Idee, auf jeden Fall ist. Sie waren irgendwie darauf angewiesen, dass das O/S das "Richtige" in dem E/A-Schreib-/Lese-Fall von Dateien sowieso macht.

Die Klasse wechselseitiges Ausschließen ist der Mutex. Für Speicher-Mapping-Dateien würde ich vorschlagen, dass Sie sich einen Speicher-gemappten Mutex ansehen, der unter Verwendung von (z. B.) pthread_mutex_init() verfügbar ist.

Bearbeiten mit einem Fehler: Wenn Sie zugeordnete Dateien verwenden, gibt es eine Versuchung, Zeiger auf die Daten in der Datei einzubetten, in der Datei selbst (denken Sie daran, verknüpfte Liste in der zugeordneten Datei gespeichert). Das möchten Sie nicht tun, da die Datei möglicherweise zu verschiedenen Zeiten oder in verschiedenen Prozessen unter verschiedenen absoluten Adressen abgebildet wird. Verwenden Sie stattdessen Offsets innerhalb der zugeordneten Datei.

43

Ich habe eine Memory-Mapped-Datei verwendet, um eine Auto-Vervollständigen-Funktion 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 mit fester Größe, die nach dem Schlüsselfeld sortiert sind.

Zur Laufzeit wird die Datei im Speicher zugeordnet, in ein C-artiges struct-Array umgewandelt, und wir führen eine Binärsuche durch, um übereinstimmende Teilenummern zu finden. Nur ein paar Speicherseiten der Datei werden tatsächlich von der Festplatte gelesen - egal, welche Seiten während der binären Suche getroffen werden.

  • Parallelität - Ich hatte ein Implementierungsproblem, wo es manchmal Speicher die Datei mehrmals im selben Prozessraum zuordnen würde. Dies war, wie ich mich erinnere, ein Problem, weil das System manchmal keinen ausreichend großen freien Block virtuellen Speichers finden konnte, um die Datei zuzuordnen. Die Lösung bestand darin, die Datei nur einmal zuzuordnen und alle Aufrufe zu thotten. Im Nachhinein wäre es mit einem vollen Windows-Dienst cool gewesen.
  • Random Access - Die binäre Suche ist sicherlich Random Access und blitzschnell
  • Leistung - Die Suche ist extrem schnell. Wenn Benutzer ein Popup-Fenster eingeben, in dem eine Liste mit übereinstimmenden Produktteilenummern angezeigt wird, schrumpft die Liste mit der weiteren Eingabe. Es gibt keine merkliche Verzögerung beim Tippen.
  • +1

    Wäre die binäre Suche nicht langsam, da die Seiten für jeden Versuch eingelesen werden? Oder ist das Betriebssystem intelligent genug, um effizient damit umgehen zu können? – jjxtra

    +1

    Ich nehme an, die Verwendung von Memory-Mapped I/O ist verschwenderisch für die binäre Suche, da die Suche nur auf ein paar einzelne Schlüssel in relativ weit entfernten Speicherorten zugreifen wird, aber das Betriebssystem wird in 4k Seiten für jede solche Anfrage laden. Aber andererseits ändert sich die Datei mit den Teilen nicht viel, also hilft der Cache, das zu verdecken. Aber streng genommen glaube ich, dass traditionelles Suchen/Lesen hier besser wäre. Schließlich, 1 Mil ist nicht viel in diesen Tagen. Warum nicht alles im RAM behalten? –

    +2

    @ the Swine und PsychoDad meine ursprüngliche Antwort war von 2008 und die tatsächliche Implementierung dieser Memory-Mapping Auto-Vervollständigen-Funktion war um 2004-2005 oder so. Das Laden von 800-1000 MB physischem Speicher zum Laden der gesamten Datei war keine gute Lösung für unsere Benutzerbasis. Die Memory-Mapped-Lösung war sehr schnell und effizient. Es hat gekickt und ich erinnere mich liebevoll an meine frühen Junior-Entwickler-Tage. :) –

    Verwandte Themen