Я изо всех сил пытался исправить проблему производительности с общим ресурсом SMB / CIFS при выполнении небольших операций записи.
Во-первых, позвольте мне описать мою текущую настройку сети:
Сервер
Клиенты (тот же компьютер с двойной загрузкой, проводной Gig-E)
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-пакет:
Время выполнения программы в 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 занимает около 10 минут (~ 2,3 Мбит / с). Очевидно, что Windows PCAP показывает очень шумный разговор SMB с очень низкой эффективностью полезной нагрузки.
Существуют ли какие-либо настройки в Windows, которые могут улучшить производительность записи небольших файлов? Глядя на захват пакетов, кажется, что Windows не буферизует записи должным образом и немедленно отправляет данные по одной строке за раз. В то время как в Linux данные сильно буферизуются и, следовательно, имеют гораздо более высокую производительность. Сообщите мне, будут ли вам полезны файлы PCAP, и я найду способ их загрузить.
Обновление 27.10.16:
Как упоминал @sehafoc, я уменьшил серверы Samba max protocol
настройте SMB1 следующим образом:
max protocol=NT1
Вышеуказанная настройка привела к точно такому же поведению.
Я также удалил переменную Samba, создав общий ресурс на другом компьютере с Windows 10, и он также демонстрирует то же поведение, что и сервер Samba, поэтому я начинаю верить, что это ошибка кэширования записи с клиентами Windows в целом.
Обновление: 06.10.17:
Полный захват пакетов Linux (14 МБ)
Полный захват пакетов Windows (375 МБ)
Обновление: 12.10.17:
Я также установил общий ресурс NFS, и Windows также пишет без буферизации. Итак, насколько я могу судить, это определенно основная проблема клиента Windows, что определенно прискорбно: - /
Любая помощь будет оценена по достоинству!
На производительность удаленных файловых операций, таких как чтение / запись, с использованием протокола SMB, может влиять размер буферов, выделяемых серверами и клиентами. Размер буфера определяет количество циклов, необходимых для отправки фиксированного количества данных. Каждый раз, когда запросы и ответы отправляются между клиентом и сервером, количество затрачиваемого времени равно по крайней мере задержке между обеими сторонами, что может быть очень значительным в случае глобальной сети (WAN).
Буфер SMB - MaxBufferSize можно настроить с помощью следующего параметра реестра:
HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\SizeReqBuf
Тип данных: REG_DWORD
Диапазон: от 1024 до 65535 (выберите значение в соответствии с вашими требованиями выше 5000)
НО ПОДПИСЬ SMB влияет на максимально допустимый размер буфера. Таким образом, нам нужно отключить подписывание SMB, чтобы достичь нашей цели. Следующий реестр должен быть создан как на стороне сервера, так и, если возможно, на стороне клиента.
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LanManWorkstation\Parameters
Имя значения: EnableSecuritySignature
Тип данных: REG_DWORD
Данные: 0 (выключить), 1 (включить)
У меня недостаточно репутации, чтобы оставить комментарий (что, я думаю, было бы лучше, учитывая уровень проверки этого ответа).
Я заметил, что одна большая разница в вашей трассировке уровня Linux и Windows заключается в том, что вы используете SMB1 в Linux и SMB2 в Windows. Возможно, механизм пакетной оппортунистической блокировки работает лучше в SMB1 samba, чем реализация эксклюзивной аренды SMB2. В обоих случаях они должны обеспечивать некоторое кэширование на стороне клиента.
1) Возможно, попробуйте установить более низкий максимальный уровень протокола в Samba, чтобы опробовать окна с SMB1 2) Убедитесь, что исключены исключительные блокировки или аренда
Надеюсь это поможет :)
C ++ endl определен для вывода '\ 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 до того, как произойдет сбой; в то время как другой файл журнала может не нуждаться в очистке каждой строки, если он просто создает подробный информационный журнал, который, как ожидается, будет очищен автоматически перед завершением работы приложения. Это не обязательно должно быть или / или, поскольку вы можете создать класс с более сложным алгоритмом очистки, отвечающим конкретным требованиям.
Сравните ваш случай с противоположным случаем людей, которым необходимо убедиться, что их данные полностью сохраняются на диске и не уязвимы в буфере операционной системы (https://stackoverflow.com/questions/7522479/how-do-i-ensure-data-is-written-to-disk-before-closing-fstream).
Обратите внимание, что, как написано, outFile.flush () является лишним, поскольку он сбрасывает уже сброшенный поток. Чтобы быть педантичным, вам следовало использовать только endl или предпочтительно "\ n" с outFile.flush (), но не оба сразу.
Интересное явление. Вот что я бы попробовал - понятия не имею, действительно ли это помогает. Если бы это была моя машина, я бы внимательно следил за счетчиками 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 «Server Work Queues \ Queue Length \ SMB2 *». Должно быть <100).