Должен ли я всегда использовать параллельный поток, когда это возможно?


С Java 8 и lambdas легко перебирать коллекции в виде потоков и так же легко использовать параллельный поток. Два примера из документы, второй с помощью parallelStream:

myShapesCollection.stream()
    .filter(e -> e.getColor() == Color.RED)
    .forEach(e -> System.out.println(e.getName()));

myShapesCollection.parallelStream() // <-- This one uses parallel
    .filter(e -> e.getColor() == Color.RED)
    .forEach(e -> System.out.println(e.getName()));

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

есть ли другие соображения? Когда следует использовать параллельный поток и когда следует непараллельно использовать?

(этот вопрос задан, чтобы вызвать дискуссию о том, как и когда использовать параллельные потоки, а не потому, что я думаю, что всегда использовать их-хорошая идея.)

4 335

4 ответа:

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

  • У меня есть огромное количество элементов для обработки (или обработка каждого элемента занимает время и распараллеливается)

  • У меня проблема с производительностью в первую очередь

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

в вашем примере производительность в любом случае будет зависеть от синхронизированного доступа к System.out.println(), и параллельность этого процесса не будет иметь никакого эффекта или даже отрицательного.

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

в любом случае мера, не думаю! Только измерение скажет вам, стоит ли параллелизм или нет.

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

Я смотрел один из презентации на Брайан Гетц(Java Language Architect & specification lead for Lambda Expressions). Он подробно объясняет следующие 4 пункта, которые следует рассмотреть, прежде чем идти на распараллеливание:

затраты на расщепление / декомпозицию
- Иногда разделение стоит дороже, чем просто выполнение работы!
затраты на диспетчеризацию / управление задачами
- Может сделать много работы за то время, которое требуется, чтобы передать работу другому потоку.
стоимость комбинации результатов
– Иногда комбинация включает в себя копирование большого количества данных. Например, добавление чисел дешево, тогда как слияние наборов дорого.
населенного пункта
– Слон в комнате. Это важный момент, который каждый может пропустить. Вы должны учитывать промахи кэша, если процессор ждет данных из-за промахов кэша, то вы ничего не получите от распараллеливания. Вот почему источники на основе массивов лучше всего распараллеливаются, поскольку следующие индексы (рядом с текущим индексом) кэшируются, и есть меньше шансов, что CPU будет испытывать промах кэша.

Он также упоминает относительно простую формулу для определения вероятности параллельного ускорения.

модель NQ:

N x Q > 10000

здесь
N = количество элементов данных
Q = объем работы на единицу

JB попал в точку на голове. Единственное, что я могу добавить, это то, что Java8 не делает чистой параллельной обработки, он делает paraquential Да, я написал статью, и я делал F/J в течение тридцати лет, поэтому я понимаю эту проблему.