Небольшие записи в общую сетевую папку SMB выполняются медленно в Windows, быстрее при монтировании CIFS Linux

10

Я изо всех сил пытался исправить проблему производительности с общим ресурсом SMB / CIFS при выполнении небольших записей.

Сначала позвольте мне описать мою текущую настройку сети:

сервер

  • Synology DS215j (с включенной поддержкой SMB3)

Клиенты (тот же компьютер с двойной загрузкой проводной Gig-E)

  • Ubuntu 14.04.5 LTS, Трасти Тар
  • 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

В настоящее время я тестирую небольшую производительность записи с помощью следующей программы, написанной на C ++ (на GitHub здесь ):

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

Конфигурация монтирования Linux:

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

Время выполнения программы в Linux (максимальная производительность сети составляет ~ 100 Мбит / с):

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

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

Снимок PCAP, показывающий разбиение множества строк на один пакет TCP:

Linux PCAP снимок

Время выполнения программы в Windows, измеренное 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

Снимок PCAP в Windows, показывающий одну строку на запрос записи SMB:

Снимок Windows PCAP

Эта же программа занимает около 10 минут (~ 2,3 Мбит / с) в Windows. Очевидно, что Windows PCAP показывает очень шумный разговор SMB с очень низкой эффективностью полезной нагрузки.

Есть ли в Windows какие-либо настройки, которые могут улучшить производительность записи при небольших объемах записи? Из анализа захвата пакетов видно, что Windows не буферизует записи должным образом и немедленно отправляет данные по одной строке за раз. Принимая во внимание, что в Linux данные сильно буферизируются и, таким образом, имеют гораздо более высокую производительность. Дайте мне знать, будут ли полезны файлы PCAP, и я найду способ загрузить их.

Обновление 27.10.16:

Как уже упоминалось @sehafoc, я сократил max protocolнастройку серверов Samba до SMB1 с помощью следующего:

max protocol=NT1

Вышеуказанная настройка привела к точно такому же поведению.

Я также удалил переменную Samba, создав общий ресурс на другом компьютере с Windows 10, и он также демонстрирует то же поведение, что и сервер Samba, поэтому я начинаю верить, что это ошибка кэширования записи для клиентов Windows в целом.

Обновление: 10/06/17:

Полный захват пакетов Linux (14 МБ)

Полный захват пакетов Windows (375 МБ)

Обновление: 10/12/17:

Я также настроил общий ресурс NFS, и Windows также пишет без буферизации для этого. Итак, насколько я могу судить, это определенно основная проблема клиента Windows, что, безусловно, вызывает сожаление: - /

Любая помощь будет оценена!

mevatron
источник

Ответы:

2

Endl C ++ определен для вывода '\ n' с последующим сбросом. flush () является дорогостоящей операцией, поэтому обычно следует избегать использования endl в качестве конца строки по умолчанию, поскольку это может создать именно ту проблему с производительностью, которую вы видите (и не только с SMB, но с любым потоком данных с дорогим сбросом, включая локальное вращение) ржавчина или даже новейший NVMe при какой-то смехотворно высокой скорости производства).

Замена endl на «\ n» исправит приведенную выше производительность, позволив системе буферизоваться как задумано. За исключением того, что некоторые библиотеки могут сбрасывать на «\ n», в этом случае у вас больше головной боли (см. Https://stackoverflow.com/questions/21129162/tell-endl-not-to-flush для решения, переопределяющего метод sync () ).

Теперь, чтобы усложнить ситуацию, flush () определяется только для того, что происходит внутри библиотечных буферов. Влияние сброса на операционную систему, диск и другие внешние буферы не определено. Для Microsoft.NET «При вызове метода FileStream.Flush буфер ввода-вывода операционной системы также очищается». ( https://msdn.microsoft.com/en-us/library/2bw4h516(v=vs.110).aspx ) Это делает флеш особенно дорогостоящим для Visual Studio C ++, так как он выполняет круговую обработку записи вплоть до физический носитель в дальнем конце вашего удаленного сервера, как вы видите. GCC, с другой стороны, говорит: «Последнее напоминание: обычно задействовано больше буферов, чем просто на уровне языка / библиотеки. Буферы ядра, дисковые буферы и т. П. Также окажут влияние. Проверка и изменение этих данных зависят от системы «.https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html ) Ваши следы Ubuntu, похоже, указывают на то, что буферы операционной системы / сети не очищаются библиотекой flush (). Системно-зависимое поведение было бы еще одной причиной, чтобы избежать чрезмерного смыва. Если вы используете VC ++, вы можете попробовать переключиться на производную Windows GCC, чтобы посмотреть, как реагирует системно-зависимое поведение, или использовать Wine для запуска исполняемого файла Windows в Ubuntu.

В целом, вам нужно подумать о своих требованиях, чтобы определить, подходит ли очистка каждой строки или нет. endl, как правило, подходит для интерактивных потоков, таких как отображение (нам нужно, чтобы пользователь действительно видел наш вывод, а не в пакетах), но, как правило, не подходит для других типов потоков, включая файлы, в которых издержки очистки могут быть значительными. Я видел, как приложения сбрасываются при каждых 1 и 2 и 4 и 8-байтовых записях ... нехорошо видеть, как ОС теряет миллионы операций ввода-вывода, чтобы написать файл размером 1 МБ.

Например, файл журнала может нуждаться в очистке каждой строки, если вы отлаживаете сбой, потому что вам нужно очистить ofstream, прежде чем произойдет сбой; в то время как другой файл журнала может не нуждаться в очистке каждой строки, если он просто создает подробное информационное ведение журнала, которое, как ожидается, автоматически очистится до завершения работы приложения. Это не должно быть ни / или так как вы могли бы получить класс с более сложным алгоритмом сброса в соответствии с конкретными требованиями.

Сравните ваш случай с противоположным случаем людей, которым необходимо убедиться, что их данные полностью сохранены на диске и не уязвимы в буфере операционной системы ( /programming/7522479/how-do-i-ensure-data -записывается на диск-перед-закрытием-fstream ).

Обратите внимание, что как написано, outFile.flush () является излишним, поскольку он сбрасывает уже очищенный поток. Чтобы быть педантичным, вы должны были использовать endl один или предпочтительно "\ n" с outFile.flush (), но не оба.

Doug
источник
Бесконечно благодарен! Вы заслуживаете более 100 очков, но это все, что я могу дать :) Это определенно была проблема!
Меватрон
2

У меня недостаточно репутации, чтобы оставлять комментарии (что, я думаю, было бы лучше, учитывая уровень проверки этого ответа).

Я заметил, что одна большая разница в вашей трассировке уровня Linux и Windows заключается в том, что вы используете SMB1 в Linux и SMB2 в Windows. Возможно, механизм пакетной блокировки работает лучше в samba SMB1, чем реализация эксклюзивной аренды SMB2. В обоих случаях это должно позволить некоторое количество кэширования на стороне клиента.

1) Возможно, попробуйте установить более низкий максимальный уровень протокола в Samba, чтобы опробовать окна с SMB1. 2) Проверить, что исключены эксклюзивные блокировки или аренды.

Надеюсь это поможет :)

sehafoc
источник
2

На производительность удаленных файловых операций, таких как чтение / запись, с использованием протокола SMB, может влиять размер буферов, выделяемых серверами и клиентами. Размер буфера определяет количество циклов, необходимых для отправки фиксированного объема данных. Каждый раз, когда запросы и ответы отправляются между клиентом и сервером, время, которое требуется, равно как минимум задержке между обеими сторонами, что может быть очень значительным в случае глобальной сети (WAN).

SMB-буфер - MaxBufferSize можно настроить с помощью следующего параметра реестра:

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

Тип данных: REG_DWORD

Диапазон: от 1024 до 65535 (выберите значение согласно вашему требованию выше 5000)

НО SMB SIGNING влияет на максимально допустимый размер буфера. Таким образом, нам нужно отключить подпись SMB, чтобы достичь нашей цели. Следующий реестр должен быть создан как на стороне сервера, так и, если возможно, на стороне клиента.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters

Имя значения: EnableSecuritySignature

Тип данных: REG_DWORD

Данные: 0 (отключить), 1 (включить)

Ади Джа
источник
Спасибо за совет; Тем не менее, я попробовал оба эти средства, и я все еще вижу вышеупомянутое поведение: - /
mevatron
Вы также можете проверить, почему Synology DS215j не использует SMB3. По умолчанию SMB3 включен в Win 8.1.
Ади Джа
1

Интересное явление. Вот что я бы попробовал - я понятия не имею, действительно ли это помогает. Если бы это была моя машина, я бы внимательно следил за перфокастерами SMB. Один из них будет показывать причину.

Больше вещей, чтобы попробовать

Добавить больше рабочих потоков

В случае, если SMB_RDR обрабатывает один запрос ввода-вывода на строку (что не должно происходить здесь), это может помочь добавить некоторые потоки в механизм исполнения.

Установите «AdditionalCriticalWorkerThreads» на 2, затем на 4.

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

По умолчанию 0, что означает, что никакие дополнительные критические рабочие потоки ядра не добавляются. Что обычно нормально. Это значение влияет на количество потоков, которые кэш файловой системы использует для запросов на чтение и запись. Увеличение этого значения может позволить увеличить количество операций ввода-вывода в очереди в подсистеме хранения (что хорошо, когда вы хотите писать построчно), но это более затратно для ЦП.

Добавить больше Длина очереди

Увеличение значения AdditionalCriticalWorkerThreads увеличивает количество потоков, которые файловый сервер может использовать для обслуживания одновременных запросов.

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

Значение по умолчанию - 20. Указание на то, что значение может потребоваться увеличить, - это если рабочие очереди SMB2 становятся очень большими (perfcounter «Серверные рабочие очереди \ Длина очереди \ SMB2 *». Должен быть <100).

bjoster
источник