Mein Problem ist folgendes: Ich habe eine C / C ++ App, die unter Linux läuft, und diese App empfängt einen Datenstrom mit konstanter Rate und hoher Bandbreite (~ 27MB / Sek.), den sie in eine Datei streamen muss (oder Dateien). Der Computer, auf dem es läuft, ist ein Quad-Core 2GHz Xeon mit Linux. Das Dateisystem ist ext4, und der Datenträger ist ein Solid-State-E-SATA-Laufwerk, das für diesen Zweck viel schneller sein sollte.
Das Problem ist das zu clevere Pufferverhalten von Linux. Konkret, anstatt die Daten sofort auf die Festplatte zu schreiben, oder kurz nachdem ich write () aufgerufen habe, speichert Linux die "geschriebenen" Daten im RAM und zu einem späteren Zeitpunkt (ich vermute, wenn die 2GB RAM voll werden) es wird plötzlich versuchen, mehrere hundert Megabyte zwischengespeicherte Daten auf einmal auf die Festplatte zu schreiben. Das Problem ist, dass diese Cache-Flush-Funktion groß ist und den Datenerfassungscode für eine längere Zeit hält, wodurch einige der aktuell eingehenden Daten verloren gehen.
Meine Frage ist: Gibt es irgendeinen vernünftigen Weg, das Caching-Verhalten von Linux zu "tunen", so dass es entweder die ausgehenden Daten nicht zwischenspeichert oder wenn es zwischengespeichert werden muss, es speichert nur eine kleinere Menge auf einmal, Dadurch wird die Bandbreitennutzung des Laufwerks geglättet und die Leistung des Codes verbessert?
Ich kenne O_DIRECT und werde das verwenden, was ich tun muss, aber es gibt einige Verhaltensbeschränkungen für das Programm (z. B. müssen Puffer ausgerichtet sein und ein Vielfaches der Plattensektorgröße usw.), die ich lieber hätte vermeide es, wenn ich kann.
Sie können die posix_fadvise()
mit dem POSIX_FADV_DONTNEED
advice verwenden (möglicherweise kombiniert mit Aufrufen von fdatasync()
), damit das System die Daten löscht und aus dem Cache entfernt.
Siehe diesen Artikel für ein praktisches Beispiel.
Wenn Sie Latenzanforderungen haben, die der Betriebssystemcache nicht selbst erfüllen kann (der Standard-E / A-Scheduler ist normalerweise für Bandbreite und nicht für Latenz optimiert), müssen Sie wahrscheinlich Ihre eigene Speicherpufferung verwalten. Schreiben Sie die eingehenden Daten sofort aus? Wenn ja, würde ich vorschlagen, diese Architektur fallen zu lassen und mit etwas wie einem Ringpuffer zu gehen, wobei ein Thread (oder multiplexer E / A-Handler) von einer Seite des Puffers schreibt, während die Lesevorgänge auf die andere Seite kopiert werden.
Bei einer bestimmten Größe ist dies groß genug, um die Latenz zu bewältigen, die von einem pessimalen OS-Cache-Flush benötigt wird. Oder nicht, in diesem Fall sind Sie tatsächlich bandbreitenbegrenzt und keine Menge an Software-Tuning wird Ihnen helfen, bis Sie schneller Speicherplatz bekommen.
Wenn wir von std :: fstream (oder einem anderen C ++ - Stream-Objekt) sprechen
Sie können Ihren eigenen Puffer angeben mit:
streambuf * ios :: rdbuf (strombuf * stream- puffer);
Indem Sie einen eigenen Puffer definieren, können Sie das Verhalten des Streams anpassen.
Alternativ können Sie den Puffer immer manuell in voreingestellten Intervallen leeren.
Hinweis: Es gibt einen Resonator für einen Puffer. Es ist schneller als direkt auf eine Festplatte schreiben (alle 10 Bytes). Es gibt sehr wenig Grund, auf einen Datenträger in Blöcken zu schreiben, die kleiner als die Plattenblockgröße sind. Wenn Sie zu schnell schreiben, wird der Disk Controler zu Ihrem Flaschenhals.
Aber ich habe ein Problem damit, dass Sie denselben Thread im Schreibprozess verwenden, der die Lesevorgänge blockieren muss.
Während die Daten geschrieben werden, gibt es keinen Grund, warum ein anderer Thread die Daten aus Ihrem Stream nicht weiterlesen kann (Sie müssen vielleicht einige schöne Fußarbeit machen, um sicherzustellen, dass sie in verschiedenen Bereichen des Puffers lesen / schreiben). Aber ich sehe kein echtes potentielles Problem damit, da das IO-System abläuft und seine Arbeit asynchron ausführt (was möglicherweise den Schreib-Thread hindert (abhängig von der Verwendung des IO-Systems), aber nicht unbedingt Ihre Anwendung).
Sie können die Einstellungen für den Seitencache in / proc / sys / vm anpassen (siehe / proc / sys / vm / dirty_ratio, / proc / sys / vm / swappiness), um den Seitencache nach Ihren Wünschen einzustellen.
Ich weiß, dass diese Frage alt ist, aber wir wissen jetzt ein paar Dinge, die wir nicht wussten, als diese Frage zuerst gestellt wurde.
Ein Teil des Problems besteht darin, dass die Standardwerte für / proc / sys / vm / dirty_ratio und / proc / sys / vm / dirty_background_ratio nicht für neuere Maschinen mit viel Speicher geeignet sind. Linux startet den Flush, wenn dirty_background_ratio erreicht ist, und blockiert alle I / O, wenn dirty_ratio erreicht ist. Lower dirty_background_ratio, um schneller mit dem Löschen zu beginnen, und raise dirty_ratio, um die E / A später zu blockieren. Auf sehr großen Speichersystemen (32 GB oder mehr) können Sie sogar dirty_bytes und dirty_background_bytes verwenden, da das minimale Inkrement von 1% für die _ratio-Einstellungen zu grob ist. Lesen Sie Ссылка für eine detailliertere Erklärung.
>Wenn Sie außerdem wissen, dass Sie die Daten nicht erneut lesen müssen, rufen Sie posix_fadvise mit FADV_DONTNEED auf, um sicherzustellen, dass Cacheseiten früher wiederverwendet werden können. Dies muss getan werden, nachdem Linux die Seite auf die Festplatte geleert hat, andernfalls wird die Seite durch die Flush-Operation wieder in die aktive Liste verschoben (was effektiv den Effekt von fadvise negiert).
Um sicherzustellen, dass Sie immer noch eingehende Daten in den Fällen lesen können, in denen Linux den Aufruf von write () blockiert, schreiben Sie Dateien in einem anderen Thread als dem, in dem Sie gerade lesen.
Sie könnten einen Multithread-Ansatz verwenden - ein Thread liest einfach Datenpakete und fügt sie einem Fifo hinzu, und der andere Thread entfernt Pakete vom Fifo und schreibt sie auf die Festplatte. Auf diese Weise kann das Programm, selbst wenn das Schreiben auf Festplatte anhält, weiter eingehende Daten lesen und im RAM zwischenspeichern.