Las pequeñas escrituras en el recurso compartido de red SMB son lentas en Windows, rápidas sobre el montaje CIFS Linux

10

He estado luchando para solucionar un problema de rendimiento con un recurso compartido SMB / CIFS al realizar pequeñas escrituras.

Primero, permítame describir mi configuración de red actual:

Servidor

  • Synology DS215j (con soporte SMB3 habilitado)

Clientes (misma computadora con cable de arranque dual 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

Actualmente estoy probando el rendimiento de escritura pequeña con el siguiente programa escrito en C ++ (en GitHub aquí ):

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

Configuración de montaje de Linux:

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

Programa en tiempo de ejecución en Linux (picos de salida de red a ~ 100Mbps):

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

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

Instantánea PCAP que muestra el agrupamiento de muchas líneas en un solo paquete TCP:

Instantánea de PCAP de Linux

Programe el tiempo de ejecución en Windows medido por 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

Instantánea de PCAP en Windows que muestra una sola línea por solicitud de escritura SMB:

Instantánea de PCAP de Windows

Este mismo programa tarda unos 10 minutos (~ 2.3Mbps) en Windows. Obviamente, el PCAP de Windows muestra una conversación SMB muy ruidosa con una eficiencia de carga muy baja.

¿Hay alguna configuración en Windows que pueda mejorar el rendimiento de escritura pequeña? Al observar las capturas de paquetes, parece que Windows no almacena las escrituras correctamente e inmediatamente envía los datos línea por línea. Mientras que, en Linux, los datos están fuertemente almacenados y, por lo tanto, tienen un rendimiento muy superior. Avíseme si los archivos PCAP serían útiles y puedo encontrar una manera de cargarlos.

Actualización 27/10/16:

Como mencionó @sehafoc, reduje la max protocolconfiguración de los servidores Samba a SMB1 con lo siguiente:

max protocol=NT1

La configuración anterior resultó exactamente en el mismo comportamiento.

También eliminé la variable de Samba al crear un recurso compartido en otra máquina con Windows 10, y también exhibe el mismo comportamiento que el servidor Samba, por lo que estoy empezando a creer que este es un error de escritura en caché con los clientes de Windows en general.

Actualización: 10/06/17:

Captura completa de paquetes de Linux (14 MB)

Captura completa de paquetes de Windows (375 MB)

Actualización: 12/10/17:

También configuré un recurso compartido NFS y Windows también escribe sin almacenamiento en búfer para esto. Entonces, definitivamente, es un problema subyacente del cliente de Windows, por lo que puedo decir, lo cual es definitivamente desafortunado: - /

¡Cualquier ayuda sería apreciada!

mevatron
fuente

Respuestas:

2

El C ++ endl está definido para la salida '\ n' seguido de un vaciado. flush () es una operación costosa, por lo que generalmente debe evitar usar endl como su final de línea predeterminado, ya que puede crear exactamente el problema de rendimiento que está viendo (y no solo con SMB, sino con cualquiera de las corrientes con un flujo costoso, incluido el giro local) óxido o incluso el último NVMe a una velocidad de salida ridículamente alta).

Reemplazar endl con "\ n" arreglará el rendimiento anterior al permitir que el sistema se almacene según lo previsto. Excepto que algunas bibliotecas pueden descargarse en "\ n", en cuyo caso tiene más dolores de cabeza (consulte /programming/21129162/tell-endl-not-to-flush para obtener una solución que anule el método sync () )

Ahora para complicar las cosas, flush () solo se define para lo que sucede dentro de los buffers de la biblioteca. El efecto del vaciado en el sistema operativo, el disco y otros búferes externos no está definido. Para Microsoft.NET "Cuando llama al método FileStream.Flush, el búfer de E / S del sistema operativo también se vacía". ( https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx ) Esto hace que el vaciado sea particularmente costoso para Visual Studio C ++, ya que hará que la escritura se desplace por completo. los medios físicos en el otro extremo de su servidor remoto como lo está viendo Por otro lado, GCC dice: "Un último recordatorio: generalmente hay más buffers involucrados que solo aquellos en el nivel de idioma / biblioteca. Los buffers de kernel, de disco y similares también tendrán un efecto. La inspección y el cambio dependen del sistema ".https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html ) Sus rastreos de Ubuntu parecerían indicar que el blush de la biblioteca no vacía los sistemas operativos / buffers de red. El comportamiento dependiente del sistema sería una razón más para evitar endl y enrojecimiento excesivo. Si está utilizando VC ++, puede intentar cambiar a un derivado de Windows GCC para ver cómo reaccionan los comportamientos dependientes del sistema, o alternativamente usar Wine para ejecutar el ejecutable de Windows en Ubuntu.

En general, debe pensar en sus requisitos para determinar si el enjuague de cada línea es apropiado o no. endl generalmente es adecuado para transmisiones interactivas como la pantalla (necesitamos que el usuario vea realmente nuestra salida, y no en ráfagas), pero generalmente no es adecuado para otros tipos de transmisiones, incluidos archivos donde la sobrecarga de descarga puede ser significativa. He visto que las aplicaciones se descargan en cada escritura de 1 y 2 y 4 y 8 bytes ... No es agradable ver que el sistema operativo muele millones de E / S para escribir un archivo de 1 MB.

Como ejemplo, un archivo de registro puede necesitar vaciar cada línea si está depurando un bloqueo porque necesita limpiar el flujo de corriente antes de que ocurra el bloqueo; mientras que otro archivo de registro puede no necesitar vaciar cada línea si solo está produciendo un registro informativo detallado que se espera que se vacíe automáticamente antes de que la aplicación finalice. No es necesario que sea uno o más, ya que podría derivar una clase con un algoritmo de descarga más sofisticado para satisfacer requisitos específicos.

Compare su caso con el caso contrastante de las personas que necesitan asegurarse de que sus datos permanezcan completamente en el disco y no sean vulnerables en un búfer del sistema operativo ( /programming/7522479/how-do-i-ensure-data -es-escrito-a-disco-antes-cierre-fstream ).

Tenga en cuenta que, tal como está escrito, outFile.flush () es superfluo, ya que descarga un flujo de corriente ya descargado. Para ser pedante, debería haber usado endl solo o preferiblemente "\ n" con outFile.flush () pero no ambos.

Doug
fuente
¡Un millón de gracias! Te mereces más de 100 puntos, pero eso es todo lo que puedo darte :) ¡Este fue definitivamente el problema!
mevatron
2

No tengo suficiente reputación para dejar un comentario (que creo que sería mejor dado el nivel de verificación de esta respuesta).

Noté que una gran variación en el rastreo de nivel de Linux vs Windows es que está utilizando SMB1 en Linux y SMB2 en Windows. Quizás el mecanismo de bloqueo por lotes funcione mejor en SMB1 samba que la implementación de arrendamiento exclusiva SMB2. En ambos casos, esto debería permitir cierta cantidad de almacenamiento en caché del lado del cliente.

1) Quizás intente establecer un nivel de protocolo máximo más bajo en Samba para probar ventanas con SMB1 2) Valide que se obtengan oplocks o arrendamientos exclusivos

Espero que esto ayude :)

sehafoc
fuente
2

El rendimiento de las operaciones de archivos remotos, como lectura / escritura, utilizando el protocolo SMB puede verse afectado por el tamaño de los buffers asignados por servidores y clientes. El tamaño del búfer determina la cantidad de viajes de ida y vuelta necesarios para enviar una cantidad fija de datos. Cada vez que se envían solicitudes y respuestas entre el cliente y el servidor, la cantidad de tiempo necesario es al menos igual a la latencia entre ambos lados, lo que podría ser muy significativo en el caso de la red de área amplia (WAN).

Búfer SMB: MaxBufferSize se puede configurar mediante la siguiente configuración de registro:

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

Tipo de datos: REG_DWORD

Rango: 1024 a 65535 (Elija el valor según su requisito por encima de 5000)

PERO SMB SIGNING afecta el tamaño máximo permitido del búfer. Por lo tanto, también debemos deshabilitar la firma de SMB para lograr nuestro objetivo. El siguiente registro debe crearse en el lado del servidor y, si es posible, en el lado del cliente también.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters

Nombre del valor: EnableSecuritySignature

Tipo de datos: REG_DWORD

Datos: 0 (deshabilitar), 1 (habilitar)

Adi Jha
fuente
Gracias por el consejo; sin embargo, probé ambos remedios y todavía veo el comportamiento anterior: - /
mevatron
También le gustaría comprobar por qué "Synology DS215j" no está utilizando SMB3. De forma predeterminada, SMB3 está habilitado en Win 8.1.
Adi Jha
1

Fenómeno interesante Esto es lo que intentaría: no tengo idea si esto realmente ayuda. Si se tratara de mi máquina, miraría mucho los contadores de SMB. Uno de ellos se mostrará la causa.

Más cosas para probar

Agregar más hilos de trabajo

En caso de que SMB_RDR obtenga una solicitud de E / S de escritura por línea (lo que no debería suceder aquí), puede ser útil agregar algunos subprocesos al motor de ejecución.

Establezca "AdditionalCriticalWorkerThreads" en 2, luego en 4.

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

El valor predeterminado es 0, lo que significa que no se agregan subprocesos de trabajo de kernel críticos adicionales. Lo cual normalmente está bien. Este valor afecta el número de subprocesos que la caché del sistema de archivos utiliza para las solicitudes de lectura anticipada y de escritura diferida. Elevar este valor puede permitir más E / S en cola en el subsistema de almacenamiento (lo cual es bueno, cuando desea escribir línea por línea), pero es más costoso para la CPU.

Agregar más longitud de cola

El aumento del valor "AdditionalCriticalWorkerThreads" aumenta el número de subprocesos que el servidor de archivos puede usar para atender solicitudes concurrentes .

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

El valor predeterminado es 20. Una indicación de que es posible que sea necesario aumentar el valor es si las colas de trabajo SMB2 crecen mucho (el contador "Colas de trabajo del servidor \ Longitud de la cola \ SMB2 *" debe ser <100).

bjoster
fuente