Как максимизировать пропускную способность при обработке большого количества файлов - PullRequest
1 голос
/ 07 марта 2019

Скажем, вы хотите обработать множество файлов как можно быстрее, где время обработки> время чтения файла.

  • Будет ли чтение нескольких файлов с использованием пула потоков увеличивать пропускную способность?или это просто вызывает больше конфликтов на диске?
  • Если пул потоков помогает, что определяет, сколько потоков необходимо для достижения максимума?это можно рассчитать на основе целевой системы?
  • Для одного ядра, будет ли чтение и обработка цикла асинхронно через потоки быстрее, чем синхронное?Я полагаю, так как задержка диска очень высока, было бы.Но, может быть, если чтение файла намного меньше времени обработки, лучше оставить этап обработки непрерывным без переключения контекста.

Кроме того, есть ли у вас какие-либо другие советы по максимизации пропускной способности диска?

1 Ответ

1 голос
/ 15 марта 2019

Я провел несколько тестов, чтобы придумать некоторые общие рекомендации. Я тестировал с ~ 500k маленькими (~ 14kb) файлами. Я думаю, что результаты должны быть одинаковыми для файлов среднего размера; но для больших файлов, я подозреваю, конфликт дисков становится более значительным. Было бы полезно, если бы кто-то, обладающий более глубокими знаниями о внутреннем оборудовании ОС / оборудования, мог дополнить этот ответ более конкретными объяснениями того, почему некоторые вещи работают быстрее, чем другие.

Я тестировал компьютер с 16 виртуальными ядрами (8 физических) с двухканальной оперативной памятью и ядром Linux 4.18.

Увеличивают ли потоки пропускную способность чтения?

Ответ - да. Я думаю, что это может быть связано либо с 1) аппаратным ограничением полосы пропускания для однопоточных приложений, либо с 2) очередь запросов диска ОС лучше используется, когда многие потоки делают запросы. Наилучшая производительность с потоками virtual_cores*2. Пропускная способность постепенно снижается, возможно, из-за увеличения конкуренции за диск. Если страницы кэшируются в ОЗУ, то лучше иметь пул потоков размером virtual_cores. Однако, если <50% страниц кэшируется (что, я думаю, является более распространенным случаем), тогда <code>virtual_cores*2 будет работать нормально.

Я думаю, что причина, по которой virtual_cores*2 лучше, чем просто virtual_cores, заключается в том, что чтение файла также включает некоторую задержку, не связанную с диском, такую ​​как системные вызовы, декодирование и т. Д. Поэтому, возможно, процессор может более эффективно чередовать потоки: пока один ожидает на диске, второй может выполнять не связанные с диском операции чтения файла. ( Может ли это быть связано с тем, что ОЗУ является двухканальным? )

Я тестировал чтение случайных файлов vs последовательно (просматривая расположение физического блока файлов в хранилище и упорядочивая запросы по нему). Последовательный доступ дает довольно значительное улучшение с жесткими дисками, что и следовало ожидать. Если ограничивающим фактором в вашем приложении является время чтения файла, а не обработка указанных файлов, я предлагаю вам изменить порядок запросов на последовательный доступ, чтобы получить повышение.

read throughput vs thread count

Существует возможность использовать асинхронный дисковый ввод-вывод вместо пула потоков. Однако, судя по моим прочтениям, пока нет портативного способа сделать это ( см. Эту ветку reddit ). Кроме того, libuv, который поддерживает NodeJS , использует пул потоков для обработки файлового ввода-вывода.

Балансирование чтения и пропускная способность обработки

В идеале, мы могли бы иметь чтение и обработку в отдельных потоках. Пока мы обрабатываем первый файл, мы можем поставить следующий в очередь в другом потоке. Но чем больше потоков мы выделяем для чтения файлов, тем больше нагрузка на ЦП с потоками обработки. Решение состоит в том, чтобы обеспечить более быструю работу (чтение по сравнению с обработкой) наименьшим числом потоков, при этом обеспечивая нулевую задержку обработки между файлами. Эта формула показала хорошие результаты в моих тестах:

prop = read_time/process_time
if prop > 1:
    # double virtual core count gives fastest reads, as per tests above
    read_threads = virtual_cores*2
    process_threads = ceil(read_threads/(2*prop))
else:
    process_threads = virtual_cores
    # double read thread pool so CPU can interleave better, as mentioned above
    read_threads = 2*ceil(process_threads*prop)

Например: Чтение = 2 с, Процесс = 10 с; так что есть 2 темы чтения для каждых 5 потоков обработки

В моих тестах потеря производительности при использовании дополнительных потоков чтения составляет всего 1-1,5%. В моих тестах при prop, близком к нулю, 1 поток операций чтения + 16 имел почти такую ​​же пропускную способность, как 32 потока операций чтения + 16. Современные потоки должны быть достаточно легкими, а прочитанные потоки должны быть спящими в любом случае, если файлы не используются достаточно быстро. (То же самое должно быть верно для потоков процесса, когда prop очень большой)

С другой стороны, слишком небольшое количество читающих потоков оказывает гораздо более значительное влияние (мой третий оригинальный вопрос).Например, для очень больших prop 1 чтение + 16 потоков процессов было на 36% медленнее, чем 1 чтение + 15 потоков процессов.Поскольку потоки процесса занимают все ядра эталонного компьютера, поток чтения имеет слишком большую нагрузку на процессор и не может 36% времени поставить в очередь следующий файл для обработки.Поэтому я рекомендую ошибиться в пользу слишком большого количества прочитанных тем.Это должно удвоить размер пула потоков чтения, как в моей формуле выше.

Примечание: Вы можете ограничить ресурсы ЦП, которые потребляет ваше приложение, установив virtual_cores в меньшем процентном соотношениидоступные ядра.Вы также можете отказаться от удвоения, поскольку нехватка ресурсов ЦП может стать менее серьезной проблемой, если имеется запасное ядро ​​или более, которые не выполняют более интенсивные потоки обработки.

Сводка

Исходя из результатов моего теста, использование пула потоков с virtual_cores*2 потоками чтения файлов + virtual_cores потоков обработки файлов даст вам хорошую производительность для множества различных сценариев синхронизации.Эта конфигурация должна давать вам ~ 2% от максимальной пропускной способности, не тратя много времени на сравнительный анализ.

...