public class ParallelMersennePrimes {
public static void main(String[] args) {
primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE))
.parallel() // 스트림 병렬화
.filter(mersenne -> mersenne.isProbablePrime(50))
.limit(20)
.forEach(System.out::println);
}
static Stream<BigInteger> primes() {
return Stream.iterate(TWO, BigInteger::nextProbablePrime);
}
}
처음 20개의 메르센 소수를 생성하는 프로그램
성능을 향상시키기 위해 parallel() 를 사용하면, 아무것도 출력하지 못하면서 CPU는 90% 나 잡아먹는 상태가 무한히 계속되는 문제가 발생함
→ 스트림 라이브러리가 이 파이프라인을 병렬화하는 방법을 찾아내지 못했기 때문
(iterate는 순차적으로 생성되는 스트림을 반환하는데, 병렬처리를 위해서는 독립적으로 나뉘어질 수 있어야 하기 때문)
데이터 소스가 Stream.iterate이거나 중간 연산으로 limit을 쓰면 파이프라인 병렬화로는 성능 개선을 기대할 수 없음 (위 코드는 두 문제를 모두 가짐)
파이프라인 병렬화는 limit를 다룰 때 CPU코어가 남는다면 원소를 몇 개 더 처리한 후 제한된 개수 이후의 결과를 버려도 아무런 해가 없다고 가정함
→ limit을 충족하기 위해 병렬로 처리된 원소 중 많은 수는 버려짐 (많은 시간을 들여 계산했는데,,)
Stream.iterate
→ 순차적으로 다음 요소를 반환하기 때문에 원하는 단위로 자를 수 없음
limit
→ 위에서 언급.
→ 소수 찾기 처럼 점점 수행 시간이 길어지는 케이스의 경우 문제 발생
가변 축소(컬렉션의 축소가 수행되면서 그 컬렉션이 가변 상태로 변경되는 방식)를 수행하는 Stream의 collect
→ 컬렉션들을 합치는 부담이 큼