Требуется 38 минут только для того, чтобы заполнить HashMap
- необычное долгое время. Предполагается, что либо Person::getKey
выполняет дорогостоящее построение, либо в результате получается объект с менее чем оптимальной hashCode
или equals
реализацией.
На моем компьютере заполнение карты десятью миллионами элементов при разумной реализации hashCode
или equals
требуется меньше секунды, сотням миллионам все еще требуется всего несколько секунд, а затем потребление памяти становится проблемой.
Тем не менее, хуже производительность параллельной поток не приходит неожиданно. Как обсуждалось в « Следует ли мне всегда использовать параллельный поток, когда это возможно? », параллельная обработка имеет некоторые фиксированные издержки, и вам требуется некоторая значительная (на элемент) рабочая нагрузка, чтобы получить преимущество, превышающее накладные расходы.
В вашем конкретном примере c нет никакой выгоды.
Параллельная операция collect
работает путем разбиения элементов потока на куски, которые будут обрабатываться различными рабочими потоками. Каждый из них создаст новый локальный контейнер, в случае toMap
карта того же типа, что и конечный результат, затем каждый поток будет накапливать элементы в своем локальном контейнере, то есть помещать значения в карту, а когда два рабочих потоки закончили свою работу, частичные результаты будут объединены, что означает помещение всех элементов одной карты в другую.
Поскольку у вас нет операции фильтрации, а отсутствие функции слияния означает, что все ключи уникальны , легко сделать вывод, что в лучшем случае у вас есть два рабочих потока, заполняющих две карты одного и того же размера идеально параллельно, после чего одна из этих карт помещается в другую, что занимает столько времени, сколько было сохранено предыдущей параллельной обработкой.
Ваш пример также не включает потенциально дорогостоящие промежуточные операции, поэтому, только если Person::getKey
стоит дорого, его стоимость может быть уменьшена параллельной обработкой.
Как обсуждено в , этот ответ , используя toConcurrentMap
instea d из toMap
может улучшить такой сценарий, поскольку он позволяет пропустить операцию объединения, а наличие полностью уникальных ключей подразумевает очень низкую конкуренцию, когда все рабочие потоки помещаются в одну карту.
Однако, стоит изучить фактическая причина проблемы с производительностью. Когда проблема заключается в реализации ключевого объекта hashCode
или equals
, ее исправление принесет гораздо больше пользы. Кроме того, параллелизм не может решить проблемы, связанные с почти полной кучей.
Наконец, toConcurrentMap
возвращает параллельную карту, что может привести к более высоким затратам на последующую обработку, даже если вы не собираетесь использовать это карта с несколькими потоками.