mmap () vs. Leseblöcke
Ich arbeite an einem Programm, das die Bearbeitung von Dateien, die möglicherweise 100GB oder mehr in der Größe. Die Dateien enthalten Sätze variabler Länge Datensätze. Ich habe eine erste Implementierung und läuft und bin nun auf der Suche zur Verbesserung der performance, besonders, das zu tun-I/O-effizienter, da die input-Datei gescannt, viele Male.
Gibt es eine Faustregel für die Verwendung von mmap()
versus Lesen in Blöcken über C++'s fstream
Bibliothek? Was ich möchte zu tun ist, Lesen großer Blöcke von der Festplatte in den Puffer -, Prozess-vollständige Datensätze aus dem Puffer, und dann Lesen Sie weiter.
Den mmap()
code könnten sich sehr chaotisch, da mmap
'd Blöcke müssen direkt auf der Seite angepasst-Grenzen (mein Verständnis), und die Datensätze könnten möglicherweise wie über page-Grenzen hinweg. Mit fstream
s, kann ich nur versuchen an den Anfang einer Aufnahme und beginnen zu Lesen, wieder, da sind wir nicht beschränkt auf das Lesen der Blöcke, die sich auf Seite, die Größe Grenzen.
Wie kann ich entscheiden, zwischen diesen beiden Optionen an, ohne das eigentlich schreiben, bis eine vollständige Umsetzung zuerst? Irgendwelche Faustregeln (z.B. mmap()
ist 2x schneller) oder einfache tests?
InformationsquelleAutor der Frage jbl | 2008-09-05
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich war auf der Suche nach dem letzten Wort auf dem mmap - /lese-performance auf Linux und ich kam in einem netten Beitrag (link) auf der Linux kernel mailing Liste. Es ist aus dem Jahr 2000, so gab es viele Verbesserungen bei den IO und virtuellem Speicher im kernel, da dann, aber es erklärt schön den Grund, warum
mmap
oderread
schneller oder langsamer.mmap
hat mehr overhead alsread
(genau wieepoll
hat mehr overhead alspoll
die mehr Aufwand alsread
). Ändern virtual memory mappings ist eine Recht teure operation, die auf einigen Prozessoren für den gleichen Gründen, dass der Wechsel zwischen verschiedenen Prozessen ist teuer.Jedoch
read
Ihre Datei wurde möglicherweise aus dem cache geleert Alter vor. Dies gilt nicht, wenn Sie eine Datei verwenden und sofort entsorgen Sie Sie. (Wenn Sie versuchen zumlock
Seiten, nur um Sie im cache, die Sie versuchen, zu überlisten, den disk-cache und diese Art von Torheiten nur selten hilft die system-performance).Die Diskussion von mmap/Lesen erinnert mich an zwei andere performance-Diskussionen:
Einige Java-Programmierer waren schockiert zu entdecken, dass nicht blockierende I/O ist oft langsamer als die Blockierung I/O, die Sinn, wenn man weiß, dass nicht blockierende I/O erforderlich macht, die mehr syscalls.
Einige andere Netzwerk-Programmierer waren schockiert zu erfahren, dass
epoll
ist oft langsamer alspoll
die macht es durchaus Sinn, wenn Sie wissen, dass die Verwaltungepoll
verlangt noch mehr syscalls.Fazit: Speicher-Karten wenn Sie access-Daten nach dem Zufallsprinzip, halten Sie herum für eine lange Zeit, oder wenn Sie wissen, Sie können es teilen mit anderen Prozessen (
MAP_SHARED
ist nicht sehr interessant, wenn es keine tatsächliche sharing). Lesen von Dateien in der Regel wenn Sie den Zugriff auf Daten sequentiell oder entsorgen Sie es nach der Lektüre. Und wenn eine Methode macht das Programm weniger Komplex, tun dass. Bei vielen realen Fällen gibt es keinen sicheren Weg, um zu zeigen, man ist schneller, ohne Prüfung Ihrer tatsächlichen Anwendung und NICHT ein benchmark.(Sorry für necro ' Ing diese Frage, aber ich war auf der Suche nach einer Antwort und diese Frage immer wieder bis an die Spitze der Google-Ergebnisse.)
InformationsquelleAutor der Antwort Dietrich Epp
Der Hauptleistung Kosten gehen zu disk i/o. "mmap()" ist sicherlich schneller als istream, aber der Unterschied könnte nicht auffallen, da die disk-i/o dominiert wird, Ihren Lauf-Zeiten.
Ich habe versucht, Ben Collins-code-fragment (siehe oben/unten) zu testen, seine Behauptung, dass "mmap() ist Weg schneller" und fand keinen messbaren Unterschied. Siehe meine Kommentare auf seine Antwort.
Ich würde sicherlich nicht empfehlen GESONDERT mmap ' Ing jeder Datensatz wiederum, es sei denn, Ihre "Aufzeichnungen" sind riesig - das wäre schrecklich langsam, erfordern 2-system ruft für jeden Datensatz zu verlieren und möglicherweise die Seite aus dem disk-cache-Speicher.....
In deinem Fall denke ich, dass mmap(), istream und den low-level-open()/read () - Aufrufe werden alle etwa die gleiche sein. Ich würde empfehlen, mmap() in diesen Fällen:
(btw - ich Liebe mmap()/MapViewOfFile()).
InformationsquelleAutor der Antwort Tim Cooper
mmap ist Weg schneller. Sie schreiben einen einfachen Maßstab, um es zu beweisen, zu sich selbst:
versus:
Klar, ich bin das weglassen von details (wie, wie, um zu bestimmen, wenn Sie erreichen das Ende der Datei im Falle, dass Ihre Datei nicht in ein Vielfaches von
page_size
zum Beispiel), aber es sollte wirklich nicht sehr viel komplizierter als das.Wenn Sie können, könnten Sie versuchen, es zu brechen, können Sie Ihre Daten in mehrere Dateien mmap()-ed in der gesamten statt in Teil (viel einfacher).
Ein paar Monaten hatte ich eine halbgare Umsetzung eines Schiebe-Fenster mmap()-ed-stream-Klasse für boost_iostreams, aber niemand kümmerte sich, und ich bekam beschäftigt mit anderen Sachen. Die meisten habe ich leider gelöscht, ein Archiv der alten unvollendeten Projekten vor ein paar Wochen, und das war eines der Opfer 🙁
Update: ich sollte auch hinzufügen der Einschränkung, dass dieser "benchmark" würde ganz anders Aussehen, in Windows da Microsoft implementiert eine nette Datei-cache, die meisten von dem, was Sie tun würden, mit mmap in den ersten Platz. I. e., für Häufig aufgerufene Dateien, können Sie einfach std::ifstream.read() und würde es so schnell wie mmap, da der Datei-cache würde schon das memory-mapping für Sie, und es ist transparent.
Letzten Update: Schauen Sie, die Menschen: über viele verschiedene Plattform-Kombinationen von Betriebssystem-und standard-Bibliotheken und Festplatten und Speicher-Hierarchien kann ich nicht sagen, für sicher, dass das system nennen
mmap
angesehen als eine black box, wird immer immer immer wesentlich schneller alsread
. Das war nicht wirklich meine Absicht, auch wenn meine Worte ausgelegt werden können, dass Art und Weise. Letztlich mein Punkt war, dass die memory-mapped i/o ist im Allgemeinen schneller als byte-basierte i/o; dies ist immer noch wahr. Wenn Sie finden, experimentell, dass es keinen Unterschied gibt zwischen den beiden, dann ist die einzige Erklärung, die scheint vernünftig zu mir, dass Ihre Plattform implementiert-Speicher-Zuordnung unter der Decke in einer Weise, die vorteilhaft ist, um die performance der Aufruferead
. Der einzige Weg, um absolut sicher zu sein, dass Sie über memory-mapped i/o in einer tragbaren Weise zu verwendenmmap
. Wenn Sie kümmern sich nicht um Portabilität und verlassen Sie sich auf die Besonderheiten der Zielplattformen, dann mitread
geeignet sein können ohne dabei messbar zu jeder Leistung.Bearbeiten zu bereinigen Antwort-Liste:
@jbl:
Sicher - ich Schreibe ein C++ - Bibliothek für die Git (ein libgit++, wenn man so will), und ich lief in ein ähnliches problem: ich muss in der Lage sein zu öffnen, einen großen (sehr großen) Dateien und nicht auf die Leistung insgesamt Hund (wie wäre es mit
std::fstream
).Boost::Iostreams
bereits eine mapped_file Quelle, aber das problem war, dass esmmap
ping ganzen Dateien, die Grenzen, die Sie zu 2^(wordsize). Auf 32-bit-Rechnern 4 GB nicht groß genug ist. Es ist nicht unvernünftig zu erwarten, dass.pack
Dateien in Git, werden viel größer als die, so musste ich die Datei zu Lesen, in Abschnitten, ohne Rückgriff auf die regelmäßige Datei-i/o. Unter der HaubeBoost::Iostreams
ich implementiert eine Quelle, die mehr oder weniger einen anderen Blick auf die Interaktion zwischenstd::streambuf
undstd::istream
. Sie könnten auch versuchen, einen ähnlichen Ansatz, indem Sie nur Erbenstd::filebuf
in einemapped_filebuf
ähnlich auch die Erbenstd::fstream
ina mapped_fstream
. Es ist die Interaktion zwischen den beiden, das ist schwer zu bekommen Recht.Boost::Iostreams
hat einen Teil der Arbeit für Sie erledigt, und es bietet auch Haken für Filter und-Ketten, so dass ich dachte, es wäre nützlicher zu implementieren, das es so ist.InformationsquelleAutor der Antwort Ben Collins
Es gibt viele gute Antworten hier schon, dass Sie decken viele der wesentlichen Punkte, also werde ich fügen Sie einfach ein paar Fragen, die ich nicht sehen direkt angesprochen oben. Das heißt, diese Antwort sollte nicht als eine umfassende vor-und Nachteile, sondern eher eine Ergänzung zu anderen Antworten hier.
mmap scheint wie Magie
Den Fall, in dem die Datei bereits vollständig in den Cache geladen1 als Grundlinie2
mmap
könnte scheinen, ziemlich viel wie Magie:mmap
benötigt nur 1 system call um (potenziell) Karte der gesamten Datei, nach der nicht mehr system-Aufrufe benötigt werden.mmap
nicht erforderlich ist, eine Kopie der Datei, die Daten aus dem kernel-in den user-space.mmap
ermöglicht Ihnen den Zugriff auf die Datei, "als Erinnerung", einschließlich der Verarbeitung das mit allem, was fortgeschrittene tricks, die Sie tun können, gegen die Erinnerung, wie der compiler die automatische Vektorisierung, SIMD Interna, prefetching, optimierte in-memory-Analyse-Routinen, OpenMP, etc.In dem Fall, dass die Datei bereits im cache, scheint es unmöglich zu schlagen: Sie einfach direkt auf die kernel-page-cache-Speicher und es kann nicht schneller als das.
Gut, es kann.
mmap ist nicht wirklich Magie, weil...
mmap immer noch pro-Seite arbeiten
Einem primären versteckten Kosten
mmap
vsread(2)
(ist das wirklich das vergleichbare OS-level-Systemaufruf für Lesen Blöcken) ist, dass mitmmap
Sie tun müssen, "einige Arbeit" für jedes 4K-Seite im user-space, obwohl es möglicherweise ausgeblendet werden, indem Sie die page-fault-Mechanismus.Für ein Beispiel einer typischen Implementierung, die nur
mmap
s die gesamte Datei benötigen, um Fehler in so 100 GB /4K = 25-Millionen-Fehler zu Lesen, eine 100 GB Datei. Nun, diese werden kleinere Störungenaber 25 Milliarden page-Fehler ist immer noch nicht super-schnell. Die Kosten für ein geringfügiger Fehler ist wahrscheinlich 100te von nanos-im besten Fall.mmap stützt sich stark auf TLB-Leistung
Nun, Sie können gehen
MAP_POPULATE
zummap
es sagen, auf alle page tables vor der Rückkehr, so sollte es keine Seitenfehler beim Zugriff auf Sie. Nun, das hat das kleine problem, dass es auch liest die gesamte Datei in den RAM, die gehen, um die Luft zu sprengen, wenn Sie versuchen, anzeigen einer 100-GB-Datei, aber wir ignorieren, dass für jetzt3. Der kernel muss pro-Seite arbeiten für die Einrichtung dieser page tables (zeigt sich als kernel-Zeit). Dies endet mit einer wichtigen Kosten in dermmap
Ansatz, und es ist proportional zur Größe der Datei (D. H., es nicht zu bekommen, relativ weniger wichtig, als die Größe der Datei wächst)4.Schließlich auch in den user-space zugreifen auf eine solche Zuordnung ist nicht ganz kostenlos (im Vergleich zu großen Puffer nicht mit Ursprung aus einer Datei-basierten
mmap
) - auch wenn die Seite Tabellen eingerichtet sind, jedem Zugriff auf eine neue Seite geht auf, konzeptionell, entstehen eine TLB-miss. Dammap
ing-eine Datei bedeutet, die Verwendung der page-cache und seine 4K-Seiten, die Sie wieder fallen diese Kosten 25 Millionen mal eine 100GB-Datei.Nun, die tatsächlichen Kosten für die TLB-misses, hängt stark auf, mindestens die folgenden Aspekte Ihres hardware: (a) wie viele TLB-4K einzutragenden Kontakte Sie und wie der rest der übersetzung caching arbeiten ausführt, (b) wie gut die hardware prefetch-befasst sich mit mit dem TLB - z.B., kann prefetch-trigger eine Seite gehen? (c) wie schnell und wie parallel die Seite walking-hardware ist. Auf modernen high-end-x86-Intel-Prozessoren, die Seite walking-hardware ist im Allgemeinen sehr stark: es gibt mindestens 2 parallele Seite Wanderer, eine Seite gehen kann auftreten, gleichzeitig mit der fortgesetzten Ausführung und hardware prefetching kann der Auslöser eine Seite gehen. Also der TLB Auswirkungen auf eine streaming Lesen Sie laden ist ziemlich niedrig - und eine solche Belastung wird oft führen Sie in ähnlicher Weise unabhängig von der Größe der Seite. Andere hardware ist in der Regel viel schlechter, aber!
read() vermeidet diese Probleme
Den
read()
syscall, das ist, was im Allgemeinen zugrunde liegt, das "block-read" - Typ-Anrufe angeboten, z.B. in C, C++ und anderen Programmiersprachen verfügt über eine primäre Nachteil, dass die alle gut bekannt sind:read()
call of N bytes muss kopiert N bytes aus dem kernel in den user-space.Auf der anderen Seite, es vermeidet die meisten der Kosten oben, die Sie nicht brauchen, um Karte in der 25 Millionen 4K-Seiten in Gebrauch Platz. Sie können in der Regel
malloc
einen einzigen Puffer kleinen buffer im user-space und re-verwenden, die immer wieder für alle Ihreread
Anrufe. Auf der kernel-Seite, es gibt fast kein Problem mit 4K Seiten-oder TLB-misses, da alle RAM-Speicher wird in der Regel Linear zugeordnet, indem Sie ein paar sehr große Seiten (z.B. 1 GB Seiten auf x86), so dass die zugrunde liegenden Seiten in den page-cache abgedeckt sind sehr effizient im kernel-space.Also im Grunde haben Sie den folgenden Vergleich, um festzustellen, welche schneller ist für eine einzelne Lesen einer großen Datei:
Ist die zusätzliche pro-Seite arbeiten, impliziert durch die
mmap
Ansatz teurer als die pro-byte-Arbeit zu kopieren den Inhalt der Datei vom kernel in den user-space impliziert durch die Verwendungread()
?Auf vielen Systemen, Sie sind tatsächlich in etwa die Waage halten. Beachten Sie, dass jeder eine Waage mit völlig anderen Parametern der hardware-und OS-stack.
Insbesondere die
mmap
Ansatz wird relativ schneller, wenn:MAP_POPULATE
- Implementierung, die eine möglichst effiziente Verarbeitung großer Karten in den Fällen, in denen zum Beispiel, die zugrunde liegenden Seiten sind zusammenhängend im physikalischen Speicher.... während die
read()
Ansatz wird relativ schneller, wenn:read()
syscall hat gute Kopie performance. E. g., gutecopy_to_user
Leistung auf der kernel-Seite.Die hardware, die oben genannten Faktoren variieren Wild über unterschiedliche Plattformen hinweg, sogar innerhalb der gleichen Familie (z.B. innerhalb der x86-Generationen-und vor allem Markt-Segmente) und auf jeden Fall über Architekturen (z.B. ARM vs x86 vs PPC).
OS Faktoren ständig ändern, wie auch, mit verschiedenen Verbesserungen auf beiden Seiten verursachen einen großen Sprung in der relativen Geschwindigkeit für ein Ansatz oder andere. Eine aktuelle Liste umfasst:
mmap
Fall ohneMAP_POPULATE
.copy_to_user
Methoden inarch/x86/lib/copy_user_64.S
z.B. mitREP MOVQ
wenn es schnell ist, die wirklich helfen, dieread()
Fall.1 Diese mehr-oder-weniger umfasst auch den Fall, wo die Datei war nicht vollständig in den Cache geladen, mit zu beginnen, aber wo das OS read-ahead ist gut genug, um es so erscheinen (d.h., die Seite wird in der Regel zwischengespeichert, wenn Sie es wollen). Dies ist eine subtile Frage, aber da der Weg read-ahead arbeitet, ist oft ganz anders zwischen
mmap
undread
Anrufe, und kann weiter eingestellt werden, indem Sie "raten" Anrufe, wie beschrieben in 2.2 ... weil wenn die Datei nicht zwischengespeichert, Ihr Verhalten wird völlig dominiert von IO-Bedenken, einschließlich der Frage, wie sympathisch Ihr Zugriff auf pattern der zugrunde liegenden hardware - und alle Ihre Bemühungen sollten bei der Absicherung solcher Zugang ist so sympathisch, wie möglich, z.B. durch die Nutzung von
madvise
oderfadvise
fordert (und was application-level-änderungen, die Sie vornehmen können, um einen besseren Zugang Muster).3 Sie umgehen könnte, zum Beispiel, indem nacheinander
mmap
ing in windows eine kleinere Größe, sagen wir 100 MB.4 In der Tat, es stellt sich heraus, das
MAP_POPULATE
Ansatz ist (mindestens ein paar hardware - /OS-Kombination) nur etwas schneller als es nicht mit, wahrscheinlich, weil der kernel ist mit faultaround - also die tatsächliche Anzahl der geringfügigen Fehler reduziert sich um einen Faktor 16 oder so.InformationsquelleAutor der Antwort BeeOnRope
Tut mir Leid, Ben Collins verlor seine Schiebefenster mmap-source-code. Das wäre schön, in Steigern.
Ja, die mapping-Datei ist wesentlich schneller. Sie sind im wesentlichen mit der der OS den virtuellen Speicher-subsystem zu verbinden Arbeitsspeicher auf Festplatte und Umgekehrt. Denken Sie an es auf diese Weise: wenn der OS-kernel-Entwickler könnte es machen, um so schneller würde. Denn wenn Sie das tun macht so ziemlich alles schneller: Datenbanken, boot-Zeiten, Programm-Ladezeiten, et cetera.
Den sliding-window-Ansatz ist wirklich nicht so schwierig, wie mehrere continguous-Seiten abgebildet werden kann auf einmal. Also die Größe des Datensatzes ist egal, so lange als der größte von jedem einzelnen Datensatz passen in den Speicher. Das wichtigste ist, verwalten die Buchhaltung.
Wenn ein Datensatz beginnt nicht auf einem getpagesize () - Grenze, dein mapping hat, beginnen Sie auf der vorherigen Seite. Die Länge der region zugeordnet, erstreckt sich von dem ersten byte des Datensatz (nach unten gerundet, wenn notwendig, auf das nächste Vielfache von getpagesize ()), um das Letzte byte des Datensatzes (aufgerundet auf das nächste Vielfache von getpagesize()). Wenn Sie die Bearbeitung eines Datensatzes abgeschlossen haben, können Sie unmap (), und bewegen auf die nächste.
Das ganze funktioniert Prima unter Windows auch mit CreateFileMapping() und MapViewOfFile() (und GetSystemInfo (), um SYSTEM_INFO.dwAllocationGranularity --- nicht SYSTEM_INFO.dwPageSize).
InformationsquelleAutor der Antwort mlbrock
mmap sollte schneller sein, aber ich weiß nicht, wie viel. Es hängt sehr stark von der code. Wenn Sie mit mmap es ist am besten, um mmap-die ganze Datei auf einmal, die machen Sie das Leben viel einfacher. Ein potentielles problem ist, dass wenn die Datei größer als 4 GB (oder in der Praxis die Grenze niedriger, oft zu 2 GB), benötigen Sie ein 64bit-Architektur. Also, wenn Sie eine 32-Umgebung, die Sie wahrscheinlich nicht wollen, es zu benutzen.
Having said that, es kann eine bessere route zur Verbesserung der performance. Sie sagte die input-Datei gescannt, viele Malewenn Sie es Lesen können in einem Durchgang und dann mit ihm getan werden könnte, die potenziell sehr viel schneller.
InformationsquelleAutor der Antwort Leon Timmermans
Ich bin damit einverstanden, dass mmap würd Datei-I/O wird schneller sein, aber während Ihr benchmarking-code nicht sollte das Gegenbeispiel sein etwas optimiert?
Ben Collins schrieb:
Ich würde auch vorschlagen, versucht:
Werden, und darüber hinaus, Sie könnten auch versuchen, indem Sie die Puffer-Größe die gleiche Größe wie eine Seite von virtuellem Speicher, im Falle 0x1000 ist nicht die Größe einer Seite des virtuellen Speichers auf Ihrem Computer... IMHO mmap würd Datei-I/O gewinnt noch immer, aber das sollten Dinge näher.
InformationsquelleAutor der Antwort paxos1977
Vielleicht sollten Sie pre-Prozess die Dateien, so dass jeder Datensatz ist in einer separaten Datei (oder zumindest, dass jede Datei ist eine mmap-Lage, Größe).
Konnte auch Sie nicht alle Verarbeitungsschritte, die für jeden Datensatz, auf, bevor die nächste? Vielleicht würde vermeiden, dass einige der IO-overhead?
InformationsquelleAutor der Antwort Douglas Leeder
Ich erinnere mich, mapping eine riesige Datei, die eine Baumstruktur in den Speicher vor Jahren. Ich war erstaunt von der Geschwindigkeit im Vergleich zu normalen de-Serialisierung, die beinhaltet viel Arbeit in Erinnerung, wie die Zuweisung von Knoten im Baum und Einstellung der Zeiger.
Also in der Tat, ich war Vergleich zu einem einzigen Aufruf von mmap (oder dessen Pendant auf Windows)
gegen viele (VIELE) Aufrufe von operator new und Konstruktor-Aufrufe.
Für diese Art von Aufgabe, mmap ist unschlagbar im Vergleich zu de-Serialisierung.
Natürlich sollte man in steigert verschiebbarer Zeiger.
InformationsquelleAutor der Antwort
Das klingt wie ein guter use-case für multi-threading... ich würde denken, Sie könnten ziemlich leicht setup ein thread zu sein, die Daten zu Lesen, während die andere(N) zu verarbeiten. Das ist ein Weg, um drastisch erhöhen die wahrgenommene Leistung. Nur so ein Gedanke.
InformationsquelleAutor der Antwort Pat Notz
Meiner Meinung nach, mit mmap() "nur" entlastet den Entwickler vom schreiben zu müssen, Ihre eigenen caching-code. In einem einfachen "Lesen durch Datei eactly einmal" Fall, das ist nicht zu schwer sein (obwohl wie mlbrock Punkte aus, die Sie noch speichern Sie die Kopie des Speichers in Prozess-Raum), aber wenn du gehst und zurück in die Datei oder das auslassen von bits und so weiter, ich glaube, dass die kernel-Entwickler haben wahrscheinlich einen besseren job getan Implementierung von caching können als ich...
InformationsquelleAutor der Antwort mike
Ich denke, die größte Sache über mmap ist Potenzial für asynchrones Lesen mit:
Problem ist, dass ich kann nicht finden die richtige MAP_FLAGS, einen Hinweis zu geben, dass dieser Speicher synchronisiert werden aus der Datei so schnell wie möglich.
Ich hoffe, dass MAP_POPULATE gibt den richtigen Tipp für mmap (d.h. es wird nicht versuchen, laden Sie alle Inhalte vor der Rückkehr aus anrufen, aber wird das nicht in async. mit feed_data). Zumindest gibt es bessere Ergebnisse mit dieser Flagge auch das Handbuch sagt, dass es tut nichts, ohne MAP_PRIVATE seit 2.6.23.
InformationsquelleAutor der Antwort ony