Gravações pequenas no compartilhamento de rede SMB são lentas no Windows, rápidas em montagem CIFS Linux

10

Tenho lutado para corrigir um problema de desempenho com um compartilhamento SMB / CIFS ao realizar pequenas gravações.

Primeiro, deixe-me descrever minha configuração de rede atual:

Servidor

  • Synology DS215j (com suporte SMB3 ativado)

Clientes (mesmo Gig-E com fio com inicialização dupla)

  • 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

Atualmente, estou testando o pequeno desempenho de gravação com o seguinte programa escrito em C ++ (no GitHub aqui ):

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

Configuração de montagem do Linux:

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

Programe o tempo de execução no Linux (atinge a saída da rede em ~ 100Mbps):

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

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

Instantâneo PCAP mostrando a divisão de várias linhas em um único pacote TCP:

Instantâneo PCAP do Linux

Programe o tempo de execução no Windows, conforme medido pelo 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âneo PCAP no Windows mostrando uma única linha por solicitação de gravação SMB:

Instantâneo PCAP do Windows

Este mesmo programa leva cerca de 10 minutos (~ 2.3Mbps) no Windows. Obviamente, o PCAP do Windows mostra uma conversa SMB muito barulhenta com eficiência de carga útil muito baixa.

Existem configurações no Windows que podem melhorar o desempenho de gravação pequena? Parece que, ao observar as capturas de pacotes, o Windows não armazena em buffer as gravações corretamente e envia imediatamente os dados, uma linha por vez. Considerando que, no Linux, os dados são fortemente armazenados em buffer e, portanto, têm desempenho muito superior. Deixe-me saber se os arquivos PCAP seriam úteis e posso encontrar uma maneira de enviá-los.

Atualização 10/27/16:

Conforme mencionado por @sehafoc, reduzi a max protocolconfiguração de servidores Samba para SMB1 com o seguinte:

max protocol=NT1

A configuração acima resultou exatamente no mesmo comportamento.

Também removi a variável do Samba criando um compartilhamento em outra máquina Windows 10, e ela também exibe o mesmo comportamento do servidor Samba, por isso estou começando a acreditar que esse é um erro de cache de gravação nos clientes Windows em geral.

Atualização: 10/06/17:

Captura completa de pacotes do Linux (14 MB)

Captura total de pacotes do Windows (375MB)

Atualização: 10/12/17:

Também configurei um compartilhamento NFS e o Windows escreve sem buffer para isso também. Portanto, é definitivamente um problema subjacente do cliente Windows, até onde eu sei, o que é definitivamente lamentável: - /

Qualquer ajuda seria apreciada!

mevatron
fonte

Respostas:

2

O endl do C ++ é definido para gerar '\ n' seguido de um flush. flush () é uma operação cara, portanto, você deve evitar o uso de endl como seu fim de linha padrão, pois isso pode criar exatamente o problema de desempenho que você está vendo (e não apenas com SMB, mas com qualquer fluxo comum com um flush caro, incluindo rotação local ferrugem ou até o mais recente NVMe com uma taxa de saída ridiculamente alta).

Substituir endl por "\ n" corrigirá o desempenho acima, permitindo que o sistema faça buffer conforme o esperado. Exceto que algumas bibliotecas podem liberar "\ n"; nesse caso, você tem mais dores de cabeça (consulte /programming/21129162/tell-endl-not-to-flush para obter uma solução que substitua o método sync () )

Agora, para complicar as coisas, flush () é definido apenas para o que acontece nos buffers da biblioteca. O efeito de liberação no sistema operacional, disco e outros buffers externos não está definido. Para Microsoft.NET "Quando você chama o método FileStream.Flush, o buffer de E / S do sistema operacional também é liberado." ( https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx ) Isso torna o flush particularmente caro para o Visual Studio C ++, pois ele percorre a gravação até o final do processo. a mídia física na extremidade do servidor remoto como você está vendo. O GCC, por outro lado, diz "Um último lembrete: geralmente há mais buffers envolvidos do que apenas aqueles no nível da linguagem / biblioteca. Buffers de kernel, buffers de disco e similares também terão efeito. Inspecionar e alterar esses itens dependem do sistema . "https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html ) Seus rastreamentos do Ubuntu parecem indicar que os buffers do sistema operacional / rede não são liberados pela liberação da biblioteca (). O comportamento dependente do sistema seria mais um motivo para evitar excessos e falhas. Se você estiver usando o VC ++, tente mudar para um derivado do Windows GCC para ver como os comportamentos dependentes do sistema reagem ou, alternativamente, usar o Wine para executar o executável do Windows no Ubuntu.

Em geral, você precisa pensar nos seus requisitos para determinar se a descarga de cada linha é apropriada ou não. endl geralmente é adequado para fluxos interativos, como a exibição (precisamos que o usuário realmente veja nossa saída, e não em rajadas), mas geralmente não é adequado para outros tipos de fluxos, incluindo arquivos nos quais a sobrecarga de descarga pode ser significativa. Eu já vi aplicativos liberados a cada gravação de 1, 2 e 4 e 8 bytes ... não é bonito ver o SO processar milhões de IOs para gravar um arquivo de 1 MB.

Como exemplo, um arquivo de log pode precisar liberar todas as linhas se você estiver depurando uma falha porque precisa liberar o fluxo antes que a falha ocorra; enquanto outro arquivo de log pode não precisar liberar todas as linhas, se estiver produzindo apenas um log informativo detalhado que é esperado que seja liberado automaticamente antes que o aplicativo seja encerrado. Ele não precisa ser um ou outro, pois você pode derivar uma classe com um algoritmo de liberação mais sofisticado para atender a requisitos específicos.

Compare seu caso com o caso contrastante de pessoas que precisam garantir que seus dados sejam completamente persistentes no disco e não vulneráveis ​​em um buffer do sistema operacional ( /programming/7522479/how-do-i-ensure-data -é-gravado-em-disco-antes-de-fechar-fluxo ).

Observe que, como está escrito, outFile.flush () é supérfluo, pois libera um fluxo já liberado. Para ser pedante, você deveria ter usado endl sozinho ou, de preferência "\ n", com outFile.flush (), mas não ambos.

Doug
fonte
Graças um milhão! Você merece muito mais do que 100 pontos, mas é tudo o que posso dar :) Esse foi definitivamente o problema!
Mevatron # 13/17
2

Não tenho reputação suficiente para deixar um comentário (o que acho melhor, dado o nível de verificação desta resposta).

Percebo que uma grande variação no seu rastreamento de nível Linux x Windows é que você está usando SMB1 no Linux e SMB2 no Windows. Talvez o mecanismo de bloqueio de lote tenha um desempenho melhor no samba SMB1 do que na implementação de concessão exclusiva do SMB2. Nos dois casos, isso deve permitir uma certa quantidade de cache do lado do cliente.

1) Talvez tente definir um nível máximo de protocolo máximo no Samba para testar janelas com o SMB1.

Espero que isto ajude :)

sehafoc
fonte
2

O desempenho das operações remotas de arquivo, como leitura / gravação, usando o protocolo SMB, pode ser afetado pelo tamanho dos buffers alocados pelos servidores e clientes. O tamanho do buffer determina o número de viagens de ida e volta necessárias para enviar uma quantidade fixa de dados. Sempre que solicitações e respostas são enviadas entre cliente e servidor, o tempo gasto é igual a pelo menos a latência entre os dois lados, o que pode ser muito significativo no caso da WAN (Wide Area Network).

Buffer SMB - O MaxBufferSize pode ser configurado através da seguinte configuração do Registro:

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

Tipo de dados: REG_DWORD

Faixa: 1024 a 65535 (escolha o valor conforme sua exigência acima de 5000)

MAS SMB SIGNING afeta o tamanho máximo permitido do buffer. Portanto, precisamos desativar a assinatura SMB e alcançar nosso objetivo. O registro a seguir precisa ser criado no servidor e, se possível, no cliente também.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters

Nome do valor: EnableSecuritySignature

Tipo de dados: REG_DWORD

Dados: 0 (desativar), 1 (ativar)

Adi Jha
fonte
Obrigado pela dica; no entanto, eu tentei tanto desses remédios e eu ainda estou vendo o comportamento acima: - /
MEVATRON
Você também gostaria de verificar por que o "Synology DS215j" não está usando o SMB3. Por padrão, o SMB3 está ativado no Windows 8.1.
Adi Jha #
1

Fenômeno interessante. Aqui está o que eu tentaria - não tenho idéia se isso realmente ajuda. Se fosse minha máquina, eu assistiria extensivamente aos perfcounters de pequenas e médias empresas. Um deles vai mostrar a causa.

Mais coisas para experimentar

Adicionar mais segmentos de trabalho

Caso o SMB_RDR atenda uma solicitação de E / S de gravação por linha (o que não deve acontecer aqui), pode ser útil adicionar alguns encadeamentos ao mecanismo de execução.

Defina "AdditionalCriticalWorkerThreads" para 2 e depois para 4.

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

O padrão é 0, o que significa que nenhum encadeamento crítico adicional do operador do kernel foi adicionado. O que geralmente está ok. Esse valor afeta o número de encadeamentos que o cache do sistema de arquivos usa para solicitações de leitura antecipada e gravação posterior. O aumento desse valor pode permitir mais E / S em fila no subsistema de armazenamento (o que é bom quando você deseja gravar linha por linha), mas é mais caro para a CPU.

Adicionar mais comprimento da fila

Aumentar o valor "AdditionalCriticalWorkerThreads" aumenta o número de threads que o servidor de arquivos pode usar para atender a solicitações simultâneas .

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

O padrão é 20. Uma indicação de que o valor pode precisar ser aumentado é se as filas de trabalho do SMB2 estiverem crescendo muito (o perfcounter 'Filas de trabalho do servidor \ Comprimento da fila \ SMB2 *'. Deve ser <100).

bjoster
fonte