목차

1. 개요


JDK 7 부터는 **포크/조인 프레임워크(fork/join framework)**을 이용해서 쉽게 병렬화를 사용할 수 있도록 도와준다. 이 포크/조인 프레임워크와 내부적인 병렬 스트림 처리가 어떤 관계가 있는지부터 병렬 스트림을 제대로 사용하기 위한 기반 지식에 대해 알아보자.

그래서 최종적으로 Spliterator를 커스텀하게 만들어서 분할 과정을 원하는 식으로 만들어 볼 것이다.

2. 병렬 스트림


스트림에서 병렬화를 하는 방식은 너무 간단하다. 병렬화 하고자 하는 구조가 컬렉션이라면 parallelStream() 을 호출하면 **병렬 스트림(parallel stream)**이 생성된다. 배열이나 그 외의 요소도 stream().parallel() 만 호출해주면 병렬 스트림이 생성될 것이다. 병렬 스트림은 각각의 스레드에서 처리할 수 있도록 스트림 요소를 여러 청크로 분할한 스트림으로 전통적인 방식부터 병렬 스트림을 이용한 방식까지 각각 로직을 구현해서 성능을 비교해보자.

스트림 성능 측정해보기

숫자 합산 로직

1부터 n까지의 모든 숫자의 합계를 반환하는 메서드를 여러 방식을 이용해 다음과 같이 생성한다고 했을 때 각각의 성능차이는 어떨까?

/**
 * 순차 리듀싱
 * 무한 스트림을 사용해 합계를 계산한다.
 */
public long sequentialSum(long n) {
    return Stream.iterate(1L, i -> i + 1)
            .limit(n)
            .reduce(0L, Long::sum);
}

/**
 * 반복형
 * 스트림을 사용하지 않고 전통적인 방식으로 합계를 계산해 반환한다.
 */
public long iterativeSum(long n) {
    long result = 0;
    for (long i = 1L; i <= n; i++) {
        result += i;
    }

    return result;
}

아직 병렬화를 안했는데, 병렬화는 parallel()만 호출해주면 된다.

/**
 * 병렬 리듀싱
 * 무한 스트림을 병렬화 처리해 합계를 반환한다.
 */
public long parallelSum(long n) {
    return Stream.iterate(1L, i -> i + 1)
            .limit(n)
            .parallel() // 스트림을 병렬 스트림으로 변환한다.
            .reduce(0L, Long::sum);
}

이제 무언가 성능적인 개선이 있을 것으로 추측된다. 하지만, 아쉽게도 위 코드에서 병렬화 스트림으로 변환해서 생기는 이점은 없을 것이다.