Les petites écritures sur le partage réseau SMB sont lentes sous Windows, rapides par rapport au montage CIFS Linux

10

J'ai eu du mal à résoudre un problème de performances avec un partage SMB / CIFS lors de l'exécution de petites écritures.

Tout d'abord, permettez-moi de décrire ma configuration réseau actuelle:

Serveur

  • Synology DS215j (avec prise en charge SMB3 activée)

Clients (même ordinateur Gig-E câblé à double démarrage)

  • 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

Je teste actuellement les petites performances d'écriture avec le programme suivant écrit en C ++ (sur GitHub ici ):

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

Configuration de montage Linux:

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

Exécution du programme sur Linux (sortie réseau maximale à ~ 100 Mbps):

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

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

Instantané PCAP montrant la segmentation de plusieurs lignes en un seul paquet TCP:

Instantané PCAP Linux

Exécution du programme sous Windows, mesurée par 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

Instantané PCAP sous Windows montrant une seule ligne par demande d'écriture SMB:

Instantané PCAP Windows

Ce même programme prend environ 10 minutes (~ 2,3 Mbps) sous Windows. De toute évidence, Windows PCAP affiche une conversation SMB très bruyante avec une efficacité de charge utile très faible.

Existe-t-il des paramètres sous Windows qui peuvent améliorer les performances d'écriture de petite taille? Il semble qu'en regardant les captures de paquets, Windows ne tamponne pas correctement les écritures et envoie immédiatement les données une ligne à la fois. Alors que sous Linux, les données sont fortement tamponnées et ont donc des performances bien supérieures. Faites-moi savoir si les fichiers PCAP seraient utiles et je peux trouver un moyen de les télécharger.

Mise à jour 27/10/16:

Comme mentionné par @sehafoc, j'ai réduit le max protocolparamètre des serveurs Samba à SMB1 avec les éléments suivants:

max protocol=NT1

Le paramètre ci-dessus a entraîné exactement le même comportement.

J'ai également supprimé la variable de Samba en créant un partage sur une autre machine Windows 10, et elle présente également le même comportement que le serveur Samba, donc je commence à croire qu'il s'agit d'un bogue de mise en cache d'écriture avec les clients Windows en général.

Mise à jour: 10/06/17:

Capture de paquets Linux complète (14 Mo)

Capture de paquets Windows complète (375 Mo)

Mise à jour: 10/12/17:

J'ai également configuré un partage NFS et Windows écrit également sans tampon pour cela. Donc, c'est certainement un problème sous-jacent de client Windows pour autant que je sache, ce qui est vraiment regrettable: - /

Toute aide serait appréciée!

mevatron
la source

Réponses:

2

L'endl C ++ est défini pour sortir '\ n' suivi d'un vidage. flush () est une opération coûteuse, vous devez donc généralement éviter d'utiliser endl comme fin de ligne par défaut car cela peut créer exactement le problème de performances que vous voyez (et pas seulement avec SMB, mais avec tout flux avec un flush coûteux, y compris le spinning local) rouille ou même le dernier NVMe à un taux de sortie ridiculement élevé).

Remplacer endl par "\ n" corrigera les performances ci-dessus en permettant au système de se tamponner comme prévu. Sauf que certaines bibliothèques peuvent vider "\ n", auquel cas vous avez plus de maux de tête (voir /programming/21129162/tell-endl-not-to-flush pour une solution remplaçant la méthode sync () ).

Maintenant, pour compliquer les choses, flush () n'est défini que pour ce qui se passe dans les tampons de bibliothèque. L'effet de vidage sur le système d'exploitation, le disque et les autres tampons externes n'est pas défini. Pour Microsoft.NET "Lorsque vous appelez la méthode FileStream.Flush, le tampon d'E / S du système d'exploitation est également vidé." ( https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx ) Cela rend la vidange particulièrement coûteuse pour Visual Studio C ++ car elle contournera l'écriture jusqu'à le support physique à l'extrémité de votre serveur distant comme vous le voyez. GCC, d'autre part, dit: "Un dernier rappel: il y a généralement plus de tampons impliqués que ceux au niveau de la langue / bibliothèque. Les tampons du noyau, les tampons de disque, etc. auront également un effet. Les inspecter et les modifier dépendent du système. . "https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html ) Vos traces Ubuntu semblent indiquer que les tampons du système d'exploitation / réseau ne sont pas vidés par la bibliothèque flush (). Un comportement dépendant du système serait une raison de plus pour éviter d'endl et de rinçage excessif. Si vous utilisez VC ++, vous pouvez essayer de basculer vers un dérivé de Windows GCC pour voir comment réagissent les comportements dépendants du système, ou utiliser Wine pour exécuter l'exécutable Windows sur Ubuntu.

Plus généralement, vous devez réfléchir à vos besoins pour déterminer si le rinçage de chaque ligne est approprié ou non. endl est généralement adapté aux flux interactifs tels que l'affichage (nous avons besoin que l'utilisateur voit réellement notre sortie, et non en rafales), mais généralement pas adapté aux autres types de flux, y compris les fichiers où la surcharge de rinçage peut être importante. J'ai vu des applications vider toutes les écritures de 1, 2 et 4 et 8 octets ... ce n'est pas joli de voir le système d'exploitation broyer des millions d'E / S pour écrire un fichier de 1 Mo.

Par exemple, un fichier journal peut nécessiter le vidage de chaque ligne si vous déboguez un plantage car vous devez vider le flux avant le plantage; tandis qu'un autre fichier journal peut ne pas avoir besoin de vider toutes les lignes s'il ne fait que produire une journalisation d'informations détaillée qui devrait être vidée automatiquement avant la fin de l'application. Il n'est pas nécessaire que ce soit / ou comme vous pourriez dériver une classe avec un algorithme de vidage plus sophistiqué pour répondre à des exigences spécifiques.

Comparez votre cas avec le cas contrasté des personnes qui doivent s'assurer que leurs données sont complètement conservées sur le disque et non vulnérables dans une mémoire tampon du système d'exploitation ( /programming/7522479/how-do-i-ensure-data -est-écrit-sur-disque-avant-fermeture-fstream ).

Notez que tel qu'écrit, outFile.flush () est superflu car il vide un flux déjà vidé. Pour être pédant, vous devez avoir utilisé endl seul ou de préférence "\ n" avec outFile.flush () mais pas les deux.

Doug
la source
Mille mercis! Vous méritez bien plus de 100 points, mais c'est tout ce que je peux vous donner :) C'était certainement le problème!
mevatron
2

Je n'ai pas assez de réputation pour laisser un commentaire (ce qui je pense serait mieux vu le niveau de vérification de cette réponse).

Je remarque qu'une grande différence dans votre trace de niveau Linux vs Windows est que vous utilisez SMB1 sous Linux et SMB2 sous Windows. Peut-être que le mécanisme de verrouillage par lots fonctionne mieux dans SMB1 samba que l'implémentation de bail exclusif SMB2. Dans les deux cas, cela devrait permettre une certaine mise en cache côté client.

1) Essayez peut-être de définir un niveau de protocole max inférieur dans Samba pour essayer des fenêtres avec SMB1 2) Validez que des oplocks ou des baux exclusifs sont supprimés

J'espère que cela t'aides :)

sehafoc
la source
2

Les performances des opérations de fichiers distants, telles que la lecture / écriture, à l'aide du protocole SMB peuvent être affectées par la taille des tampons alloués par les serveurs et les clients. La taille de la mémoire tampon détermine le nombre d'aller-retour nécessaires pour envoyer une quantité fixe de données. Chaque fois que des demandes et des réponses sont envoyées entre le client et le serveur, le temps nécessaire est au moins égal à la latence entre les deux côtés, ce qui pourrait être très important dans le cas d'un réseau étendu (WAN).

Tampon SMB - Le MaxBufferSize peut être configuré via le paramètre de registre suivant:

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

Type de données: REG_DWORD

Plage: 1024 à 65535 (choisissez une valeur selon vos besoins au-dessus de 5000)

MAIS SMB SIGNING affecte la taille de tampon maximale autorisée. Par conséquent, nous devons également désactiver la signature SMB pour atteindre notre objectif. Le registre suivant doit être créé à la fois côté serveur et si possible côté client également.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters

Nom de la valeur: EnableSecuritySignature

Type de données: REG_DWORD

Données: 0 (désactiver), 1 (activer)

Adi Jha
la source
Merci pour le conseil; cependant, j'ai essayé ces deux remèdes et je vois toujours le comportement ci-dessus: - /
mevatron
Vous pouvez également vérifier pourquoi "Synology DS215j" n'utilise pas SMB3. Par défaut, SMB3 est activé sur Win 8.1.
Adi Jha
1

Phénomène intéressant. Voici ce que j'essaierais - je ne sais pas si cela aide vraiment. Si c'était ma machine, je regarderais longuement les compteurs SMB. L' un d'eux va montrer la cause.

Plus de choses à essayer

Ajouter plus de fils de travail

Dans le cas où le SMB_RDR obens une demande d'E / S d'écriture par ligne (ce qui ne devrait pas se produire ici), il peut être utile d'ajouter des threads au moteur d'exécution.

Définissez "AdditionalCriticalWorkerThreads" sur 2, puis sur 4.

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

La valeur par défaut est 0, ce qui signifie qu'aucun thread de travail critique supplémentaire n'est ajouté. Ce qui est généralement correct. Cette valeur affecte le nombre de threads que le cache du système de fichiers utilise pour les demandes en lecture anticipée et en écriture différée. Augmenter cette valeur peut permettre plus d'E / S en file d'attente dans le sous-système de stockage (ce qui est bien, lorsque vous voulez écrire ligne par ligne), mais c'est plus coûteux en CPU.

Ajouter plus de longueur de file d'attente

L'augmentation de la valeur "AdditionalCriticalWorkerThreads" augmente le nombre de threads que le serveur de fichiers peut utiliser pour traiter les demandes simultanées .

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

La valeur par défaut est 20. Une indication que la valeur peut devoir être augmentée est si les files d'attente de travail SMB2 deviennent très grandes (perfcounter 'Server Work Queues \ Queue Length \ SMB2 *'. Doit être <100).

bjoster
la source