Le piccole scritture sulla condivisione di rete SMB sono lente su Windows, veloci su mount CIFS Linux


10

Ho avuto difficoltà a risolvere un problema di prestazioni con una condivisione SMB / CIFS durante l'esecuzione di piccole scritture.

Prima di tutto, lasciami descrivere la mia attuale configurazione di rete:

server

  • Synology DS215j (con supporto SMB3 abilitato)

Client (stesso computer Gig-E cablato a doppio avvio)

  • 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

Attualmente sto testando le piccole prestazioni di scrittura con il seguente programma scritto in C ++ (su GitHub qui ):

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

Configurazione di montaggio Linux:

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

Tempo di esecuzione del programma su Linux (picco dell'output di rete a ~ 100 Mbps):

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

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

Istantanea PCAP che mostra la suddivisione di più righe in un singolo pacchetto TCP:

Istantanea PCAP Linux

Runtime del programma su Windows misurato da 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

Istantanea PCAP su Windows che mostra una riga singola per richiesta di scrittura SMB:

Istantanea PCAP di Windows

Questo stesso programma richiede circa 10 minuti (~ 2.3 Mbps) su Windows. Ovviamente, il PCAP di Windows mostra una conversazione SMB molto rumorosa con efficienza di payload molto bassa.

Ci sono delle impostazioni su Windows che possono migliorare le prestazioni di scrittura di piccole dimensioni? Dall'acquisizione dei pacchetti sembra che Windows non memorizzi correttamente le scritture e invii immediatamente i dati una riga alla volta. Considerando che, su Linux, i dati sono fortemente bufferizzati e quindi hanno prestazioni di gran lunga superiori. Fammi sapere se i file PCAP potrebbero essere utili e posso trovare un modo per caricarli.

Aggiornamento 27/10/16:

Come menzionato da @sehafoc, ho ridotto l' max protocolimpostazione dei server Samba a SMB1 con il seguente:

max protocol=NT1

L'impostazione sopra ha portato allo stesso identico comportamento.

Ho anche rimosso la variabile di Samba creando una condivisione su un'altra macchina Windows 10, e presenta anche lo stesso comportamento del server Samba, quindi sto cominciando a credere che si tratti di un bug di memorizzazione nella cache con i client Windows in generale.

Aggiornamento: 10/06/17:

Acquisizione di pacchetti Linux completa (14 MB)

Acquisizione pacchetto completa di Windows (375 MB)

Aggiornamento: 10/10/17:

Ho anche installato una condivisione NFS e Windows scrive senza buffer anche per questo. Quindi, per quanto posso dire, è sicuramente un problema client di Windows sottostante, il che è decisamente sfortunato: - /

Qualsiasi aiuto sarebbe apprezzato!

Risposte:


2

L'endl C ++ è definito in output '\ n' seguito da un flush. flush () è un'operazione costosa, quindi in genere dovresti evitare di utilizzare endl come end of line predefinito in quanto può creare esattamente il problema di prestazioni che stai riscontrando (e non solo con SMB, ma con qualsiasi ofstream con un flush costoso incluso la filatura locale ruggine o persino l'ultima NVMe con un tasso di uscita ridicolmente alto).

La sostituzione di endl con "\ n" risolverà le prestazioni di cui sopra consentendo al sistema di bufferizzare come previsto. Tranne per il fatto che alcune librerie possono scaricare "\ n", nel qual caso hai più mal di testa (vedi /programming/21129162/tell-endl-not-to-flush per una soluzione che ha la priorità sul metodo sync () ).

Ora per complicare le cose, flush () è definito solo per ciò che accade all'interno dei buffer della libreria. L'effetto del flush sul sistema operativo, sul disco e su altri buffer esterni non è definito. Per Microsoft.NET "Quando si chiama il metodo FileStream.Flush, anche il buffer I / O del sistema operativo viene scaricato." ( https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx ) Questo rende il flush particolarmente costoso per Visual Studio C ++ in quanto andrà a completare la scrittura fino a il supporto fisico all'estremità remota del server remoto come stai vedendo. GCC d'altra parte dice "Un ultimo promemoria: di solito ci sono più buffer coinvolti rispetto a quelli a livello di lingua / libreria. Anche i buffer del kernel, i buffer del disco e simili avranno un effetto. Ispezionare e modificare quelli dipendono dal sistema ".https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html ) Le tracce di Ubuntu sembrano indicare che i buffer del sistema operativo / della rete non vengono svuotati dalla libreria flush (). Il comportamento dipendente dal sistema sarebbe una ragione in più per evitare endl e flushing eccessivo. Se stai usando VC ++ potresti provare a passare a un derivato GCC di Windows per vedere come reagiscono i comportamenti dipendenti dal sistema, o in alternativa usare Wine per eseguire l'eseguibile di Windows su Ubuntu.

Più in generale, è necessario pensare ai propri requisiti per determinare se lo svuotamento di ogni linea è appropriato o meno. endl è generalmente adatto a flussi interattivi come il display (è necessario che l'utente visualizzi effettivamente il nostro output e non a raffica), ma generalmente non è adatto per altri tipi di flussi, inclusi i file in cui l'overhead del flushing può essere significativo. Ho visto app scaricare ogni 1 e 2 e 4 e 8 byte di scrittura ... non è bello vedere il sistema operativo macinare milioni di IO per scrivere un file da 1 MB.

Ad esempio un file di registro potrebbe aver bisogno di svuotare ogni riga se si esegue il debug di un arresto anomalo perché è necessario svuotare il flusso di flusso prima che si verifichi l'incidente; mentre un altro file di registro potrebbe non aver bisogno di svuotare ogni riga se sta solo producendo una registrazione informativa dettagliata che dovrebbe svuotare automaticamente prima che l'applicazione venga chiusa. Non è necessario che sia / o poiché si potrebbe derivare una classe con un algoritmo di flush più sofisticato per soddisfare requisiti specifici.

Confronta il tuo caso con il caso contrastante di persone che devono assicurarsi che i loro dati siano completamente persistenti su disco e non vulnerabili in un buffer del sistema operativo ( /programming/7522479/how-do-i-ensure-data -è-scritto-su-disco-prima-di-chiusura-fstream ).

Si noti che, come scritto, outFile.flush () è superfluo in quanto scarica un flusso già scaricato. Per essere pedanti, avresti dovuto usare endl da solo o preferibilmente "\ n" con outFile.flush () ma non entrambi.


Grazie mille! Ti meriti oltre 100 punti, ma questo è tutto ciò che posso dare :) Questo è stato sicuramente il problema!
Mevatron,

2

Non ho abbastanza reputazione per lasciare un commento (che penso sarebbe meglio dato il livello di verifica su questa risposta).

Ho notato che una grande variazione nella traccia di livello Linux vs Windows è che stai usando SMB1 su Linux e SMB2 in Windows. Forse il meccanismo di oplock batch funziona meglio nella samba SMB1 rispetto all'implementazione del lease esclusivo SMB2. In entrambi i casi, ciò dovrebbe consentire una certa quantità di cache sul lato client.

1) Forse prova a impostare un livello di protocollo massimo inferiore in Samba per provare windows con SMB1 2) Convalida che vengano eliminati oplock o leasing esclusivi

Spero che sia di aiuto :)


2

Le prestazioni delle operazioni su file remoti, come lettura / scrittura, mediante il protocollo SMB possono essere influenzate dalla dimensione dei buffer allocati da server e client. La dimensione del buffer determina il numero di round trip necessari per inviare una quantità fissa di dati. Ogni volta che vengono inviate richieste e risposte tra client e server, il tempo impiegato è pari almeno alla latenza tra le due parti, il che potrebbe essere molto significativo nel caso della Wide Area Network (WAN).

Buffer SMB: MaxBufferSize può essere configurato tramite le seguenti impostazioni del registro:

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

Tipo di dati: REG_DWORD

Intervallo: da 1024 a 65535 (Scegli il valore secondo il tuo requisito sopra 5000)

MA SEGNALAZIONE SMB influisce sulla dimensione massima del buffer consentita. Pertanto, è necessario disabilitare anche la firma SMB per raggiungere il nostro obiettivo. Il registro seguente deve essere creato sia sul lato server che, se possibile, anche sul lato client.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters

Nome valore: EnableSecuritySignature

Tipo di dati: REG_DWORD

Dati: 0 (disabilita), 1 (abilita)


Grazie per il consiglio; tuttavia, ho provato entrambi questi rimedi e sto ancora vedendo il comportamento sopra riportato: - /
mevatron,

Ti piacerebbe anche verificare perché "Synology DS215j" non utilizza SMB3. Per impostazione predefinita, SMB3 è abilitato su Win 8.1.
Adi Jha,

1

Fenomeno interessante Ecco cosa proverei: non ho idea se questo aiuti davvero. Se fosse la mia macchina, guarderei ampiamente i perfcounters di SMB. Uno di loro sarà mostrare la causa.

Altre cose da provare

Aggiungi più thread di lavoro

Nel caso in cui SMB_RDR ottenga una richiesta I / O di scrittura per riga (cosa non dovrebbe succedere qui), può essere utile aggiungere alcuni thread al motore di esecuzione.

Impostare "AdditionalCriticalWorkerThreads" su 2, quindi su 4.

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

Il valore predefinito è 0, il che significa che non vengono aggiunti ulteriori thread di lavoro del kernel critico. Che di solito è ok. Questo valore influisce sul numero di thread utilizzati dalla cache del file system per le richieste read-ahead e write-behind. L'aumento di questo valore può consentire un numero maggiore di I / O in coda nel sottosistema di archiviazione (il che è utile quando si desidera scrivere riga per riga), ma è più costoso della CPU.

Aggiungi più lunghezza della coda

L'aumento del valore "AdditionalCriticalWorkerThreads" aumenta il numero di thread che il file server può utilizzare per soddisfare le richieste simultanee .

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

Il valore predefinito è 20. Un'indicazione che potrebbe essere necessario aumentare il valore è se le code di lavoro SMB2 stanno diventando molto grandi (il perfcounter 'Code di lavoro server \ Lunghezza coda \ SMB2 *'. Dovrebbe essere <100).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.