Intro::
이펙티브 자바 정리본입니다.
결론
•
계산도 올바로 수행하고 성능도 빨라질 거라는 확신 없이는 스트림 파이프라인 병렬화는 시도조차 하지 말라.
•
계산도 정확하고 성능도 좋아졌음이 확실해졌을 때, 오직 그럴 때만 병렬화 버전 코드를 운영 코드에 반영하라.
스트림을 사용해 처음 20개의 메스센 소수를 생성하는 프로그램
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);
}
}
Java
복사
해당 프로그램은 아무것도 출력하지 못하면서 CPU를 90%나 잡아먹는다고 합니다.
환경이 아무리 좋더라도, 데이터 소스가 Stream.iterate거나 중간 연산으로 limit을 쓰면 파이프라인 병렬화로는 성능 개선을 기대할 수 없습니다.
언제 사용하는 것이 좋은가?
•
대체로 스트림의 소스가 ArrayList, HashMap, HashSet, ConcurrentHashMap의 인스턴스거나 배열, int 범위, long 범위일 때 병렬화의 효과가 가장 좋습니다.
◦
데이터를 원하는 크기로 정확하고 손쉽게 나눌 수 있어서 일을 다수의 스레드에 분배하기 좋습니다.
◦
원소들을 순차적으로 실행할 때의 참조 지역성이 뛰어나 좋습니다.
◦
스트림 파이프라인의 종단 연산의 동작 방식 역시 병렬 수행 효율에 영향을 주는데, 종단 연산 중 병렬화에 가장 적합한 것은 축소입니다.
▪
Stream의 reduce 메서드 중 하나
▪
min, max, count, sum 같이 완성된 형태로 제공되는 메서드
◦
anyMatch, allMatch, noneMatch처럼 조건에 맞으면 반환하는 메서드도 병렬화에 적합합니다.
스트림을 잘못 병렬화하면 성능이 나빠질 뿐만아니라 결과 자체가 잘못되거나 예상 못한 동작이 발생할 수 있습니다.
좋은 예시
public class ParallelPrimeCounting {
static long pi(long n)
return LongStream.rangeClosed(2, n)
.parallel()
.mapToObj(BigInteger::valueOf)
.filter(i -> i.isProbablePrime(50))
.count();
}
public static void main(String[] args) {
System.out.println(pi(10_000_000));
}
}
Java
복사
References::
이펙티브 자바 / 조슈아 블로크 지음 (프로그래밍 인사이트)