Kleine Schreibvorgänge auf die SMB-Netzwerkfreigabe sind unter Windows langsam und über CIFS Linux schnell

10

Ich hatte Probleme, ein Leistungsproblem mit einer SMB / CIFS-Freigabe zu beheben, wenn kleine Schreibvorgänge ausgeführt wurden.

Lassen Sie mich zunächst mein aktuelles Netzwerk-Setup beschreiben:

Server

  • Synology DS215j (mit aktivierter SMB3-Unterstützung)

Clients (derselbe Computer mit doppeltem Boot und kabelgebundenem Gig-E)

  • Ubuntu 14.04.5 LTS, Trusty Tahr
  • Windows 8.1

smb.conf

[global]
    printcap name=cups
    winbind enum groups=yes
    include=/var/tmp/nginx/smb.netbios.aliases.conf
    socket options=TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=65536 SO_SNDBUF=65536
    security=user
    local master=no
    realm=*
    passdb backend=smbpasswd
    printing=cups
    max protocol=SMB3
    winbind enum users=yes
    load printers=yes
    workgroup=WORKGROUP

Ich teste derzeit die kleine Schreibleistung mit dem folgende Programm in C ++ geschrieben (auf GitHub hier ):

#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main(int argc, char* argv[])
{
    ofstream outFile(argv[1]);
    for(int i = 0; i < 1000000; i++)
    {
        outFile << "Line #" << i << endl;   
    }

    outFile.flush();
    outFile.close();
    return 0;
}

Linux-Mount-Konfiguration:

//192.168.1.10/nas-main on /mnt/nas-main type cifs (rw,noexec,nodev)

Programmlaufzeit unter Linux (Spitzenausgabe des Netzwerks bei ~ 100 Mbit / s):

$ time ./nas-write-test /mnt/nas-main/home/will/test.txt

real    0m0.965s
user    0m0.148s
sys 0m0.672s

PCAP-Snapshot, der das Aufteilen vieler Zeilen in ein einzelnes TCP-Paket zeigt:

Linux PCAP-Snapshot

Programmlaufzeit unter Windows, gemessen mit PowerShell:

> Measure-Command {start-process .\nas-write-test.exe -argumentlist "Z:\home\will\test-win.txt" -wait}


Days              : 0
Hours             : 0
Minutes           : 9
Seconds           : 29
Milliseconds      : 316
Ticks             : 5693166949
TotalDays         : 0.00658931359837963
TotalHours        : 0.158143526361111
TotalMinutes      : 9.48861158166667
TotalSeconds      : 569.3166949
TotalMilliseconds : 569316.6949

PCAP-Snapshot unter Windows mit einer einzelnen Zeile pro SMB-Schreibanforderung:

Windows PCAP-Snapshot

Das gleiche Programm dauert unter Windows etwa 10 Minuten (~ 2,3 Mbit / s). Offensichtlich zeigt der Windows-PCAP eine sehr laute SMB-Konversation mit sehr geringer Nutzlasteffizienz.

Gibt es unter Windows Einstellungen, die die Leistung bei kleinen Schreibvorgängen verbessern können? Aus der Betrachtung von Paketerfassungen geht hervor, dass Windows die Schreibvorgänge nicht ordnungsgemäß puffert und die Daten sofort zeilenweise sendet. Unter Linux sind die Daten stark gepuffert und weisen daher eine weitaus bessere Leistung auf. Lassen Sie mich wissen, ob die PCAP-Dateien hilfreich sind, und ich kann einen Weg finden, sie hochzuladen.

Update 27.10.16:

Wie von @sehafoc erwähnt, habe ich die Samba- max protocolServereinstellung wie folgt auf SMB1 reduziert :

max protocol=NT1

Die obige Einstellung führte zu genau demselben Verhalten.

Ich habe auch die Variable von Samba entfernt, indem ich eine Freigabe auf einem anderen Windows 10-Computer erstellt habe, und sie zeigt auch das gleiche Verhalten wie der Samba-Server. Daher glaube ich, dass dies ein Schreib-Caching-Fehler bei Windows-Clients im Allgemeinen ist.

Update: 10/06/17:

Vollständige Linux-Paketerfassung (14 MB)

Vollständige Windows-Paketerfassung (375 MB)

Update: 12.10.17:

Ich habe auch eine NFS-Freigabe eingerichtet und Windows schreibt auch ohne Pufferung. Soweit ich das beurteilen kann, handelt es sich definitiv um ein zugrunde liegendes Windows-Client-Problem, das definitiv bedauerlich ist: - /

Jede Hilfe wäre dankbar!

Mevatron
quelle

Antworten:

2

Das C ++ - Endl ist so definiert, dass es '\ n' gefolgt von einem Flush ausgibt. flush () ist eine teure Operation, daher sollten Sie generell vermeiden, endl als Standardzeilenende zu verwenden, da dies genau das Leistungsproblem verursachen kann, das Sie sehen (und zwar nicht nur bei SMB, sondern bei jedem Stream mit einem teuren Flush, einschließlich lokalem Spinnen Rost oder sogar das neueste NVMe mit einer lächerlich hohen Ausgaberate).

Durch Ersetzen von endl durch "\ n" wird die oben genannte Leistung behoben, indem das System wie vorgesehen puffern kann. Außer einige Bibliotheken können auf "\ n" geleert werden. In diesem Fall haben Sie mehr Kopfschmerzen (siehe /programming/21129162/tell-endl-not-to-flush für eine Lösung, die die sync () -Methode überschreibt ).

Um die Sache zu verkomplizieren, wird flush () nur für das definiert, was in den Bibliothekspuffern 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 Flush für Visual Studio C ++ besonders teuer, da es das Schreiben bis zum Ende umrundet die physischen Medien am anderen Ende Ihres Remote-Servers, wie Sie sehen. GCC sagt andererseits: "Eine letzte Erinnerung: Es sind normalerweise mehr Puffer beteiligt als nur die auf Sprach- / Bibliotheksebene. Kernelpuffer, Plattenpuffer und dergleichen wirken sich ebenfalls aus. Das Überprüfen und Ändern dieser Puffer ist systemabhängig . "https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html ) Ihre Ubuntu-Traces scheinen darauf hinzudeuten, dass die Betriebssystem- / Netzwerkpuffer nicht durch die Bibliothek flush () geleert werden. Ein systemabhängiges 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 unter Ubuntu auszuführen.

Im Allgemeinen müssen Sie über Ihre Anforderungen nachdenken, um festzustellen, ob das Spülen jeder Leitung angemessen ist oder nicht. endl eignet sich im Allgemeinen für interaktive Streams wie die Anzeige (der Benutzer muss unsere Ausgabe tatsächlich sehen und nicht in Bursts), ist jedoch 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 sowie 4 und 8 Byte-Schreibvorgänge gelöscht wurden. Es ist nicht schön zu sehen, dass das Betriebssystem Millionen von E / A-Vorgängen zum Schreiben einer 1-MB-Datei schleift.

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 nur eine ausführliche Informationsprotokollierung erstellt wird, die voraussichtlich automatisch gelöscht wird, bevor die Anwendung beendet wird. Es muss nicht entweder / oder sein, da Sie eine Klasse mit einem ausgefeilteren Flush-Algorithmus ableiten könnten, um bestimmten Anforderungen zu entsprechen.

Vergleichen Sie Ihren Fall mit dem kontrastierenden Fall von Personen, die sicherstellen müssen, dass ihre Daten vollständig auf der Festplatte gespeichert und in einem Betriebssystempuffer nicht anfällig sind ( /programming/7522479/how-do-i-ensure-data) -ist auf die Festplatte geschrieben, bevor der Stream geschlossen wird ).

Beachten Sie, dass outFile.flush () wie geschrieben überflüssig ist, da es einen bereits gelöschten Stream löscht. Um pedantisch zu sein, sollten Sie endl alleine oder vorzugsweise "\ n" mit outFile.flush () verwendet haben, aber nicht beide.

Doug
quelle
Tausend Dank! Sie verdienen weit mehr als 100 Punkte, aber das ist alles, was ich geben kann :) Dies war definitiv das Problem!
Mevatron
2

Ich habe nicht genug Ruf, um einen Kommentar zu hinterlassen (was meiner Meinung nach angesichts des Grads der Überprüfung dieser Antwort besser wäre).

Ich stelle fest, 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. Möglicherweise ist der Batch-Oplock-Mechanismus in SMB1-Samba besser als in der exklusiven SMB2-Lease-Implementierung. In beiden Fällen sollte dies ein gewisses Maß an clientseitigem Caching ermöglichen.

1) Versuchen Sie möglicherweise, in Samba eine niedrigere maximale Protokollstufe festzulegen, um Windows mit SMB1 auszuprobieren. 2) Überprüfen Sie, ob exklusive Oplocks oder Leases entfernt wurden

Hoffe das hilft :)

sehafoc
quelle
2

Die Leistung von Remote-Dateivorgängen wie Lesen / Schreiben mithilfe des SMB-Protokolls kann durch die Größe der von Servern und Clients zugewiesenen Puffer beeinflusst werden. Die Puffergröße bestimmt die Anzahl der Roundtrips, die zum Senden einer festen Datenmenge erforderlich sind. Jedes Mal, wenn Anforderungen und Antworten zwischen Client und Server gesendet werden, entspricht die benötigte Zeit mindestens der Latenz zwischen beiden Seiten, was im Fall von Wide Area Network (WAN) sehr bedeutsam 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äß Ihrer Anforderung über 5000)

ABER SMB SIGNING bewirkt die maximal zulässige Puffergröße. Daher müssen wir auch die SMB-Signatur deaktivieren, um unser Ziel zu erreichen. Die folgende Registrierung muss sowohl auf der Serverseite als auch nach Möglichkeit auch auf der Clientseite erstellt werden.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters

Wert Name: EnableSecuritySignature

Datentyp: REG_DWORD

Daten: 0 (deaktivieren), 1 (aktivieren)

Adi Jha
quelle
Danke für den Tipp; Ich habe jedoch beide Mittel ausprobiert und sehe immer noch das obige Verhalten: - /
Mevatron
Sie können auch gerne überprüfen, warum "Synology DS215j" SMB3 nicht verwendet. Standardmäßig ist SMB3 unter Win 8.1 aktiviert.
Adi Jha
1

Interessantes Phänomen. Folgendes würde ich versuchen - ich habe keine Ahnung, ob dies wirklich hilft. Wenn es meine Maschine wäre, würde ich die SMB-Perfcounter ausgiebig beobachten. Einer von ihnen wird die Ursache zeigen.

Weitere Dinge zu versuchen

Fügen Sie weitere Worker-Threads hinzu

Falls der SMB_RDR eine Schreib-E / A-Anforderung pro Zeile übersteigt (was hier nicht passieren sollte), kann es hilfreich sein, der Ausführungsengine einige Threads hinzuzufügen.

Setzen Sie "AdditionalCriticalWorkerThreads" auf 2 und dann auf 4.

HKLM\System\CurrentControlSet\Control\Session Manager\Executive\AdditionalCriticalWorkerThreads

Der Standardwert ist 0, was bedeutet, dass keine zusätzlichen kritischen Kernel-Worker-Threads hinzugefügt werden. Welches ist normalerweise ok. Dieser Wert wirkt sich auf die Anzahl der Threads aus, die der Dateisystem-Cache für Vorauslese- und Rückschreibanforderungen verwendet. Das Erhöhen dieses Werts kann zu mehr E / A in der Warteschlange im Speichersubsystem führen (was gut ist, wenn Sie zeilenweise schreiben möchten), ist jedoch CPU-teurer.

Weitere Warteschlangenlänge hinzufügen

Durch Erhöhen des Werts "AdditionalCriticalWorkerThreads" wird die Anzahl der Threads erhöht, die der Dateiserver zum Bearbeiten gleichzeitiger Anforderungen verwenden kann.

HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters\MaxThreadsPerQueue

Der Standardwert ist 20. Ein Hinweis darauf, dass der Wert möglicherweise erhöht werden muss, ist, wenn die SMB2-Arbeitswarteschlangen sehr groß werden (perfcounter 'Server-Arbeitswarteschlangen \ Warteschlangenlänge \ SMB2 *' sollte <100 sein).

bjoster
quelle