2010-04-10 4 views
7

Я работаю над учебным пособием для своего курса параллелизма Java. Цель состоит в том, чтобы использовать пулы потоков для параллельного вычисления простых чисел.Рекурсивное добавление потоков в пул потоков Java

Дизайн выполнен на основе сита Эратосфена. Он имеет массив n bools, где n - наибольшее целое число, которое вы проверяете, и каждый элемент в массиве представляет одно целое число. True - это простое, false - это не простое, а массив изначально все верно.

Пул потоков используется с фиксированным числом потоков (мы должны экспериментировать с количеством потоков в пуле и наблюдать за производительностью).

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

После того, как сформирован новый поток, существующий поток затем по-прежнему устанавливает для всех кратных его целых чисел в массиве значение false.

Основная нить программы запускает первый поток с целым числом «2», а затем ждет завершения всех порожденных потоков. Затем он выплескивает простые числа и время, затрачиваемое на вычисление.

Проблема у меня в том, что чем больше потоков есть в пуле потоков, тем медленнее это происходит, когда 1 поток является самым быстрым. Это должно ускориться быстрее!

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

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

+3

Продолжайте использовать подход Thread, это опыт обучения, и вы поймете многое о потоках, когда вы закончите. Кто заботится о Сите Эратосфена? Многие профессиональные программисты никогда не понимают знания на этой странице. Просто помните, что если женщина может иметь ребенка через 9 месяцев, это не означает, что девять могут сделать это один месяц! –

ответ

5

Ваше решение может работать медленнее, как потоки добавляются некоторые из следующих проблем:

  • накладные расходы на создание потоков: создание потока дорого.

  • Конкуренция процессора: если есть больше потоков, чем есть процессоры для их выполнения, некоторые потоки будут приостановлены в ожидании свободного процессора. В результате средняя скорость обработки для каждого потока падает. Кроме того, ОС затем нужно время срезать потоки, и это забирает время, которое в противном случае было бы использовано для «реальной» работы.

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

  • Конфликт кэш: каждая нить будет (предположительно) сканировать другой раздел массив, в результате чего промахи кэша памяти. Это замедляет доступ к памяти.

  • Заблокировать конфликт: если ваши потоки все читают и обновляют общий массив и используют synchronized и один объект блокировки для управления доступом к массиву, вы можете столкнуться с конфликтом блокировки. Если используется один объект блокировки, каждый поток будет тратить большую часть своего времени на ожидание блокировки. Конечным результатом является то, что вычисление эффективно сериализуется, и общая скорость обработки падает до скорости одного процессора/потока.

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

  • Перекодируйте приложение так, чтобы каждый поток просматривал несколько целых чисел, но в своем собственном разделе массива. Это устранит конфликты блокировок на массивах, хотя вам понадобится способ рассказать каждой теме о том, что делать, и это нужно разрабатывать с учетом мнения.
  • Создайте массив блокировок для разных областей массива и выделите потоки блокировки для использования на основе области массива, на котором они работают. Вы все равно будете спорить, но в среднем вы должны получить меньше голосов.
  • Проектирование и внедрение бесконтактного решения. Это повлечет за собой ГЛУБОКОЕ ПОНИМАНИЕ модели памяти Java. И было бы очень сложно доказать/продемонстрировать, что бесконтактное решение не содержит тонких недостатков параллелизма.

Наконец, рекурсивный создание потоков, вероятно, является ошибкой, так как он будет делать это труднее осуществить повторное использование потоков и меры по борьбе с блокировкой разногласий.

+0

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

+0

@Peter - я бы сказал, что точка упражнения заключается в том, что вы должны использовать потоки * правильно *, чтобы увеличить производительность приложения при добавлении процессоров. –

+0

Мне действительно нравится давать один голос на этот пост, недостаточно, такое замечательное резюме для возможных недостатков многопоточности. – posdef

3

Сколько процессоров доступно в вашей системе? Если #threads> #processors, добавление большего количества потоков будет замедлять работу задачи, связанной с вычислением.

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

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

+0

Мой текущий компьютер имеет два ядра, но код был первоначально протестирован на Intel Core i7 с 4 ядрами (8 виртуальных), и у него были аналогичные проблемы. т.е. 1 нить = 1 с. 4 потока = 20 с. – ljbade

0

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

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

нить # 1: Набор 2 в кратном ложные

нить # 2: найти 3, набор 3 в кратном ложные

нить # 3: найти 5, набор 5 в кратном ложную

нити # 4: найти 7, установить 7 в кратных ложных

....

Эти потоки должны быть запущены в порядке, и они перемежения (как график времени выполнения т подол).

Например, если поток №3 запускается до того, как поток №1 устанавливает «4» в значение «ложь», он найдет «4» и продолжит сброс 4-кратных значений. В результате получается много дополнительной работы, хотя конечный результат будет правильным.

+0

Нить 5 никогда не будет «найти» 4 - единственная цель - удалить все кратные 5. После того, как все рабочие потоки закончат удаление простых чисел, основной поток «найдет» оставшиеся числа. – danben

+0

Если нить # 3 должна ждать тех потоков, которые начинаются до ее завершения, нам не нужно несколько потоков. Проблема заключается в том, что нить # 3 не должна ждать потока # 1, чтобы удалить * all * из 2-х кратных чисел, но ему нужно дождаться потока # 1, чтобы удалить «достаточный» 2-кратный символ до того, как он начнет поиск. – evergreen

0

Реструктурируйте свою программу, чтобы создать фиксированный ThreadPoolExecutor заблаговременно. Убедитесь, что вы вызываете ThreadPoolExecutor # prestartAllCoreThreads(). Попросите свой основной метод отправить задачу для целого числа. 2. Каждая задача представит другую задачу. Поскольку вы используете пул потоков, вы не будете создавать и завершать кучу потоков, но вместо этого позволяете тем же потокам принимать новые задачи по мере их появления. Это уменьшит общие накладные расходы.

Вы должны обнаружить, что в этом случае оптимальное количество потоков равно числу процессоров (P) на машине. Часто бывает, что оптимальное количество потоков составляет P + 1. Это связано с тем, что P + 1 минимизирует накладные расходы при переключении контекста, а также минимизирует потери от времени ожидания/блокировки.

Смежные вопросы