#48 스트림 병렬화는 주의해서 적용하라
스트림은 parallel 메서드만 호출하면 자동으로 병렬 실행할 수 있는 스트림을 지원하고 있습니다.
사용하기는 쉽지만 주의할 점들이 있습니다.
위 코드는 메르센 소수를 구하는 코드를 스트림 병렬화로 처리하고 있습니다.
메르센 소수 : 2의 거듭제곱에서 1이 모자란 숫자를 가리킨다
병렬로 처리하기 때문에 성능이 좋아 보이지만, 실제로는 스트림 라이브러리가 이 파이프라인을 병렬화하는 방법을 찾아내지 못해서 아무것도 출력하지 못하고 CPU는 90%나 차지하는 상태가 무한히 계속됩니다.
이유는 아래와 같이 데이터 소스가 Stream.iterate이고, 중간 연산으로 limit를 사용하기 때문입니다.
스트림 병렬화로 성능 개선을 기대할 수 없는 경우
데이터 소스가 Stream.iterate(데이터를 원하는 크기로 정확하게 나눌 수 없어서 그런 거 같네요?)
중간 연산으로 limit 사용 (CPU 코어가 남는다면 원소 몇 개 더 처리 후, 제한된 개수 이후의 결과를 버림)
가변 축소(mutable reduction)를 수행하는 Stream#collect 메서드 (Collection을 합치는 부담이 큼)
스트림 병렬화로 성능 개선을 기대할 수 있는 경우
ArrayList, HashMap, HashSet, ConcurrentHashMap instance (데이터를 원하는 크기로 정확하고 손쉽게 나눌 수 있어서 다수의 스레드에서 분배하기 좋은 자료구조)
배열, int 범위, long 범위 (원소들을 순차적으로 실행할 때 참조 지역성(locality of reference)이 뛰어난 자료 구조)
min, max, count, sum과 같은 reduce 메서드(파이프라인에서 만들어진 모든 원소를 하나로 합치는 축소(reduction) 작업)
위 코드는 long 범위(LongStream.rangeClosed)와 reduce 메서드(count)를 이용한 소수 계산 스트림입니다.
이를 parallel을 이용해 병렬로 처리하면, 실제로 더 나은 성능을 볼 수 있습니다.
스트림 병렬 화도 일반 병렬 처리처럼 성능과 오작동에 신경 써서 이용하라는 내용입니다.
Effective Java 3판도 다 봤습니다.
2판 비해 아무래도 Stream에 대한 내용 추가가 대부분입니다.
Android 개발자 입장에서는 Java Stream이 아직 낯설게 다가오지만, Rx나 Kotlin에서도 비슷한? 개념과 문법이 있기에 익혀두면 좋을 거 같습니다.