L'endl C++ è definito per produrre '\n' seguito da un flush. flush() è un'operazione costosa, quindi in genere dovresti evitare di utilizzare endl come fine linea 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 che includa lo spinning locale ruggine o anche l'ultimo NVMe a un tasso di output ridicolmente alto).
La sostituzione di endl con "\n" risolverà le prestazioni di cui sopra consentendo al sistema di bufferizzare come previsto. Tranne che alcune librerie potrebbero scaricarsi su "\n", nel qual caso hai più mal di testa (vedi https://stackoverflow.com/questions/21129162/tell-endl-not-to-flush per una soluzione che sovrascrive il metodo sync() ).
Ora, per complicare le cose, flush() è definito solo per ciò che accade all'interno dei buffer della libreria. L'effetto dello svuotamento sul sistema operativo, sul disco e su altri buffer esterni non è definito. Per Microsoft.NET "Quando si chiama il metodo FileStream.Flush, viene scaricato anche il buffer di I/O del sistema operativo." (https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx) Ciò rende flush particolarmente costoso per Visual Studio C++ in quanto esegue il round trip della scrittura fino a il supporto fisico all'estremità del tuo 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. L'ispezione e la modifica di questi dipendono dal sistema ." (https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html) Le tue tracce di Ubuntu sembrerebbero indicare che i buffer del sistema operativo/di rete non vengono scaricati 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 di Windows GCC 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 alle proprie esigenze per determinare se lavare ogni linea sia appropriato o meno. endl è generalmente adatto per flussi interattivi come il display (abbiamo bisogno che l'utente veda effettivamente il nostro output, e non in burst), ma generalmente non adatto per altri tipi di flussi, inclusi i file in cui l'overhead di scaricamento può essere significativo. Ho visto app svuotarsi ogni volta che scrivo da 1 e 2 e 4 e 8 byte... non è bello vedere il sistema operativo macinare milioni di IO per scrivere un file da 1 MB.
Ad esempio, un file di registro potrebbe richiedere lo svuotamento di ogni riga se si esegue il debug di un arresto anomalo perché è necessario eliminare l'ofstream prima che si verifichi l'arresto anomalo; mentre un altro file di registro potrebbe non aver bisogno di svuotare ogni riga se sta solo producendo una registrazione informativa dettagliata che dovrebbe essere scaricata automaticamente prima che l'applicazione termini. Non è necessario che sia l'una o l'altra poiché potresti derivare una classe con un algoritmo di svuotamento più sofisticato per soddisfare requisiti specifici.
Confronta il tuo caso con il caso contrastante di persone che devono garantire che i propri dati siano completamente persistenti su disco e non vulnerabili in un buffer del sistema operativo (https://stackoverflow.com/questions/7522479/how-do-i-ensure-data -è-scritto-su-disco-prima-di-chiudere-fstream).
Si noti che come scritto, outFile.flush() è superfluo in quanto scarica un ofstream già scaricato. Per essere pedanti, avresti dovuto usare endl da solo o preferibilmente "\n" con outFile.flush() ma non entrambi.
Le prestazioni delle operazioni su file remoti, come lettura/scrittura, utilizzando 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, la quantità di tempo impiegata è pari almeno alla latenza tra le due parti, che potrebbe essere molto significativa nel caso di Wide Area Network (WAN).
Buffer SMB:MaxBufferSize può essere configurato tramite la seguente impostazione di registro:
HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\SizeReqBuf
Tipo di dati:REG_DWORD
Intervallo:da 1024 a 65535 (scegli il valore in base alle tue esigenze sopra 5000)
MA LA FIRMA SMB influisce sulla dimensione massima del buffer consentita. Quindi dobbiamo disabilitare anche la firma SMB per raggiungere il nostro obiettivo. Il seguente registro 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)
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 tua traccia di livello Linux rispetto a Windows è che stai usando SMB1 su Linux e SMB2 su Windows. Forse il meccanismo di oplock batch funziona meglio in samba SMB1 rispetto all'implementazione del lease esclusivo SMB2. In entrambi i casi ciò dovrebbe consentire una certa quantità di cache lato client.
1) Forse prova a impostare un livello di protocollo massimo inferiore in Samba per provare Windows con SMB12) Verifica che gli oplock o i lease esclusivi siano stati eliminati
Spero che questo aiuti :)