Ich arbeite an einem Programm, das Dateien verarbeitet, die möglicherweise 100 GB oder mehr groß sein können. Die Dateien enthalten Sätze von Datensätzen variabler Länge. Ich habe eine erste Implementierung in Betrieb genommen und versuche nun, die Leistung zu verbessern, insbesondere um E / A effizienter zu gestalten, da die Eingabedatei viele Male gescannt wird.
Gibt es eine Faustregel für die Verwendung im mmap()
Vergleich zum Lesen in Blöcken über die C ++ - fstream
Bibliothek? Ich möchte große Blöcke von der Festplatte in einen Puffer lesen, vollständige Datensätze aus dem Puffer verarbeiten und dann mehr lesen.
Der mmap()
Code könnte möglicherweise sehr unordentlich werden, da mmap
d-Blöcke an Seitengrößengrenzen liegen müssen (nach meinem Verständnis) und Datensätze möglicherweise über Seitengrenzen hinweg mögen könnten. Mit fstream
s kann ich einfach den Anfang eines Datensatzes suchen und erneut mit dem Lesen beginnen, da wir uns nicht darauf beschränken, Blöcke zu lesen, die an Grenzen der Seitengröße liegen.
Wie kann ich mich zwischen diesen beiden Optionen entscheiden, ohne zuerst eine vollständige Implementierung zu schreiben? Irgendwelche Faustregeln (zB mmap()
ist 2x schneller) oder einfache Tests?
mmap()
ist es 2-6 mal schneller als mit Syscalls, zread()
.Antworten:
Ich habe versucht, das letzte Wort zur mmap / read-Leistung unter Linux zu finden, und bin auf einen netten Beitrag ( Link ) auf der Linux-Kernel-Mailingliste gestoßen. Es ist aus dem Jahr 2000, daher wurden seitdem viele Verbesserungen an E / A und virtuellem Speicher im Kernel vorgenommen, aber es erklärt gut den Grund, warum
mmap
oderread
möglicherweise schneller oder langsamer.mmap
hat mehr Overhead alsread
(genau wieepoll
mehr Overhead alspoll
, was mehr Overhead hat alsread
). Das Ändern von Zuordnungen virtueller Speicher ist auf einigen Prozessoren aus den gleichen Gründen ziemlich teuer, aus denen das Umschalten zwischen verschiedenen Prozessen teuer ist.Jedoch,
read
wurde Ihre Datei möglicherweise vor langer Zeit aus dem Cache gelöscht. Dies gilt nicht, wenn Sie eine Datei verwenden und diese sofort verwerfen. (Wenn Sie versuchen,mlock
Seiten nur zu speichern, um sie im Cache zu halten, versuchen Sie, den Festplatten-Cache zu überlisten, und diese Art von Dummheit trägt selten zur Systemleistung bei.)Die Diskussion über mmap / read erinnert mich an zwei andere Performance-Diskussionen:
Einige Java-Programmierer waren schockiert, als sie feststellten, dass nicht blockierende E / A häufig langsamer sind als blockierende E / A. Dies ist absolut sinnvoll, wenn Sie wissen, dass für nicht blockierende E / A mehr Systemaufrufe erforderlich sind.
Einige andere Netzwerkprogrammierer waren schockiert,
epoll
alspoll
sie erfuhren, dass dies oft langsamer ist alsepoll
. Dies ist durchaus sinnvoll, wenn Sie wissen, dass für die Verwaltung mehr Systemaufrufe erforderlich sind.Fazit: Verwenden Sie Speicherzuordnungen, wenn Sie zufällig auf Daten zugreifen, diese lange aufbewahren oder wenn Sie wissen, dass Sie sie für andere Prozesse freigeben können (
MAP_SHARED
nicht sehr interessant, wenn keine tatsächliche Freigabe erfolgt). Lesen Sie Dateien normal, wenn Sie nacheinander auf Daten zugreifen, oder verwerfen Sie sie nach dem Lesen. Und wenn entweder Methode macht Ihr Programm weniger komplex, tun , dass . In vielen Fällen der realen Welt gibt es keinen sicheren Weg, um zu zeigen, dass einer schneller ist, ohne Ihre tatsächliche Anwendung und NICHT einen Benchmark zu testen.(Es tut mir leid, dass ich diese Frage beantwortet habe, aber ich habe nach einer Antwort gesucht und diese Frage tauchte immer wieder ganz oben in den Google-Ergebnissen auf.)
quelle
mmap
vsread()
in diesem Thread wie in der Vergangenheit zutreffen, kann die Gesamtleistung nicht wirklich durch Addition der Vor- und Nachteile bestimmt werden, sondern nur durch Testen einer bestimmten Hardwarekonfiguration. Beispielsweise ist fraglich, ob "Ein Aufruf von mmap hat mehr Overhead als das Lesen" - yesmmap
muss der Prozessseitentabelle Zuordnungen hinzufügen, aberread
alle gelesenen Bytes vom Kernel in den Benutzerbereich kopieren.mmap
der Overhead geringer ist alsread
bei Lesevorgängen mit einer Größe von mehr als einer Seite (4 KiB). Nun ist es sehr richtig, dass, wenn Sie sparsam und zufällig auf Daten zugreifen möchten, diesmmap
wirklich sehr, sehr gut ist - aber das Gegenteil ist nicht unbedingt der Fall:mmap
Möglicherweise ist es auch für den sequentiellen Zugriff das Beste.mmap
erstellen möchten , der schneller ist, würde ich erwarten, dass mindestens das gesamte Testgerät (Quellcode) mit den tabellarischen Ergebnissen und der Prozessormodellnummer angezeigt wird.mmap
der TLB nur unter ungewöhnlichen Umständen (abermunmap
möglicherweise) geleert wird . Meine Tests umfassten sowohl Mikrobenchmarks (einschließlichmunmap
) als auch "in Anwendung", die in einem realen Anwendungsfall ausgeführt wurden. Natürlich ist meine Bewerbung nicht mit Ihrer Bewerbung identisch, daher sollten die Teilnehmer vor Ort testen. Es ist nicht einmal klar, dassmmap
ein Mikro-Benchmark dies bevorzugt: Erread()
erhält auch einen großen Schub, da der benutzerseitige Zielpuffer im Allgemeinen in L1 bleibt, was in einer größeren Anwendung möglicherweise nicht der Fall ist. Also ja, "es ist kompliziert".Die Hauptkosten für die Leistung werden Festplatten-E / A sein. "mmap ()" ist sicherlich schneller als istream, aber der Unterschied ist möglicherweise nicht erkennbar, da die Festplatten-E / A Ihre Laufzeiten dominieren.
Ich habe versucht, das Codefragment von Ben Collins (siehe oben / unten) auf seine Behauptung zu testen, dass "mmap () viel schneller ist" und keinen messbaren Unterschied festgestellt. Siehe meine Kommentare zu seiner Antwort.
Ich würde sicherlich nicht empfehlen, jeden Datensatz einzeln einzeln zuzuordnen, es sei denn, Ihre "Datensätze" sind riesig - das wäre schrecklich langsam, würde 2 Systemaufrufe für jeden Datensatz erfordern und möglicherweise die Seite aus dem Festplattenspeicher-Cache verlieren ... .
In Ihrem Fall denke ich, dass mmap (), istream und die Aufrufe open () / read () auf niedriger Ebene ungefähr gleich sind. Ich würde mmap () in folgenden Fällen empfehlen:
(Übrigens - ich liebe mmap () / MapViewOfFile ()).
quelle
mmap ist viel schneller. Sie könnten einen einfachen Benchmark schreiben, um sich selbst zu beweisen:
gegen:
Natürlich lasse ich Details aus (wie Sie beispielsweise feststellen können, wann Sie das Ende der Datei erreichen, falls Ihre Datei kein Vielfaches von
page_size
ist), aber es sollte wirklich nicht viel komplizierter sein .Wenn Sie können, können Sie versuchen, Ihre Daten in mehrere Dateien aufzuteilen, die mmap () - statt teilweise (viel einfacher) mmap () - bearbeitet werden können.
Vor ein paar Monaten hatte ich eine halbherzige Implementierung einer mmap () - Ed-Stream-Klasse für Boost-Fenster für boost_iostreams, aber niemand kümmerte sich darum und ich beschäftigte mich mit anderen Dingen. Leider habe ich vor einigen Wochen ein Archiv alter unvollendeter Projekte gelöscht, und das war eines der Opfer :-(
Update : Ich sollte auch den Vorbehalt hinzufügen, dass dieser Benchmark in Windows ganz anders aussehen würde, da Microsoft einen raffinierten Datei-Cache implementiert hat, der das meiste tut, was Sie mit mmap überhaupt tun würden. Das heißt, für Dateien, auf die häufig zugegriffen wird, können Sie einfach std :: ifstream.read () ausführen, und es wäre so schnell wie mmap, da der Dateicache bereits eine Speicherzuordnung für Sie durchgeführt hätte und transparent ist.
Letztes Update : Schauen Sie, Leute: In vielen verschiedenen Plattformkombinationen von Betriebssystem- und Standardbibliotheken sowie Festplatten und Speicherhierarchien kann ich nicht mit Sicherheit sagen, dass der Systemaufruf
mmap
, der als Black Box angesehen wird, immer immer wesentlich schneller sein wird alsread
. Das war nicht genau meine Absicht, auch wenn meine Worte so ausgelegt werden könnten. Letztendlich war mein Punkt, dass speicherabgebildete E / A im Allgemeinen schneller sind als bytebasierte E / A. das ist immer noch wahr . Wenn Sie experimentell feststellen, dass es keinen Unterschied zwischen den beiden gibt, ist die einzige Erklärung, die mir vernünftig erscheint, dass Ihre Plattform die Speicherzuordnung unter der Decke auf eine Weise implementiert, die für die Leistung von Aufrufen von vorteilhaft istread
. Die einzige Möglichkeit, absolut sicher zu sein, dass Sie speicherabgebildete E / A auf tragbare Weise verwenden, ist die Verwendungmmap
. Wenn Sie sich nicht für Portabilität interessieren und sich auf die besonderen Merkmale Ihrer Zielplattformen verlassen können, ist die Verwendungread
möglicherweise geeignet, ohne die Leistung messbar zu beeinträchtigen.Bearbeiten, um die Antwortliste zu bereinigen: @jbl:
Sicher - Ich habe eine C ++ - Bibliothek für Git geschrieben (ein libgit ++, wenn Sie so wollen) und bin auf ein ähnliches Problem gestoßen: Ich musste in der Lage sein, große (sehr große) Dateien zu öffnen und keine Leistung zu haben, um ein totaler Hund zu sein (wie es wäre mit
std::fstream
).Boost::Iostreams
hat bereits eine mapped_file-Quelle, aber das Problem war, dassmmap
ganze Dateien gepingt wurden, was Sie auf 2 ^ (Wortgröße) beschränkt. Auf 32-Bit-Computern sind 4 GB nicht groß genug. Es ist nicht unangemessen zu erwarten, dass.pack
Dateien in Git viel größer werden, daher musste ich die Datei in Blöcken lesen, ohne auf reguläre Datei-E / A zurückgreifen zu müssen. Unter dem Deckmantel von habeBoost::Iostreams
ich eine Quelle implementiert, die mehr oder weniger eine andere Sicht auf die Interaktion zwischenstd::streambuf
und iststd::istream
. Sie können auch einen ähnlichen Ansatz ausprobieren, indem Sie einfachstd::filebuf
in a erbenmapped_filebuf
und in ähnlicher Weisestd::fstream
ina mapped_fstream
. Es ist die Interaktion zwischen den beiden, die schwer zu finden ist.Boost::Iostreams
hat einen Teil der Arbeit für Sie erledigt und bietet auch Haken für Filter und Ketten, daher dachte ich, es wäre nützlicher, es auf diese Weise zu implementieren.quelle
mmap()
eine Seite nach der anderen zu archivieren? Wenn asize_t
groß genug ist, um die Größe der Datei zu speichern (sehr wahrscheinlich auf 64-Bit-Systemen), dann nurmmap()
die gesamte Datei in einem Aufruf.Hier gibt es bereits viele gute Antworten, die viele der wichtigsten Punkte abdecken. Daher möchte ich nur einige Punkte hinzufügen, die ich nicht direkt oben angesprochen habe. Das heißt, diese Antwort sollte nicht als umfassend für die Vor- und Nachteile angesehen werden, sondern als Ergänzung zu anderen Antworten hier.
mmap scheint magisch
Wenn Sie den Fall, in dem die Datei bereits vollständig zwischengespeichert ist 1 als Basis 2 haben ,
mmap
als magisch erscheinen :mmap
Es ist nur ein Systemaufruf erforderlich, um (möglicherweise) die gesamte Datei zuzuordnen. Danach sind keine weiteren Systemaufrufe erforderlich.mmap
erfordert keine Kopie der Dateidaten vom Kernel in den User-Space.mmap
Ermöglicht den Zugriff auf die Datei "als Speicher", einschließlich der Verarbeitung mit allen erweiterten Tricks, die Sie gegen den Speicher ausführen können, z. B. automatische Vektorisierung des Compilers, SIMD- Intrinsics, Prefetching, optimierte In-Memory-Parsing-Routinen, OpenMP usw.Für den Fall, dass sich die Datei bereits im Cache befindet, scheint es unmöglich zu sein: Sie greifen einfach direkt auf den Kernel-Seiten-Cache als Speicher zu und es kann nicht schneller werden.
Nun, das kann es.
mmap ist eigentlich keine Magie, weil ...
mmap arbeitet immer noch pro Seite
Ein primärer versteckter Preis von
mmap
vsread(2)
(was eigentlich der vergleichbare Systemaufruf auf Betriebssystemebene zum Lesen von Blöcken ist ) besteht darin, dassmmap
Sie für jede 4K-Seite im Benutzerbereich "etwas Arbeit" erledigen müssen, auch wenn sie möglicherweise von der Seitenfehlermechanismus.Zum Beispiel muss eine typische Implementierung, die nur
mmap
die gesamte Datei enthält, einen Fehler verursachen, sodass 100 GB / 4K = 25 Millionen Fehler zum Lesen einer 100-GB-Datei vorliegen. Nun, dies werden kleinere Fehler sein , aber 25 Milliarden Seitenfehler werden immer noch nicht superschnell sein. Die Kosten für einen kleinen Fehler liegen wahrscheinlich im besten Fall bei 100 Nanos.mmap hängt stark von der TLB-Leistung ab
Jetzt können Sie an übergeben
MAP_POPULATE
,mmap
um anzuweisen, dass alle Seitentabellen eingerichtet werden sollen, bevor Sie zurückkehren, damit beim Zugriff keine Seitenfehler auftreten. Dies hat das kleine Problem, dass es auch die gesamte Datei in den Arbeitsspeicher liest, was explodieren wird, wenn Sie versuchen, eine 100-GB-Datei zuzuordnen - aber lassen Sie uns dies vorerst ignorieren 3 . Der Kernel muss pro Seite arbeiten , um diese Seitentabellen einzurichten (wird als Kernelzeit angezeigt). Dies ist ein erheblicher Kostenfaktor für denmmap
Ansatz und proportional zur Dateigröße (dh er wird mit zunehmender Dateigröße nicht weniger wichtig) 4 .Selbst im Benutzerbereich ist der Zugriff auf eine solche Zuordnung nicht gerade kostenlos (im Vergleich zu großen Speicherpuffern, die nicht aus einer dateibasierten Zuordnung stammen
mmap
). Selbst wenn die Seitentabellen eingerichtet sind, wird jeder Zugriff auf eine neue Seite ausgeführt. konzeptionell entsteht ein TLB-Fehler. Da dasmmap
Erstellen einer Datei die Verwendung des Seitencaches und seiner 4K-Seiten bedeutet, fallen für eine 100-GB-Datei erneut 25 Millionen Mal Kosten an.Nun hängen die tatsächlichen Kosten dieser TLB-Fehler stark von mindestens den folgenden Aspekten Ihrer Hardware ab: (a) wie viele 4K-TLB-Enties Sie haben und wie der Rest des Übersetzungs-Caching funktioniert (b) wie gut Hardware-Prefetch funktioniert mit dem TLB - kann zB Prefetch einen Seitenlauf auslösen? (c) wie schnell und wie parallel die Page-Walking-Hardware ist. Auf modernen High-End-x86-Intel-Prozessoren ist die Page-Walk-Hardware im Allgemeinen sehr stark: Es gibt mindestens zwei parallele Page-Walker, ein Page-Walk kann gleichzeitig mit der fortgesetzten Ausführung erfolgen, und Hardware-Prefetching kann einen Page-Walk auslösen. Daher ist die Auswirkung des TLB auf eine Streaming- Leselast relativ gering - und eine solche Last wird unabhängig von der Seitengröße häufig ähnlich ausgeführt. Andere Hardware ist jedoch normalerweise viel schlechter!
read () vermeidet diese Fallstricke
Der
read()
Syscall, der im Allgemeinen den "Block Read" -Aufrufen zugrunde liegt, die z. B. in C, C ++ und anderen Sprachen angeboten werden, hat einen Hauptnachteil, den jeder kennt:read()
Aufruf von N Bytes muss N Bytes vom Kernel in den Benutzerbereich kopieren.Auf der anderen Seite werden die meisten der oben genannten Kosten vermieden - Sie müssen nicht 25 Millionen 4K-Seiten in den Benutzerbereich abbilden. Normalerweise können Sie
malloc
einen einzelnen Puffer, einen kleinen Puffer im Benutzerbereich, verwenden und diesen wiederholt für alle Ihreread
Anrufe wiederverwenden . Auf der Kernelseite gibt es fast kein Problem mit 4K-Seiten oder TLB-Fehlern, da der gesamte RAM normalerweise linear mit einigen sehr großen Seiten (z. B. 1 GB Seiten auf x86) zugeordnet wird, sodass die zugrunde liegenden Seiten im Seitencache abgedeckt sind sehr effizient im Kernelraum.Grundsätzlich haben Sie also den folgenden Vergleich, um festzustellen, welche für einen einzelnen Lesevorgang einer großen Datei schneller ist:
Ist die zusätzliche Arbeit pro Seite, die durch den
mmap
Ansatz impliziert wird, teurer als die Arbeit pro Byte beim Kopieren von Dateiinhalten vom Kernel in den Benutzerbereich, die durch die Verwendung impliziert wirdread()
?Auf vielen Systemen sind sie tatsächlich ungefähr ausgeglichen. Beachten Sie, dass jeder mit völlig unterschiedlichen Attributen der Hardware und des Betriebssystemstapels skaliert.
Insbesondere wird der
mmap
Ansatz relativ schneller, wenn:MAP_POPULATE
Implementierung, mit der große Karten effizient verarbeitet werden können, wenn beispielsweise die zugrunde liegenden Seiten im physischen Speicher zusammenhängend sind.... während der
read()
Ansatz relativ schneller wird, wenn:read()
Syscall hat eine gute Kopierleistung. ZB gutecopy_to_user
Leistung auf der Kernelseite.Die Hardware - Faktoren , die oben variieren wild über verschiedene Plattformen hinweg, sogar innerhalb der gleichen Familie (zB innerhalb x86 Generationen und vor allem Marktsegmente) und auf jeden Fall über Architekturen (zB ARM vs x86 vs PPC).
Auch die OS-Faktoren ändern sich ständig, wobei verschiedene Verbesserungen auf beiden Seiten bei dem einen oder anderen Ansatz zu einem starken Anstieg der Relativgeschwindigkeit führen. Eine aktuelle Liste enthält:
mmap
Fall ohne wirklich hilftMAP_POPULATE
.copy_to_user
Methodenarch/x86/lib/copy_user_64.S
, z. B.REP MOVQ
wenn es schnell ist, was demread()
Fall wirklich hilft .Update nach Spectre und Meltdown
Die Abschwächung der Schwachstellen Spectre und Meltdown erhöhte die Kosten eines Systemaufrufs erheblich. Auf den Systemen, die ich gemessen habe, gingen die Kosten für einen Systemaufruf "nichts tun" (der eine Schätzung des reinen Overheads des Systemaufrufs darstellt, abgesehen von der tatsächlichen Arbeit, die durch den Aufruf ausgeführt wurde) von ungefähr 100 ns auf einen typischen Wert modernes Linux-System bis ca. 700 ns. Abhängig von Ihrem System kann der speziell für Meltdown festgelegte Seitentabellen-Isolations- Fix neben den direkten Systemaufrufkosten zusätzliche Downstream-Effekte haben, da TLB-Einträge neu geladen werden müssen.
All dies ist ein relativer Nachteil für
read()
basierte Methoden im Vergleich zummap
basierten Methoden, daread()
Methoden für jede Datenmenge mit "Puffergröße" einen Systemaufruf ausführen müssen. Sie können die Puffergröße nicht willkürlich erhöhen, um diese Kosten zu amortisieren, da die Verwendung großer Puffer normalerweise schlechter abschneidet, da Sie die L1-Größe überschreiten und daher ständig unter Cache-Fehlern leiden.Auf der anderen Seite können Sie mit
mmap
eine große Speicherregion mitMAP_POPULATE
und nur effizientem Zugriff auf Kosten eines einzigen Systemaufrufs abbilden.1 Dies schließt mehr oder weniger auch den Fall ein, in dem die Datei zunächst nicht vollständig zwischengespeichert war, das Vorauslesen des Betriebssystems jedoch gut genug ist, um es so erscheinen zu lassen (dh die Seite wird normalerweise zu dem Zeitpunkt zwischengespeichert, zu dem Sie sich befinden will es). Dies ist jedoch ein subtiles Problem, da die Art und Weise, wie das Vorauslesen funktioniert, zwischen
mmap
undread
Anrufen häufig sehr unterschiedlich ist und durch "Beratung" -Anrufe weiter angepasst werden kann, wie in 2 beschrieben .2 ... denn wenn die Datei nicht zwischengespeichert wird, wird Ihr Verhalten vollständig von E / A-Bedenken dominiert, einschließlich der Sympathie Ihres Zugriffsmusters für die zugrunde liegende Hardware - und Sie sollten sich alle Mühe geben, um sicherzustellen, dass ein solcher Zugriff so sympathisch ist wie möglich, z. B. durch Verwendung von
madvise
oderfadvise
Aufrufe (und welche Änderungen auf Anwendungsebene Sie vornehmen können, um die Zugriffsmuster zu verbessern).3 Sie können dies umgehen, indem Sie beispielsweise
mmap
Fenster kleinerer Größe, z. B. 100 MB , nacheinander eingeben.4 Tatsächlich stellt sich heraus, dass der
MAP_POPULATE
Ansatz (mindestens eine Kombination aus Hardware und Betriebssystem) nur geringfügig schneller ist als die Nichtverwendung , wahrscheinlich weil der Kernel eine Fehlerbehebung verwendet. Daher wird die tatsächliche Anzahl kleinerer Fehler um den Faktor 16 reduziert oder so.quelle
mmap
hat dies einen unüberwindlichen Vorteil, da der feste Kernelaufruf-Overhead vermieden wird. Auf der anderen Seitemmap
erhöht sich auch der TLB-Druck und wird tatsächlich langsamer für die "Aufwärm" -Phase, in der Bytes im aktuellen Prozess zum ersten Mal gelesen werden (obwohl sie sich noch auf der Seitenseite befinden), da dies möglicherweise der Fall ist mehr Arbeit alsread
zum Beispiel, um benachbarte Seiten "zu umgehen" ... und für die gleichen Anwendungen ist "Aufwärmen" alles, was zählt! @ CaetanoSauerEs tut mir leid, dass Ben Collins seinen MMAP-Quellcode für Schiebefenster verloren hat. Das wäre schön in Boost zu haben.
Ja, das Zuordnen der Datei ist viel schneller. Sie verwenden im Wesentlichen das virtuelle Speichersubsystem des Betriebssystems, um Speicher der Festplatte zuzuordnen und umgekehrt. Stellen Sie sich das so vor: Wenn die Entwickler des Betriebssystemkerns es schneller machen könnten, würden sie es tun. Denn dadurch wird fast alles schneller: Datenbanken, Startzeiten, Ladezeiten von Programmen usw.
Der Schiebefenster-Ansatz ist wirklich nicht so schwierig, da mehrere zusammenhängende Seiten gleichzeitig zugeordnet werden können. Die Größe des Datensatzes spielt also keine Rolle, solange der größte eines einzelnen Datensatzes in den Speicher passt. Das Wichtigste ist die Verwaltung der Buchhaltung.
Wenn ein Datensatz nicht an einer getpagesize () - Grenze beginnt, muss Ihre Zuordnung auf der vorherigen Seite beginnen. Die Länge des zugeordneten Bereichs erstreckt sich vom ersten Byte des Datensatzes (bei Bedarf auf das nächste Vielfache von getpagesize () abgerundet) bis zum letzten Byte des Datensatzes (auf das nächste Vielfache von getpagesize () aufgerundet). Wenn Sie mit der Verarbeitung eines Datensatzes fertig sind, können Sie die Zuordnung aufheben () und mit dem nächsten fortfahren.
Dies alles funktioniert auch unter Windows mit CreateFileMapping () und MapViewOfFile () (und GetSystemInfo (), um SYSTEM_INFO.dwAllocationGranularity --- nicht SYSTEM_INFO.dwPageSize) zu erhalten.
quelle
mmap sollte schneller sein, aber ich weiß nicht wie viel. Es hängt sehr stark von Ihrem Code ab. Wenn Sie mmap verwenden, ist es am besten, die gesamte Datei auf einmal zuzuordnen, was Ihnen das Leben erheblich erleichtert. Ein mögliches Problem besteht darin, dass Sie eine 64-Bit-Architektur benötigen, wenn Ihre Datei größer als 4 GB ist (oder in der Praxis das Limit niedriger ist, häufig 2 GB). Wenn Sie also eine 32-Umgebung verwenden, möchten Sie sie wahrscheinlich nicht verwenden.
Allerdings gibt es möglicherweise einen besseren Weg, um die Leistung zu verbessern. Sie sagten, dass die Eingabedatei viele Male gescannt wird. Wenn Sie sie in einem Durchgang auslesen und dann fertig sind, könnte dies möglicherweise viel schneller sein.
quelle
Vielleicht sollten Sie die Dateien vorverarbeiten, damit sich jeder Datensatz in einer separaten Datei befindet (oder zumindest, dass jede Datei eine mmap-fähige Größe hat).
Können Sie auch alle Verarbeitungsschritte für jeden Datensatz ausführen, bevor Sie mit dem nächsten fortfahren? Vielleicht würde das einen Teil des E / A-Overheads vermeiden?
quelle
Ich bin damit einverstanden, dass mmap'd file I / O schneller sein wird, aber sollte das Zählerbeispiel nicht etwas optimiert werden, während Sie den Code vergleichen ?
Ben Collins schrieb:
Ich würde vorschlagen, auch zu versuchen:
Darüber hinaus können Sie auch versuchen, die Puffergröße auf die Größe einer Seite des virtuellen Speichers zu bringen, falls 0x1000 nicht die Größe einer Seite des virtuellen Speichers auf Ihrem Computer ist ... IMHO mmap'd file I / O noch gewinnt, aber das sollte die Dinge näher bringen.
quelle
Meiner Meinung nach entlastet die Verwendung von mmap () den Entwickler "nur" davon, seinen eigenen Caching-Code schreiben zu müssen. In einem einfachen Fall "Datei einmal genau durchlesen" wird dies nicht schwierig sein (obwohl, wie mlbrock hervorhebt, Sie die Speicherkopie immer noch im Prozessspeicher speichern), aber wenn Sie in der Datei oder hin und her gehen Ich glaube, die Kernel-Entwickler haben das Caching wahrscheinlich besser implementiert als ich ...
quelle
mmap
Caching besteht darin, dass Sie den vorhandenen Seitencache, der bereits vorhanden sein wird, einfach wiederverwenden , sodass Sie diesen Speicher kostenlos erhalten und er auch prozessübergreifend gemeinsam nutzen kann.Ich erinnere mich, wie ich vor Jahren eine riesige Datei mit einer Baumstruktur in den Speicher abgebildet habe. Ich war erstaunt über die Geschwindigkeit im Vergleich zur normalen De-Serialisierung, die viel Arbeit im Speicher erfordert, wie das Zuweisen von Baumknoten und das Setzen von Zeigern. Tatsächlich habe ich einen einzelnen Aufruf von mmap (oder seinem Gegenstück unter Windows) mit vielen (VIELEN) Aufrufen von Operator-Neu- und Konstruktoraufrufen verglichen. Für solche Aufgaben ist mmap im Vergleich zur De-Serialisierung unschlagbar. Natürlich sollte man sich dafür die Boosts des verschiebbaren Zeigers ansehen.
quelle
Dies klingt nach einem guten Anwendungsfall für Multithreading ... Ich würde denken, Sie könnten ziemlich einfach einen Thread so einrichten, dass er Daten liest, während die anderen ihn verarbeiten. Dies kann eine Möglichkeit sein, die wahrgenommene Leistung dramatisch zu steigern. Nur ein Gedanke.
quelle
Ich denke, das Beste an mmap ist das Potenzial für asynchrones Lesen mit:
Das Problem ist, dass ich nicht die richtigen MAP_FLAGS finden kann, um einen Hinweis zu geben, dass dieser Speicher so schnell wie möglich aus der Datei synchronisiert werden sollte. Ich hoffe, dass MAP_POPULATE den richtigen Hinweis für mmap gibt (dh es wird nicht versucht, alle Inhalte vor der Rückkehr vom Aufruf zu laden, sondern dies wird asynchron mit feed_data durchgeführt). Zumindest liefert es mit diesem Flag bessere Ergebnisse, selbst wenn das Handbuch angibt, dass es seit 2.6.23 nichts ohne MAP_PRIVATE macht.
quelle
posix_madvise
mit derWILLNEED
Flagge faule Hinweise vorab ausfüllen.posix_madvise
sagen, dass es sich um einen asynchronen Aufruf handelt. Es wäre auch schön,mlock
auf diejenigen zu verweisen , die warten möchten, bis der gesamte Speicherbereich ohne Seitenfehler verfügbar ist.