아이템 48. 스트림 병렬화는 주의해서 적용하라

문법식·2022년 10월 11일
0

Effective Java 3/E

목록 보기
48/52

동시성 프로그래밍 측면에서 자바는 항상 앞서갔다. 자바가 버전을 업데이트할수록 자바로 동시성 프로그램으 작성하기가 점점 쉬어지고는 있지만, 이를 올바르고 빠르게 작성하는 일은 여전히 어려운 일이다. 동시성 프로그래밍을 할 때는 안전성과 응답 가능 상태를 유지해야 한다.

데이터 소스가 Stream.iterate거나 중간 연산으로 limit을 쓰면 파이프라인 병렬화로는 성능 개선을 기대할 수 없다. 또한 파이프라인 병렬화는 limit을 다룰 때 CPU 코어가 남는다면 원소를 몇 개 더 처리한 후 제한된 개수 이후의 결과는 버려도 아무 해가 없다고 가정한다. 핵심은 스트림 파이프라인을 마구잡이로 병렬화하면 안 된다는 것이다. 성능이 끔직하게 나빠질 수 있기 때문이다.

대체로 스트림의 소스가 ArrayList, HashMap, HashSet, ConcurrentHashMap의 인스턴스거나 배열, int 범위, long 범위일 때 병렬화의 효과가 가장 좋다. 이 자료구조들은 모두 데이터를 원하는 크기로 정확하고 손쉽게 나눌 수 있어서 다수의 스레드에 분배하기 좋다는 특징이 있다. 또한 이 자료구조들의 또 다른 중요한 공통점은 원소들을 순차적으로 실행할 때의 참조 지역성이 뛰어나다는 것이다. 참조 지역성이 가장 뛰어난 자료구조는 기본 타입의 배열이다. 기본 타입 배열에서는 참조가 아닌 데이터 자체가 메모리에 연속해서 저장되기 때문이다.

스트림 파이프라인의 종단 연산에서 수행하는 작업량이 파이프라인 전체 작업에서 상당 비중을 차지하면서 순차적인 연산이라면 파이프라인 병렬 수행의 효과는 제한될 수 밖에 없다. 종단 연산 중 병렬화에 가장 적합한 것은 축소다. 축소는 파이프라인에서 만들어진 모든 원소를 하나로 합치는 작업으로, Streamreduce 메서드 중 하나, 혹은 min, max, count, sum 같이 완성된 형태로 제공되는 메서드 중 하나를 선택해 수행한다. 반면 가변 축소를 수행하는 Streamcollect 메서드는 컬렉션들을 합치는 부담이 크기 때문에 병렬화에 적합하지 않다.
직접 구현한 Stream, Iterable, Collection이 병렬화의 이점을 제대로 누리게 하고 싶다면, spliterator 메서드를 반드시 재정의하고 결과 스트림의 병렬화 성능을 강도 높게 테스트해야 한다.

스트림을 잘못 병렬화하면 응답 불가를 포함해 성능이 나빠질 뿐만 아니라 결과 자체가 잘못되거나 예상 못한 동작이 발생할 수 있다. 결과가 잘못되거나 오동작하는 것은 안전 실패라 한다.

실제로 성능이 향상될지를 추정해보는 간단한 방법이 있다. 스트림 안의 원소 수와 원소당 수행되는 코드 줄 수를 곱해보면 된다. 이 값이 최소 수십만은 되어야 성능 향상을 맛볼 수 있다.

조건이 잘 갖춰지면 parallel 메서드 호출 하나로 거의 프로세서 코어 수에 비례하는 성능 향상을 만끽할 수 있다.

profile
백엔드

0개의 댓글