스트림 병렬처리를 진행할 때, paraller Stream 이 순차적인 iterate(반복자) 보다 성능이 느리게 나오는 경우가 있습니다.
❓ 스트림 병렬처리의 성능이 낮게나오는 이유 중 하나로,
기본형(원시 타입) 특화되지 않은 스트림을 초러할 때 수반되는 오토박싱, 언방식 등의 오버헤드를 수반하기 때문이다.
라는 이유가 언급되었고, 그렇다면 스트림에서 왜 오토박싱이 일어나는지 잊어먹지 않기위해 정리합니다.
📗 요약 : 제네릭 때문에
자바 Stream 에서 내부적으로 오토박싱이(auto boxing) 일어나는 이유
📌 modern java in action 103 쪽을 보면
이유 1
👏🏻 함수형 인터페이스들에 제네릭 파라미터가 쓰여져있고, Java 에서 generice 은 참조형만 사용할 수 있다고 합니다. (제네릭 내부 구현상)
- 그렇기 때문에 자바에서는 기본형을 참조형으로 바꾸는 오토박싱이 일어나고,
- 기본형을 제넥릭에서 사용하기 위한 참조형 타입으로 박싱하고,
- 다시 언박싱해서 내보내는 일련의 과정이 추가되기 때문에 병렬 스트림에서 이에 대한 오버헤드가 일어날 수 있다..!
로 이해가 되는 것 같습니다.
이유 2
👏🏻 머,, 이유2 까지는 아니구 동일한 이야긴데 Stream 내부적으로보면, Stream Interface 도 제네릭 파라미터로 정의가 되어있습니다.
public interface Stream<T> extends BaseStream<T, Stream<T>> {
...
}
이처럼 Stream API 는 데이터가 객체라는 것을 기본 전제로 구현이 되어있기 때문에
기본형 타입(primitve type) 의 사용시, 내부적으로 오토박싱이 일어나는 이유였습니다!!
예시
오토박싱이 일어나는 Stream
➡️ 받아온 파라미터 숫자 n 까지의 합을 병렬 스트림을 이용해 구하는 로직입니다.
public long parallelSum(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
.parallel()
.reduce(0L, Long::sum);
}
기본형 원시타입 long 이 파리미터로 들어오기 때문에 오토박싱 오버헤드를 수반합니다.
오토박싱이 일어나지 않도록 성능을 개서한 Stream
➡️ Stream API 에서는 기본형 (long, int, double ..) 특화 스트림을 별도로 제공합니다.
- DoubleStream
- IntStream
- LongStream
public long parallelSum(long n) {
return LongStream.rangeClosed(1, n)
.parallel()
.reduce(0L, Long::sum);
}
아래처럼, LongStream 의 rangeClosed 메소드는 기본형타입을 다루기 때문에 오토박싱이 일어나지 않습니다..!
public static LongStream rangeClosed(long startInclusive, final long endInclusive) {
if (startInclusive > endInclusive) {
return empty();
} else if (endInclusive - startInclusive + 1 <= 0) {
// Size of range > Long.MAX_VALUE
// Split the range in two and concatenate
// Note: if the range is [Long.MIN_VALUE, Long.MAX_VALUE] then
// the lower range, [Long.MIN_VALUE, 0), and upper range,
// [0, Long.MAX_VALUE], will both be further split in two
long m = startInclusive + Long.divideUnsigned(endInclusive - startInclusive, 2) + 1;
return concat(range(startInclusive, m), rangeClosed(m, endInclusive));
} else {
return StreamSupport.longStream(
new Streams.RangeLongSpliterator(startInclusive, endInclusive, true), false);
}
}
참고 : 모던 자바 인 액션 103쪽
'Java > Java 문법' 카테고리의 다른 글
[JAVA] 지역변수 vs 전역변수 (feat. java8 JVM Static Object Heap Area) (0) | 2022.09.26 |
---|---|
추상클래스의 객체 생성 (추상클래스 인스턴스) - 추상클래스를 사용하는 이유 (0) | 2022.09.19 |
[JAVA] record 불변 객체 타입 (feat Lombok @Value) (0) | 2022.06.02 |
[JAVA8] Stream API란 - fiter /map /of 사용방법 (0) | 2022.05.24 |
private final VS private static final 왜 쓸까 (0) | 2022.03.10 |