Search
🙈

이펙티브 자바:: 아이템 48 <스트림 병렬화는 주의해서 적용하라>

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::

이펙티브 자바 / 조슈아 블로크 지음 (프로그래밍 인사이트)