Производительность Java Parallel Stream против ExecutorService - PullRequest
0 голосов
/ 12 сентября 2018

Предположим, у нас есть список и мы хотим выбрать все элементы, удовлетворяющие свойству (скажем, некоторые функции f). Существует 3 способа параллельного процесса.

Один:

listA.parallelStream.filter(element -> f(element))..collect(Collectors.toList());

Два:

listA.parallelStream.collect(Collectors.partitioningBy(element -> f(element))).get(true);

Три:

ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
//separate the listA into several batches
for each batch {
     Future<List<T>> result = executorService.submit(() -> {
          // test the elements in this batch and return the validate element list
     });
}
//merge the results from different threads.

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

Ответы [ 2 ]

0 голосов
/ 12 сентября 2018

Когда вы используете .filter(element -> f(element)).collect(Collectors.toList()), он собирает соответствующие элементы в List, тогда как .collect(Collectors.partitioningBy(element -> f(element))) собирает все элементы в любой из двух списков, после чего вы отбрасываете один из них иполучение только списка совпадений с помощью .get(true).

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

Третий вариант несопоставим, так каквы не показали реальную реализацию, а просто набросок.Нет смысла сравнивать гипотетическую реализацию с реальной.Логика, которую вы описываете, такая же, как логика реализации параллельного потока.Итак, вы просто изобретаете велосипед.Может случиться так, что вы делаете что-то немного лучше, чем эталонная реализация, или просто лучше адаптируетесь к конкретной задаче, но вероятность того, что вы пропустите вещи, которые разработчики Stream API уже рассматривали в процессе разработки, который длился несколько лет, гораздо выше.

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

Таким образом, первый вариант является наиболее эффективным, особенно в том, что он есть.также самый простой, самый читаемый, прямо выражающий ваше намерение.

0 голосов
/ 12 сентября 2018

Один и два используют ForkJoinPool, который предназначен именно для параллельной обработки одной задачи, а ThreadPoolExecutor используется для параллельной обработки независимых задач.Так что Один и Два должны быть быстрее.

...