[Java] 스트림과 병렬 실행
데이터 컬렉션을 병렬로 처리하려면 데이터를 작은 단위로 분할하고 분할된 서브파티를 각각의 쓰레드로 할당해서 실행해야 한다.
이 과정에서 쓰레드의 Race Condition이 발생하지 않도록 동기화 해 줘야 하고, 각 쓰레드의 작업 결과를 합쳐 줘야 한다.
몇 개의 쓰레드를 사용하고 결과 변수는 어떻게 동기화 해야 하는지도 함께 고려해 줘야 한다.
스트림은 순차 스트림을 병렬 스트림으로 바꾸는 기능을 제공한다.
스트림을 사용해 병렬 작업을 구현해보자.
컬렉션에서 parallelStream을 호출하면 병렬 스트림이 생성된다.
병렬 스트림은 각각의 쓰레드에서 처리할 수 있도록 스트림 요소를 여러 덩어리로 분할한 스트림으로, 멀티코어 프로세서가 각 덩어리를 처리하도록 할당할 수 있다.
public long parallelStream(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
.parallel()
.reduce(0L, Long::sum);
}
parallel() 메서드를 통해 리듀싱 연산을 여러 청크에 나눠 병렬로 수행하고 부분의 결과를 다시 합쳐 전체 스트림의 리듀싱 결과를 도출한다.
작업을 병렬로 수행하면 이론적으로는 n개의 쓰레드가 나눠서 작업을 처리해 성능이 n배 더 좋아질 것으로 예상되지만..
스트림을 재귀적으로 분할하고 각 스트림을 하나로 합치는 등 여러 가지 오버헤드가 발생한다.
멀티코어 간 데이터 이동의 오버헤드는 생각보다 비싸다.
데이터 전송 시간보다 훨씬 오래 걸리는 작업만 병렬로 처리하는 편이 합리적이다.
박싱, 스트림의 자료구조, 병합 비용, 쓰레드가 안전하게 실행될 수 있는 환경 등을 고려해 상황에 맞춰서 병렬화를 수행하자.
병렬 스트림은 내부적으로 ForkJoinPool을 사용해 쓰레드를 관리한다.
기본적으로 분할 정복 알고리즘을 사용해 작업 단위를 나누고 각 작업들을 쓰레드로 실행시킨 후 결과를 취합하는 방식으로 작동한다.
스트림은 병렬 작업을 위해 내부적으로 Spliterator 인터페이스를 구현해서 사용한다.
데이터타입과 Spliterator를 기반으로 작업을 나누는 기준을 설정하고,
나눈 작업은 Work-Stealing 알고리즘을 통해 모든 쓰레드에게 작업을 균등하게 분배한다.
'Programming Language > Java' 카테고리의 다른 글
[Java] 컬렉션과 스트림 (0) | 2023.08.01 |
---|---|
[Java] 람다 표현식 (0) | 2023.06.09 |
자바 예외 이해하기 (0) | 2022.09.03 |
[Java] 네트워킹 (Networking) 2 (0) | 2021.12.13 |
[Java] 네트워킹 (Networking) 1 (0) | 2021.12.05 |
댓글
이 글 공유하기
다른 글
-
[Java] 컬렉션과 스트림
[Java] 컬렉션과 스트림
2023.08.01 -
[Java] 람다 표현식
[Java] 람다 표현식
2023.06.09 -
자바 예외 이해하기
자바 예외 이해하기
2022.09.03 -
[Java] 네트워킹 (Networking) 2
[Java] 네트워킹 (Networking) 2
2021.12.13