Stream API был разработан, чтобы упростить написание вычислений таким способом, который был абстрагирован от способа их выполнения, что облегчало переключение между последовательным и параллельным.
Однако, просто потому, что это легко, это не значит, что это всегда хорошая идея, и на самом деле, это плохая идея - просто бросить .parallel()
повсюду просто потому, что вы можете.
Во-первых, обратите внимание, что параллелизм не дает никаких преимуществ, кроме возможности более быстрого выполнения, когда доступно больше ядер. Параллельное выполнение всегда будет включать больше работы, чем последовательное, потому что в дополнение к решению проблемы, оно также должно выполнять диспетчеризацию и координацию подзадач. Надежда состоит в том, что вы сможете быстрее найти ответ, разбив работу по нескольким процессорам; произойдет ли это на самом деле, зависит от многих вещей, включая размер набора данных, объем вычислений, которые вы выполняете для каждого элемента, характер вычислений (в частности, взаимодействует ли обработка одного элемента с обработкой других?) количество доступных процессоров и число других задач, конкурирующих за эти процессоры.
Кроме того, обратите внимание, что параллелизм также часто выявляет недетерминизм в вычислениях, который часто скрыт последовательными реализациями; иногда это не имеет значения или может быть смягчено путем ограничения задействованных операций (т. е. операторы сокращения должны быть без сохранения состояния и ассоциативными).
В действительности иногда параллелизм ускоряет ваши вычисления, иногда нет, а иногда даже замедляет их. Лучше всего сначала разработать с использованием последовательного выполнения, а затем применить параллелизм, где
(A) вы знаете, что на самом деле есть преимущества для повышения производительности и
(B) что он на самом деле доставит повышенную производительность.
(А) это бизнес-проблема, а не техническая. Если вы эксперт по производительности, вы обычно сможете посмотреть на код и определить (B), но разумный путь - это измерить. (И даже не беспокойтесь, пока не убедитесь в (A); если код достаточно быстрый, лучше применить свои мозговые циклы в другом месте.)
Простейшей моделью производительности для параллелизма является модель «NQ», где N - количество элементов, а Q - вычисление на элемент. Как правило, продукт NQ должен превышать пороговое значение, прежде чем вы начнете получать выигрыш в производительности. Для задачи с низким Q, такой как «сложение чисел от 1 до N», вы обычно видите безубыточность между N = 1000 и N = 10000. При проблемах с более высоким Q вы увидите безубыточности при более низких порогах.
Но реальность довольно сложная. Поэтому, пока вы не достигнете мастерства, сначала определите, когда последовательная обработка вам действительно чего-то стоит, а затем измерьте, поможет ли параллелизм.