Das C++-Endl ist so definiert, dass es '\n' gefolgt von einem Flush ausgibt. flush() ist eine teure Operation, daher sollten Sie es im Allgemeinen vermeiden, endl als standardmäßiges Zeilenende zu verwenden, da dies genau das Leistungsproblem verursachen kann, das Sie sehen (und nicht nur mit SMB, sondern mit jedem Ofstream mit einem teuren Flush, einschließlich lokalem Spinning). rust oder sogar das neueste NVMe mit einer lächerlich hohen Ausgaberate).
Das Ersetzen von endl durch "\n" korrigiert die obige Leistung, indem es dem System ermöglicht, wie beabsichtigt zu puffern. Außer, dass einige Bibliotheken möglicherweise auf "\n" geleert werden, in diesem Fall haben Sie mehr Kopfschmerzen (siehe https://stackoverflow.com/questions/21129162/tell-endl-not-to-flush für eine Lösung, die die sync()-Methode überschreibt ).
Um die Sache noch komplizierter zu machen, ist flush() nur für das definiert, was innerhalb der Bibliothekspuffer passiert. Die Auswirkung von Flush auf Betriebssystem, Festplatte und andere externe Puffer ist nicht definiert. Für Microsoft.NET „Wenn Sie die FileStream.Flush-Methode aufrufen, wird auch der E/A-Puffer des Betriebssystems geleert.“ (https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx) Dies macht das Flushen für Visual Studio C++ besonders teuer, da es den gesamten Schreibvorgang bis zu Roundtrip durchführt das physische Medium am anderen Ende Ihres Remote-Servers, wie Sie sehen. GCC hingegen sagt:"Eine letzte Erinnerung:Normalerweise sind mehr Puffer beteiligt als nur die auf Sprach- / Bibliotheksebene. Kernel-Puffer, Festplattenpuffer und dergleichen wirken sich ebenfalls aus. Das Überprüfen und Ändern dieser ist systemabhängig ." (https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html) Ihre Ubuntu-Traces scheinen darauf hinzudeuten, dass die Betriebssystem-/Netzwerkpuffer nicht von der Bibliothek flush() geleert werden. Systembedingtes Verhalten wäre umso mehr ein Grund, Endl und übermäßiges Spülen zu vermeiden. Wenn Sie VC++ verwenden, können Sie versuchen, zu einem Windows GCC-Derivat zu wechseln, um zu sehen, wie das systemabhängige Verhalten reagiert, oder alternativ Wine verwenden, um die ausführbare Windows-Datei auf Ubuntu auszuführen.
Generell müssen Sie über Ihre Anforderungen nachdenken, um festzustellen, ob das Spülen jeder Leitung angemessen ist oder nicht. endl ist im Allgemeinen für interaktive Streams wie die Anzeige geeignet (der Benutzer muss unsere Ausgabe tatsächlich sehen, und nicht in Bursts), aber im Allgemeinen nicht für andere Arten von Streams geeignet, einschließlich Dateien, bei denen der Spülaufwand erheblich sein kann. Ich habe gesehen, wie Apps alle 1 und 2 und 4 und 8 Byte geschrieben wurden. Es ist nicht schön zu sehen, wie das Betriebssystem Millionen von IOs schleift, um eine 1-MB-Datei zu schreiben.
Beispielsweise muss eine Protokolldatei möglicherweise jede Zeile leeren, wenn Sie einen Absturz debuggen, da Sie den Ofstream leeren müssen, bevor der Absturz auftritt. während eine andere Protokolldatei möglicherweise nicht jede Zeile löschen muss, wenn sie nur eine ausführliche Informationsprotokollierung erzeugt, von der erwartet wird, dass sie automatisch gelöscht wird, bevor die Anwendung beendet wird. Es muss nicht entweder/oder sein, da Sie eine Klasse mit einem ausgeklügelteren Flush-Algorithmus ableiten könnten, um spezifische Anforderungen zu erfüllen.
Vergleichen Sie Ihren Fall mit dem anderen Fall von Personen, die sicherstellen müssen, dass ihre Daten vollständig auf der Festplatte gespeichert und nicht in einem Betriebssystempuffer anfällig sind (https://stackoverflow.com/questions/7522479/how-do-i-ensure-data -wird-vor-dem-Schließen-fstream-auf-die-Festplatte-geschrieben).
Beachten Sie, dass outFile.flush() wie geschrieben überflüssig ist, da es einen bereits geleerten Ofstream leert. Um pedantisch zu sein, hätten Sie endl allein oder vorzugsweise "\n" mit outFile.flush() verwenden sollen, aber nicht beides.
Die Leistung von Remote-Dateivorgängen, wie z. B. Lesen/Schreiben, mit dem SMB-Protokoll kann durch die Größe der von Servern und Clients zugewiesenen Puffer beeinträchtigt werden. Die Puffergröße bestimmt die Anzahl der Roundtrips, die zum Senden einer festen Datenmenge erforderlich sind. Jedes Mal, wenn Anfragen und Antworten zwischen Client und Server gesendet werden, entspricht die benötigte Zeit mindestens der Latenzzeit zwischen beiden Seiten, was im Fall von Wide Area Network (WAN) sehr erheblich sein kann.
SMB-Puffer – Die MaxBufferSize kann über die folgende Registrierungseinstellung konfiguriert werden:
HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\SizeReqBuf
Datentyp:REG_DWORD
Bereich:1024 bis 65535 (Wählen Sie einen Wert gemäß Ihren Anforderungen über 5000)
ABER SMB SIGNING wirkt sich auf die maximal zulässige Puffergröße aus. Daher müssen wir auch die SMB-Signierung deaktivieren, um unser Ziel zu erreichen. Die folgende Registrierung muss sowohl auf der Serverseite als auch auf der Clientseite erstellt werden.
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters
Wertname:EnableSecuritySignature
Datentyp:REG_DWORD
Daten:0 (deaktiviert), 1 (aktiviert)
Ich habe nicht genug Reputation, um einen Kommentar zu hinterlassen (was meiner Meinung nach angesichts des Überprüfungsgrades dieser Antwort besser wäre).
Mir ist aufgefallen, dass eine große Abweichung in Ihrem Trace auf Linux- und Windows-Ebene darin besteht, dass Sie SMB1 unter Linux und SMB2 unter Windows verwenden. Vielleicht funktioniert der Batch-Oplock-Mechanismus in SMB1-Samba besser als die exklusive Lease-Implementierung von SMB2. In beiden Fällen sollten diese ein gewisses Maß an clientseitigem Caching ermöglichen.
1) Versuchen Sie vielleicht, eine niedrigere maximale Protokollebene in Samba einzustellen, um Windows mit SMB auszuprobieren
Hoffe das hilft :)