Тест производительности JDK8 Stream, чтобы увидеть, насколько быстро

Java

Анализ эффективности потока данных JDK8 Stream

Поток - это новая ключевая абстракция в библиотеке классов Java SE 8, и она определена в Java.util.stream (есть несколько типов потоков в этом пакете: поток подставки для эталонного потока объекта.

Кроме того, существует ряд специализированных потоков, таких как IntStream, LongStream, DoubleStream и т. д.), поток, представленный в Java 8, в основном используется для замены операции некоторой коллекции,Каждый поток представляет собой последовательность значений, а потоки предоставляют ряд часто используемых операций агрегирования., вы можете легко выполнять различные операции над ним.

Библиотека коллекций также предоставляет удобные способы использования коллекций, массивов и других структур данных в потоке операций;

тип операции на потоке

图片

1 промежуточная операция

  • Когда данные в источнике данных конвейеризированы, все операции, выполняемые над данными в этом процессе, называются «промежуточными операциями»;

  • Промежуточные операции по-прежнему возвращают объект потока, поэтому несколько промежуточных операций можно объединить в цепочку, чтобы сформировать конвейер;

  • поток предоставляет множество типов промежуточных операций, таких как фильтрация, различение, сопоставление, сортировка и т. д.;

② Работа терминала

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

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

Особенности стрима

① Его можно пройти только один раз:

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

② Принят метод внутренней итерации:

Для обработки Коллекции обычно используютIteratorМетод обхода траверсера, которыйвнешняя итерация;

Для обработки Stream, пока метод обработки объявлен, процесс обработки завершается самим объектом потока, который является своего родавнутренняя итерация, для итеративной обработки больших объемов данных внутренняя итерация более эффективна, чем внешняя;

Преимущества потока перед коллекцией

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

  • функциональный стиль: операция над потоком даст результат, но источник данных потока не будет изменен;

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

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

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


Сравнение эффективности сравнение потока и итерации итерации

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

Вывод первый:

- Традиционный итератор (цикл for) имеет более высокую производительность итерации, чем поток (JDK8), особенно в случае небольшого объема данных;

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

В сценарии отображения, фильтрации, сортировки, редукции статистики и преобразования строки столбца случайных чисел List (числа от 10 до 10 000 000) я сделал статистику по эффективности использования потока и итератора.

Тестовая среда выглядит следующим образом:

Система: Ubuntu 16.04 Xenial

Процессор: Intel Core i7-8550U

Оперативная память: 16 ГБ

Версия JDK: 1.8.0_151

JVM: 64-разрядная виртуальная машина сервера HotSpot™ (сборка 25.151-b12, смешанный режим)

JVM Settings:

-Xms1024m

-Xmx6144m

-XX:MaxMetaspaceSize=512m

-XX:ReservedCodeCacheSize=1024m

-XX:+UseConcMarkSweepGC

-XX:SoftRefLRUPolicyMSPerMB=100

1. Тест обработки карт

После увеличения каждого элемента в последовательности случайных чисел (список) на 1, он снова собирается в новый список.Вместимость тестируемой последовательности случайных чисел составляет от 10 до 10000000, а среднее время получается путем запуска 10 раз;

`//stream`
`List<Integer> result = list.stream()`
`.mapToInt(x -> x)`
`.map(x -> ++x)`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`
`//iterator`
`List<Integer> result = new ArrayList<>();`
`for(Integer e : list){`
 `result.add(++e);`
`}`
`//parallel stream`
`List<Integer> result = list.parallelStream()`
`.mapToInt(x -> x)`
`.map(x -> ++x)`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`

图片

2. Тест обработки фильтра

Выньте элементы больше 200 в последовательности случайных чисел (список) и соберите их в новый список.Вместимость протестированной последовательности случайных чисел составляет от 10 до 10 000 000, а среднее время получается при 10-кратном запуске;

`//stream`
`List<Integer> result = list.stream()`
`.mapToInt(x -> x)`
`.filter(x -> x > 200)`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`
`//iterator`
`List<Integer> result = new ArrayList<>(list.size());`
`for(Integer e : list){`
 `if(e > 200){`
 `result.add(e);`
 `}`
`}`
`//parallel stream`
`List<Integer> result = list.parallelStream()`
`.mapToInt(x -> x)`
`.filter(x -> x > 200)`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`

图片

3. Тест на естественную сортировку

Естественно отсортировать случайную числовую последовательность (Список) и собрать ее в новый Список.Итератор использует Collections# sort API (реализованный с использованием алгоритма сортировки слиянием).Разрядность тестируемой случайной последовательности от 10 до 10000000, а среднее прогоняется 10 раз;

`//stream`
`List<Integer> result = list.stream()`
`.mapToInt(x->x)`
`.sorted()`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`
`//iterator`
`List<Integer> result = new ArrayList<>(list);`
`Collections.sort(result);`
`//parallel stream`
`List<Integer> result = list.parallelStream()`
`.mapToInt(x->x)`
`.sorted()`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`

图片

4. Тест статистики сокращения

Получите максимальное значение последовательности случайных чисел (список), емкость протестированной последовательности случайных чисел составляет от 10 до 10000000, и запустите 10 раз, чтобы получить среднее время;

`//stream`
`int max = list.stream()`
`.mapToInt(x -> x)`
`.max()`
`.getAsInt();`
`//iterator`
`int max = -1;`
`for(Integer e : list){`
 `if(e > max){`
 `max = e;`
 `}`
`}`
`//parallel stream`
`int max = list.parallelStream()`
`.mapToInt(x -> x)`
`.max()`
`.getAsInt();`

图片

5. Тест на конъединированные строки

Получить последовательность случайных чисел (список), каждый элемент которой разделен символом ",". Емкость тестируемой последовательности случайных чисел составляет от 10 до 10000000, а среднее время получается путем запуска 10 раз;

`//stream`
`String result = list.stream().map(String::valueOf).collect(Collectors.joining(","));`
`//iterator`
`StringBuilder builder = new StringBuilder();`
`for(Integer e : list){`
 `builder.append(e).append(",");`
`}`
`String result = builder.length() == 0 ? "" : builder.substring(0,builder.length() - 1);`
`//parallel stream`
`String result = list.stream().map(String::valueOf).collect(Collectors.joining(","));`

图片

6. Тест смешанной работы

Последовательность случайных чисел (список) обнуляется, дедуплицируется, сопоставляется, фильтруется и собирается в новый список Емкость тестируемой последовательности случайных чисел составляет от 10 до 10 000 000, а среднее время получается путем запуска 10 раз;

`//stream`
`List<Integer> result = list.stream()`
`.filter(Objects::nonNull)`
`.mapToInt(x -> x + 1)`
`.filter(x -> x > 200)`
`.distinct()`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`
`//iterator`
`HashSet<Integer> set  = new HashSet<>(list.size());`
`for(Integer e : list){`
 `if(e != null && e > 200){`
 `set.add(e + 1);`
 `}`
`}`
`List<Integer> result = new ArrayList<>(set);`
`//parallel stream`
`List<Integer> result = list.parallelStream()`
`.filter(Objects::nonNull)`
`.mapToInt(x -> x + 1)`
`.filter(x -> x > 200)`
`.distinct()`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`

图片

Сводка экспериментальных результатов

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

  • В сценарии обработки небольшого и небольшого объема данных (размер

  • При большом объеме данных (размер > 10000) эффективность обработки потока будет выше, чем у Iterator, особенно при использовании параллельного потока, просто назначенного множеству основных потоков процессора (конечно, базовый параллельный поток, использующий JVM ForkJoinPool, эта вещь сама по себе назначается метафизикой потоков), может достигать очень высокой эффективности работы, но, как правило, не имеет фактического обычного бизнеса, требует итеративного расчета более 10000 раз;

  • На Parallel Stream сильно влияет среда ЦП.Когда несколько ядер ЦП не выделены, плюс накладные расходы на обращение к forkJoinPool, эффективность работы может быть не такой хорошей, как у обычного Stream;

Рекомендации по использованию потока

  • Для простой итерационной логики вы можете использовать итератор напрямую.Для итерационной логики с многоэтапной обработкой вы можете использовать поток,Стоит немного пожертвовать эффективностью в обмен на высокую читабельность кода.;

  • Среда с одноядерным процессором, параллельный поток не рекомендуется, при условии многоядерного процессора и большого объема данных рекомендуется использовать параллельный поток;

  • В потоке есть коробочные типы, перед выполнением промежуточных операций лучшеПреобразование в соответствующий числовой поток, снизить потери производительности, вызванные частой распаковкой и упаковкой;

Обратите внимание на общедоступный номер: ИТ-брат, получите следующую информацию

面试题1.png

面试题2.png

实战项目1.png

实战项目2.png