Подтвердить что ты не робот

Сколько потоков требуется, чтобы сделать их плохим выбором?

Мне нужно написать не очень большую программу на С++, используя boost:: thread.

Проблема заключается в обработке большого (может быть, тысяч или десятков тысяч), а также количества (возможно) больших файлов. Каждый файл независим от другого, и все они находятся в одном каталоге. Я думаю об использовании многопоточного aproach, но вопрос в том, сколько потоков я должен использовать? Я имею в виду, какой порядок величины? 10, 500, 12400?

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

Я думал о

for(each file f in directory){

    if (N < max_threads)//N is a static variable controlling amount of threads
         thread_process(f)
    else
       sleep()
}

Это в HP - UX, но я не буду тестировать его часто, поскольку это удаленный и совершенно недоступный сервер.

4b9b3361

Ответ 1

В соответствии с законом Амдаля, который обсуждался Хербом Саттером в его статье:

Некоторая часть обработки программы полностью "O (N)" параллелизуема (вызовите эту часть p), и только эта часть может масштабироваться непосредственно на машинах, имеющих все больше процессорных ядер. Остальная часть работы программы - "O (1)" последовательная (s). [1,2] Предполагая безупречное использование всех доступных ядер и отсутствие накладных расходов на параллелизацию, Amdahl Law говорит, что наилучшее возможное ускорение этой загрузки программы на машине с N ядрами дается с помощью formula image

В вашем случае операции ввода-вывода могут занимать большую часть времени, а также проблемы синхронизации. Вы можете рассчитывать время, затрачиваемое на блокирование (?) Медленных операций ввода-вывода и приблизительно поиск количества потоков, которые будут подходящими для вашей задачи.


Полный список concurrency связанных статей Херба Саттера можно найти здесь.

Ответ 2

Я не слишком уверен в HP/UX, но в мире Windows мы используем пулы потоков для решения этой проблемы. Raymond Chen написал об этом некоторое время назад, фактически...

Тощий из этого заключается в том, что я, как правило, не ожидал, что что-то будет хорошо масштабироваться на нагрузке, связанной с процессором, если количество потоков превышает примерно 2x количества ядер процессора, которые у вас есть в системе. Для нагрузок, связанных с вводом-выводом, вы можете уйти с большим количеством, в зависимости от того, насколько быстро ваша дисковая подсистема, но как только вы достигнете около 100 или около того, я бы серьезно подумал об изменении модели...

Ответ 3

Разработать это действительно зависит от

IO boundedness of the problem
    how big are the files
    how contiguous are the files
    in what order must they be processed
    can you determine the disk placement
how much concurrency you can get in the "global structure insert"
    can you "silo" the data structure with a consolidation wrapper
the actual CPU cost of the "global structure insert" 

Например, если ваши файлы находятся на трехпалубном массиве флэш-памяти, тогда решение отличается от того, что они находятся на одном диске (где, если "вставка глобальной структуры" занимает меньше, чем прочитанная проблема, это I/O ограничен, и вы можете иметь двухступенчатую трубку с двумя потоками - этап чтения, подающий этап вставки).

Но в обоих случаях архитектура, вероятно, будет вертикальным конвейером из двух этапов. n чтение нитей и m запись потоков с n и m определяется "естественным concurrency" для сцены.

Создание потока на файл, вероятно, приведет к обрыву диска. Точно так же, как вы адаптируете количество потоков процесса, связанного с процессором, к естественно достижимому CPU concurrency (и выше, что создает переключение ADA с переходом на контекст) то же самое верно на стороне ввода-вывода - в некотором смысле вы можете думать диска, как "переключение контекста на диск".

Ответ 4

Вы сказали, что все файлы находятся в одном каталоге. Означает ли это, что все они находятся на одном физическом диске?

Если это так и предполагается, что они еще не кэшированы, ваша работа будет заключаться в том, чтобы одна занятая голова чтения занята, и никакое количество потоков не поможет. Фактически, если он должен прыгать между дорожками из-за parallelism, вы можете замедлить его.

С другой стороны, если часть вычисления занимает значительное время, в результате чего голова чтения должна ждать, тогда может иметь смысл иметь > 1 поток.

Часто, используя потоки для производительности, отсутствует точка, если только она не позволяет одновременно использовать параллельные части оборудования.

Чаще всего значение потоков присутствует, например, отслеживание нескольких одновременных разговоров, например, если у вас несколько пользователей, где каждый поток может ждать своего собственного Джона или Сьюзи, а не путаться.

Ответ 5

Если рабочая нагрузка находится где-то рядом с I/O, как это звучит, тогда вы, вероятно, получите максимальную пропускную способность столько потоков, сколько у вас есть шпиндели. Если у вас более одного диска, и все данные находятся на одном и том же RAID 0, вы, вероятно, не хотите больше одного потока, Если более чем один поток пытается получить доступ к нескольким частям диска, ОС должна прекратить чтение одного файла, даже если он может быть прямо под головой, и переместиться на другую часть диска для обслуживания другого потока, так что он не голодает. При использовании только одного потока диск никогда не должен прекращать чтение для перемещения головы.

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

Как показывают другие плакаты, сначала профиль!

Ответ 6

Не звучать банально, но вы используете столько потоков, сколько вам нужно.

В принципе вы можете нарисовать график количества потоков против (реального) времени до завершения. Вы также можете нарисовать то, что является общим потоком, для общего времени потока.

Первый график, в частности, поможет вам определить, где находится узкое место в мощности процессора. В какой-то момент вы станете либо I/O bound (что означает, что диск не может загрузить данные достаточно быстро), либо количество потоков будет становится настолько большим, что это скажется на производительности машины.

Второе происходит. Я видел один фрагмент кода, который создал 30 000+ потоков. Это закончилось тем, что было быстрее, закрыв его до 1000.

Другой способ взглянуть на это: как быстро это достаточно быстро? Точка, в которой ввод-вывод становится узким местом, - это одно, но вы можете поразить точку до того, где она "достаточно быстро".

Ответ 7

Используйте пул потоков вместо создания потока для каждого файла. Вы можете легко настроить количество потоков после написания своего решения. Если задания не зависят друг от друга, я бы сказал, что количество потоков должно быть равно числу ядер/cpus.

Ответ 8

Ответ зависит в некоторой степени от того, как интенсивная загрузка процессора требует обработки для каждого файла.

В крайнем случае, когда время обработки доминирует над временем I/O, преимущество, которое дает вам поток, - это просто возможность воспользоваться преимуществами из нескольких ядер (и, возможно, гиперпотоков), чтобы использовать максимальную доступную вычислительную мощность вашего ЦП. В этом случае вы хотите нацелиться на ряд рабочих потоков, примерно равных количеству логических ядер в системе.

В другом крайнем случае, где I/O является вашим узким местом, вы не увидите много преимуществ от нескольких потоков, поскольку они потратят большую часть своего времени на сну, ожидая завершения ввода-вывода. В этом случае вы хотели бы сосредоточиться на максимизации пропускной способности ввода-вывода, а не на использовании вашего ЦП. На одном бездокументированном жестком диске или DVD-диске, где вы были связаны с вводом-выводом, имеющим несколько потоков, может повредить производительность, так как вы получите максимальную пропускную способность ввода-вывода от последовательных чтений в одном потоке. Если диск фрагментирован или у вас есть массив RAID или аналогичный, то одновременное использование нескольких запросов ввода-вывода в полете может повысить пропускную способность ввода-вывода, поскольку контроллер может разумно переупорядочить их для повышения эффективности чтения.

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

Ответ 9

Это может быть слишком устаревшее школьное звучание, но вы считаете, что это просто процессы разветвления? Похоже, у вас очень независимые рабочие единицы с небольшой совокупностью возвращаемых данных. Модель процесса также освободит виртуальное адресное пространство (которое может быть жестким, если вы на 32-битной машине), позволяя каждой рабочей комнате сказать mmap() весь обрабатываемый файл.

Ответ 10

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

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

Ответ 11

Я согласен со всеми, предлагающими пул потоков: вы планируете задачи с пулом, а пул назначает потоки для выполнения задач.

Если вы привязаны к процессору, просто продолжайте добавлять потоки, пока использование ЦП ниже 100%. Когда вы I/O связаны, переполнение диска может в какой-то момент предотвратить большее количество потоков от повышения скорости. Это вам нужно будет узнать сами.

Вы видели Intel Threading Building Blocks? Обратите внимание, что я не могу прокомментировать, является ли это то, что вам нужно. Я только сделал небольшой игрушечный проект в Windows, и это было несколько лет назад. (Он был несколько похож на ваш, BTW: он рекурсивно пересекает иерархию папок и подсчитывает строки в найденных файлах исходного кода.)

Ответ 12

Больше потоков не обязательно даст вам более высокую пропускную способность. Потоки имеют нетривиальную стоимость, как для создания (с точки зрения времени процессора и ресурсов ОС), так и для запуска (с точки зрения памяти и планирования). И чем больше у вас потоков, тем больше возможностей для соперничества с другими потоками. Добавление потоков иногда может даже замедлить выполнение. Каждая проблема тонко отличается, и вам лучше писать хорошее, гибкое решение и экспериментировать с параметрами, чтобы увидеть, что лучше всего работает.

Ваш примерный код, создающий поток для каждого файла, почти сразу запустит систему для значений max_threads около 10. Как и другие, предложенный пул потоков с рабочей очередью - это то, что вы, вероятно, захотите. Тот факт, что каждый файл является независимым, хорош, поскольку это делает его почти неловко параллельным (за исключением агрегации в конце каждой единицы работы).

Некоторые факторы, которые повлияют на вашу пропускную способность:

  • Число ядер процессора
  • Количество дисковых каналов (шпиндели, устройства RAID и т.д.)
  • Алгоритм обработки и проблема связана с тем, что проблема связана с ЦП или I/O
  • Конфликт для структуры главной статистики

В прошлом году я написал приложение, которое по сути то же, что и вы описываете. Я закончил использование Python и pprocess library. Он использовал многопроцессорную модель с пулом рабочих процессов, обмениваясь по каналам (а не потокам). Мастер-процесс должен прочитать рабочую очередь, отрубить входные данные и отправить информацию о куске работникам. Работник будет хруст данными, собирать статистику, и когда это будет сделано, отправьте результаты, чтобы вернуть мастеру. Мастер объединил бы результаты с глобальными итогами и отправил бы еще один кусок работнику. Я обнаружил, что он масштабируется почти линейно до 8 рабочих потоков (на 8-ядерном ящике, что довольно хорошо), и помимо этого он ухудшился.

Некоторые вещи, которые следует учитывать:

  • Используйте пул потоков с рабочей очередью, где количество потоков, вероятно, будет вокруг количества ядер в вашей системе.
  • В качестве альтернативы используйте многопроцессорную настройку, которая связывается через каналы
  • Оценить с помощью mmap() (или эквивалента) карту памяти входных файлов, но только после того, как вы профилировали базовый случай
  • Прочитайте данные в кратных размерах блока (например, 4kB) и отрубите строки в памяти
  • Создайте подробное ведение журнала с самого начала, чтобы помочь отлаживать
  • Следите за конкуренцией при обновлении основной статистики, хотя она, вероятно, будет зависеть от времени обработки и чтения данных.
  • Не делайте предположений - проверяйте и измерьте
  • Настройте локальную среду разработчиков, максимально приближенную к системе развертывания.
  • Используйте базу данных (например, SQLite) для данных состояния, результатов обработки и т.д.
  • База данных может отслеживать, какие файлы были обработаны, в каких строках были ошибки, предупреждения и т.д.
  • Предоставляйте доступ только для чтения к исходному каталогу и файлам и записывайте результаты в другом месте
  • Будьте осторожны, чтобы не пытаться обрабатывать файлы, открытые другим процессом (здесь есть несколько трюков)
  • Осторожно, что вы не нажимаете ограничения ОС на количество файлов в каталоге
  • Профилируйте все, но не забудьте изменить только одно за раз и сохранить подробные записи. Оптимизация производительности сложна.
  • Настройте сценарии, чтобы вы могли последовательно повторять тесты. Здесь помогает DB, поскольку вы можете удалить записи, которые помещают файл как обработанный и повторно запускаемый с теми же данными.

Когда у вас есть значительное количество файлов в одном каталоге, о котором вы описываете, помимо потенциального ограничения файловой системы, время статирования каталога и выяснение, какие файлы вы уже обработали, и которые все еще нужно поднимать вверх значительно. Например, распакуйте файлы в подкаталоги по дате.

Еще одно слово о профилировании производительности: будьте осторожны при экстраполяции производительности с небольших наборов данных теста на супер-огромные наборы данных. Вы не можете. Я обнаружил, что вы можете достичь определенного момента, когда регулярные предположения о ресурсах, которые мы делаем каждый день в программировании, просто не выдерживают. Например, я только узнал, что буфер буфера в MySQL равен 16 МБ, когда мое приложение пошло по нему! И сохранение 8 ядер занятых может занять много памяти, но вы можете легко пережевывать 2 ГБ оперативной памяти, если не будете осторожны! В какой-то момент вам нужно протестировать реальные данные в производственной системе, но дать вам безопасную тестовую тестовую среду, чтобы вы не запускали производственные данные или файлы.

Непосредственно связанным с этим обсуждением является серия статей в блоге Тима Брея, называемая "Широкий поиск" . Проблема заключалась в простом анализе лог файлов и генерации простых статистических данных, но наиболее быстрым способом для многоядерной системы. Многие люди внесли свои решения на разных языках. Это определенно стоит прочитать.

Ответ 13

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

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

В пределе вам может понадобиться использовать механизм совместной работы без операционной системы или даже мультиплексные события самостоятельно, используя крошечные объекты контекста выполнения.

Просто начните с потоков и об этом позаботите позже:)

Ответ 14

В качестве номера шарика вы должны, вероятно, сохранить количество потоков от 10 до 100, чтобы минимизировать конфликт блокировок и перераспределение контекста.

Ответ 15

Здесь есть две проблемы: во-первых, ваш вопрос об идеальном количестве threads для использования для обработки этого большого количества файлов, во-вторых, как добиться максимальной производительности.

Давайте начнем со второй проблемы, чтобы начать с того, что я не буду распараллеливать каждый файл, но я бы распараллеливал обработку, выполняемую в одном файле за раз. Это поможет значительно в нескольких частях вашей среды:  - Жесткий диск, поскольку он не должен искать один файл для n - 1 других  - Кэш файловой системы операционной системы будет теплым с данными, которые вам понадобятся во всех ваших потоках, и вы не будете испытывать столько разборок кеша.

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

Из этого ответа на ваш вопрос легко, вы должны соответствовать не более одного потока на ядро, присутствующего в вашей системе. Это позволит вам проявлять уважение к вашим кэшам и в конечном итоге достичь наилучшей производительности в вашей системе.

Конечным моментом, конечно, является то, что с использованием такого типа обработки ваше приложение будет более уважительно относиться к вашей системе, так как доступ к n файлам одновременно может сделать вашу ОС неактуальной.