Małe zapisy w udziale sieciowym SMB są powolne w systemie Windows, szybko w stosunku do CIFS Linux mount

10

Podczas wykonywania małych zapisów próbowałem rozwiązać problem z wydajnością udziału SMB / CIFS.

Najpierw opiszę moją obecną konfigurację sieci:

serwer

  • Synology DS215j (z włączoną obsługą SMB3)

Klienci (ten sam komputer, przewodowy Gig-E z podwójnym uruchomieniem)

  • 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

Obecnie testuję małą wydajność zapisu za pomocą następującego programu napisanego w C ++ ( tutaj na GitHub ):

#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;
}

Konfiguracja montowania w systemie Linux:

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

Czas działania programu w systemie Linux (szczytowa wydajność sieci przy ~ 100 Mb / s):

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

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

Migawka PCAP pokazująca fragmentowanie wielu linii w jednym pakiecie TCP:

Migawka Linux PCAP

Czas działania programu w systemie Windows mierzony za pomocą programu 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

Migawka PCAP w systemie Windows z pojedynczą linią dla każdego żądania zapisu SMB:

Migawka Windows PCAP

Ten sam program zajmuje około 10 minut (~ 2,3 Mb / s) w systemie Windows. Oczywiście Windows PCAP pokazuje bardzo głośną rozmowę SMB o bardzo niskiej wydajności.

Czy są jakieś ustawienia w systemie Windows, które mogą poprawić wydajność małego zapisu? Wygląda na to, że patrząc na przechwytywanie pakietów, Windows nie buforuje poprawnie zapisów i natychmiast wysyła dane po jednej linii na raz. Natomiast w systemie Linux dane są mocno buforowane, a zatem mają znacznie lepszą wydajność. Daj mi znać, czy pliki PCAP będą pomocne, a ja mogę znaleźć sposób na ich przesłanie.

Aktualizacja 27.10.16:

Jak wspomniano w @sehafoc, zmniejszyłem max protocolustawienie serwerów Samba do SMB1, wykonując następujące czynności:

max protocol=NT1

Powyższe ustawienie spowodowało dokładnie takie samo zachowanie.

Usunąłem również zmienną Samby, tworząc udział na innym komputerze z systemem Windows 10, a także wykazuje to samo zachowanie, co serwer Samba, więc zaczynam wierzyć, że jest to błąd buforowania zapisu ogólnie dla klientów Windows.

Aktualizacja: 10/06/17:

Przechwytywanie pełnego pakietu Linux (14 MB)

Przechwytywanie pełnego pakietu Windows (375 MB)

Aktualizacja: 10/12/17:

Skonfigurowałem również udział NFS, a system Windows również pisze bez buforowania. Jest to zdecydowanie podstawowy problem klienta Windows, o ile wiem, co jest zdecydowanie niefortunne: - /

Każda pomoc będzie mile widziana!

mevatron
źródło

Odpowiedzi:

2

End ++ w C ++ jest zdefiniowany tak, aby wyświetlał „\ n”, a następnie kolor. flush () jest kosztowną operacją, więc ogólnie powinieneś unikać używania endl jako domyślnego końca linii, ponieważ może to stworzyć dokładnie problem z wydajnością, który widzisz (i to nie tylko z SMB, ale z dowolnym strumieniem z drogim flush, w tym lokalnym spinowaniem rdza, a nawet najnowsze NVMe przy niesamowicie wysokim poziomie wydajności).

Zastąpienie endl przez „\ n” poprawi powyższą wydajność, umożliwiając buforowanie systemu zgodnie z przeznaczeniem. Z wyjątkiem niektórych bibliotek, które mogą opróżniać się w „\ n”, w takim przypadku masz więcej problemów (patrz /programming/21129162/tell-endl-not-to-flush, aby znaleźć rozwiązanie zastępujące metodę sync () ).

Teraz, aby skomplikować sprawę, flush () jest zdefiniowany tylko dla tego, co dzieje się w buforach biblioteki. Wpływ opróżniania na system operacyjny, dysk i inne zewnętrzne bufory nie jest zdefiniowany. W przypadku Microsoft.NET „Po wywołaniu metody FileStream.Flush bufor bufora we / wy systemu operacyjnego jest również opróżniany”. ( https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx ) To sprawia, że ​​flush jest szczególnie drogi dla Visual Studio C ++, ponieważ spowoduje to całkowite przejście do zapisu fizyczny nośnik na odległym końcu zdalnego serwera, tak jak widzisz. Z drugiej strony GCC mówi: „Ostatnie przypomnienie: zwykle jest zaangażowanych więcej buforów niż tylko na poziomie języka / biblioteki. Bufory jądra, bufory dyskowe itp. Również będą miały wpływ. Sprawdzanie i zmiana są zależne od systemu . ”https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html ) Twoje ślady Ubuntu wydają się wskazywać, że system operacyjny / bufory sieciowe nie są opróżniane przez flush biblioteki (). Zachowanie zależne od systemu byłoby tym bardziej powodem, aby unikać endl i nadmiernego spłukiwania. Jeśli używasz VC ++, możesz spróbować przejść na pochodną GCC systemu Windows, aby zobaczyć, jak reagują zachowania zależne od systemu, lub alternatywnie użyć Wine do uruchomienia pliku wykonywalnego Windows na Ubuntu.

Mówiąc bardziej ogólnie, musisz przemyśleć swoje wymagania, aby ustalić, czy opróżnianie każdej linii jest właściwe, czy nie. endl jest ogólnie odpowiedni dla interaktywnych strumieni, takich jak wyświetlanie (potrzebujemy, aby użytkownik faktycznie zobaczył nasze wyjście, a nie w seriach), ale ogólnie nie nadaje się do innych rodzajów strumieni, w tym plików, w których narzut może być znaczny. Widziałem aplikacje opróżniające się co 1 i 2 oraz 4 i 8 bajtów zapisu ... to nie jest piękne, że system operacyjny zbiera miliony IO do napisania pliku 1 MB.

Na przykład plik dziennika może wymagać opróżnienia każdej linii, jeśli debugujesz awarię, ponieważ musisz opróżnić strumień danych, zanim nastąpi awaria; podczas gdy inny plik dziennika może nie wymagać opróżniania każdej linii, jeśli tylko generuje pełne rejestrowanie informacji, które powinno zostać automatycznie opróżnione przed zakończeniem działania aplikacji. Nie musi to być / lub jak można wyprowadzić klasę z bardziej wyrafinowanym algorytmem spłukiwania, aby spełnić określone wymagania.

Porównaj swój przypadek z kontrastowym przypadkiem osób, które muszą upewnić się, że ich dane są całkowicie utrwalone na dysku i nie są podatne na atak w buforze systemu operacyjnego ( /programming/7522479/how-do-i-ensure-data -jest zapisany na dysk przed zamknięciem strumienia ).

Zauważ, że jak napisano, outFile.flush () jest zbędny, ponieważ opróżnia już opróżniony strumień. Aby być pedantycznym, powinieneś był używać endl sam lub najlepiej „\ n” z outFile.flush (), ale nie jednocześnie.

Doug
źródło
Stukrotne dzięki! Zasługujesz na ponad 100 punktów, ale to wszystko, co mogę dać :) To zdecydowanie był problem!
mevatron
2

Nie mam wystarczającej reputacji, aby zostawić komentarz (co moim zdaniem byłoby lepiej, biorąc pod uwagę poziom weryfikacji tej odpowiedzi).

Zauważam, że jedną dużą różnicą w śledzeniu na poziomie Linux vs. Windows jest to, że używasz SMB1 w systemie Linux i SMB2 w systemie Windows. Być może mechanizm blokowania partii działa lepiej w sambie SMB1 niż w ekskluzywnej implementacji dzierżawy SMB2. W obu przypadkach powinny one zezwalać na buforowanie po stronie klienta.

1) Być może spróbuj ustawić niższy maksymalny poziom protokołu w Sambie, aby wypróbować system Windows za pomocą SMB1 2) Sprawdź, czy wyłączane są wyłączne blokady lub dzierżawy

Mam nadzieję że to pomoże :)

sehafoc
źródło
2

Na wydajność zdalnych operacji na plikach, takich jak odczyt / zapis przy użyciu protokołu SMB, może mieć wpływ rozmiar buforów przydzielanych przez serwery i klientów. Rozmiar bufora określa liczbę podróży w obie strony potrzebnych do wysłania określonej ilości danych. Za każdym razem, gdy żądania i odpowiedzi są wysyłane między klientem a serwerem, ilość czasu jest równa co najmniej opóźnieniu między obiema stronami, co może być bardzo znaczące w przypadku sieci rozległej (WAN).

Bufor SMB - MaxBufferSize można skonfigurować za pomocą następującego ustawienia rejestru:

HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\SizeReqBuf

Typ danych: REG_DWORD

Zakres: od 1024 do 65535 (wybierz wartość zgodnie z wymaganiami powyżej 5000)

ALE SMB SIGNING wpływa na maksymalny dozwolony rozmiar bufora. Dlatego też musimy wyłączyć podpisywanie SMB, aby osiągnąć nasz cel. Należy utworzyć następujący rejestr zarówno po stronie serwera, jak i, jeśli to możliwe, również po stronie klienta.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters

Nazwa wartości: EnableSecuritySignature

Typ danych: REG_DWORD

Dane: 0 (wyłącz), 1 (włącz)

Adi Jha
źródło
Dzięki za wskazówkę; jednak wypróbowałem oba te środki i nadal widzę powyższe zachowanie: - /
mevatron
Chcesz również sprawdzić, dlaczego „Synology DS215j” nie używa SMB3. Domyślnie SMB3 jest włączony w Win 8.1.
Adi Jha,
1

Ciekawe zjawisko. Oto, co bym spróbował - nie mam pojęcia, czy to naprawdę pomaga. Gdyby to była moja maszyna, intensywnie obserwowałbym perfcounterów SMB. Jednym z nich będzie wykazać przyczynę.

Więcej rzeczy do wypróbowania

Dodaj więcej wątków roboczych

W przypadku, gdy SMB_RDR wykryje jedno żądanie We / Wy zapisu na linię (co nie powinno się tu zdarzyć), może pomóc dodanie niektórych wątków do silnika wykonawczego.

Ustaw „AdditionalCriticalWorkerThreads” na 2, a następnie na 4.

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

Wartość domyślna to 0, co oznacza, że ​​nie są dodawane żadne dodatkowe krytyczne wątki robocze jądra. Co zwykle jest w porządku. Ta wartość wpływa na liczbę wątków używanych przez pamięć podręczną systemu plików do żądania odczytu z wyprzedzeniem i zapisu. Zwiększenie tej wartości może pozwolić na kolejkowanie we / wy w podsystemie pamięci masowej (co jest dobre, gdy chcesz pisać wiersz po wierszu), ale jest to droższe procesor.

Dodaj więcej długości kolejki

Zwiększenie wartości „AdditionalCriticalWorkerThreads” zwiększa liczbę wątków, których serwer plików może użyć do obsługi współbieżnych żądań.

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

Wartość domyślna to 20. Wskazanie, że wartość może wymagać zwiększenia, to jeśli kolejki robocze SMB2 stają się bardzo duże (perfcounter „Serwerowe kolejki robocze \ Długość kolejki \ SMB2 *”. Powinien wynosić <100).

bjoster
źródło