Больше потоков не обязательно даст вам большую пропускную способность. Потоки имеют нетривиальную стоимость, как для создания (с точки зрения времени процессора и ресурсов ОС), так и для запуска (с точки зрения памяти и планирования). И чем больше у вас потоков, тем больше вероятность конфликта с другими потоками. Добавление потоков может иногда даже замедлять выполнение. Каждая проблема немного отличается, и вам лучше написать хорошее, гибкое решение и поэкспериментировать с параметрами, чтобы увидеть, что работает лучше.
Ваш пример кода, порождающий поток для каждого файла, почти сразу затопит систему значениями max_threads
, превышающими около 10. Как и другие предлагали, пул потоков с рабочей очередью - это то, что вы, вероятно, хотите. Приятен тот факт, что каждый файл независим, поскольку это делает его почти смущающе параллельным (за исключением агрегации в конце каждой единицы работы).
Некоторые факторы, которые влияют на вашу пропускную способность:
- Количество ядер процессора
- Количество каналов диска (шпинделей, RAID-устройств и т. Д.)
- Алгоритм обработки и проблема, связанная с ЦП или с вводом / выводом
- Конфликт за основную статистическую структуру
В прошлом году я написал приложение, которое по сути делает то же, что вы описываете. В итоге я использовал Python и библиотеку pprocess
. Он использовал многопроцессную модель с пулом рабочих процессов, взаимодействующих через каналы (а не потоки). Главный процесс будет читать рабочую очередь, разбивать входные данные на куски и отправлять рабочим информацию о чанках. Рабочий собирал данные, собирал статистику и по окончании отправлял результаты мастеру. Мастер будет объединять результаты с глобальными итогами и отправлять другой кусок работнику. Я обнаружил, что он масштабируется почти линейно до 8 рабочих потоков (на 8-ядерном корпусе, что довольно неплохо), и помимо этого он ухудшается.
Некоторые вещи, которые следует учитывать:
- Используйте пул потоков с рабочей очередью, где число потоков, вероятно, равно количеству ядер в вашей системе
- В качестве альтернативы, используйте многопроцессную настройку, которая обменивается данными по каналам
- Оцените, используя
mmap()
(или эквивалентный), для сопоставления памяти входных файлов, но только после того, как вы профилировали базовый случай
- Считать данные кратными размеру блока (например, 4 КБ) и разбить их на строки в памяти
- Встроенное детальное ведение журнала с самого начала, чтобы облегчить отладку
- Следите за конфликтами при обновлении основной статистики, хотя она, вероятно, будет затоплена из-за времени обработки и чтения данных
- Не делайте предположений - тестируйте и измеряйте
- Установите локальную среду разработки, максимально приближенную к системе развертывания
- Использовать базу данных (например, SQLite ) для данных о состоянии, результатов обработки и т. Д.
- База данных может отслеживать, какие файлы были обработаны, какие строки имели ошибки, предупреждения и т. Д.
- Предоставляйте вашему приложению доступ только для чтения к исходному каталогу и файлам и сохраняйте результаты в других местах
- Будьте осторожны, не пытайтесь обрабатывать файлы, открытые другим процессом (здесь есть несколько хитростей)
- Осторожно, вы не достигнете пределов ОС количества файлов в каталоге
- Профилируйте все, но не забудьте изменить только одну вещь за один раз и вести подробные записи. Оптимизация производительности составляет жесткий .
- Настройте сценарии, чтобы вы могли последовательно перезапускать тесты. Здесь помогает наличие БД, поскольку вы можете удалить записи, которые помечают файл как обработанный, и повторно выполнить те же данные.
Когда у вас есть значительное количество файлов в одном каталоге, как вы описываете, помимо возможного превышения ограничений файловой системы, время для статистики каталога и выяснения, какие файлы вы уже обработали, а какие вам еще нужно увеличить значительно. Например, рассмотрите возможность разбиения файлов на подкаталоги по дате.
Еще одно слово в профилировании производительности: будьте осторожны при экстраполяции производительности из небольших наборов тестовых данных в супер огромные наборы данных. Ты не можешь Я нашел трудный путь, которым вы можете достичь определенной точки, когда обычные предположения о ресурсах, которые мы делаем каждый день в программировании, просто больше не выполняются. Например, я обнаружил, что буфер операторов в MySQL составляет 16 МБ, когда мое приложение прошло через него! А занятость 8 ядер может занять много памяти, но вы можете легко потратить 2 ГБ ОЗУ, если не будете осторожны! В какой-то момент вам придется тестировать реальные данные в производственной системе, но вы можете сами выбрать безопасную тестовую среду для запуска, чтобы вы не взламывали производственные данные или файлы.
С этим обсуждением напрямую связана серия статей в блоге Тима Брея, названная "Wide Finder" . Проблема заключалась в том, чтобы просто проанализировать файлы журналов и сгенерировать простую статистику, но максимально быстрым способом в многоядерной системе. Многие люди предлагали решения на разных языках. Это определенно стоит прочитать.