본문 바로가기
java

Optional은 얼마나 비쌀까

by 참된오징어 2025. 4. 25.

 

블로그들을 보다보면 Optional은 비싸다고 한다는데 얼마나 비싼것일까?

이런 의문을 가지고 간단하게 리서칭 및 테스트를 진행해보았다.

 

 

Optional Cost에 대한 블로깅

https://homes.cs.washington.edu/~mernst/advice/nothing-is-better-than-optional.html

 

Nothing is better than the Optional type

Nothing is better than the Optional type by Michael Ernst June 23, 2018 (和訳, Japanese translation, Java Magazine version) JDK 8 introduced the Optional class, a container that is either empty or contains a non-null value. Optional has numerous problems

homes.cs.washington.edu

http://pkolaczk.github.io/overhead-of-optional/

 

Overhead of Returning Optional Values in Java and Rust | Piotr Kołaczkowski

October 16, 2021 Some programming languages like Java or Scala offer more than one way to express a concept of “lack of value”. Traditionally, a special null value is used to denote references that don’t reference any value at all. However, over time

pkolaczk.github.io

https://dzone.com/articles/using-optional-correctly-is-not-optional

 

26 Reasons Why Using Optional Correctly Is Not Optional

We take a look at the top 25 most important concepts to keep in mind when working with Optionals in Java, focusing on null variables and more.

dzone.com

https://velog.io/@seanee3670/Optional-%EA%B3%BC-%EC%84%B1%EB%8A%A5

 

Optional 과 성능, 그리고 pros and cons

문득 그런 생각이 들었다.

velog.io

 

성능 차이가 있다 vs 성능 차이가 없다. 로 팽팽한 편

벤치마킹을 하여 직접 성능 차이를 느껴보자.

 

 

# 테스트

테스트 케이스를 구성할 때 여러 필드를 가진 복잡한 객체보다는 간단한 값으로 테스트를 구성. 불필요한 오버헤드를 최소화하도록 의도.

벤치마킹을 위해 JMH (Java Microbenchmark Harness) 라이브러리 이용.

 

  • 1부터 10억까지 덧셈하는 케이스로 테스트하였으며, 평균 시간, 메모리 사용량을 벤치마킹
  • 총 5개의 케이스에 대해 벤치마킹
     
    1. 1 ~ 10억까지 덧셈 - sumLong()
    2. 1 ~ 10억까지 Optional.ofNullable()으로 치환해서 덧셈 - sumOptional()
    3. 1 ~ 10억까지 OptionalLong을 이용하여 불필요한 박싱/언박싱을 최소화하기 위해 사용 - sumOptionalPrimitive()
    4. 1 ~ 10억까지 10억개 중 66% 비율만 덧셈 - sumLongWithNull()
    5. 1 ~ 10억까지 10억개 중 66% 비율만 Optional.ofNullable()으로 치환해서 덧셈 - sumOptionalNulls()

 

# 테스트 결과

테스트한 코드는 링크로 대체 (_ _)
https://github.com/ohtaeg/TIL/blob/master/java/src/main/java/optional/OptionalBenchmark.java

 

Benchmark                                                  Mode  Cnt            Score    Error   Units

OptionalBenchmark.sumLong                                  avgt    5          267.153 ± 11.951   ms/op
OptionalBenchmark.sumLong:gc.alloc.rate                    avgt    5           ≈ 10⁻⁴           MB/sec
OptionalBenchmark.sumLong:gc.alloc.rate.norm               avgt    5           14.562 ±  1.437    B/op
OptionalBenchmark.sumLong:gc.count                         avgt    5              ≈ 0           counts
OptionalBenchmark.sumLongWithNull                          avgt    5          424.664 ± 18.002   ms/op
OptionalBenchmark.sumLongWithNull:gc.alloc.rate            avgt    5           ≈ 10⁻⁴           MB/sec
OptionalBenchmark.sumLongWithNull:gc.alloc.rate.norm       avgt    5           22.933 ±  2.296    B/op
OptionalBenchmark.sumLongWithNull:gc.count                 avgt    5              ≈ 0           counts
OptionalBenchmark.sumOptional                              avgt    5          257.686 ± 10.979   ms/op
OptionalBenchmark.sumOptional:gc.alloc.rate                avgt    5           ≈ 10⁻⁴           MB/sec
OptionalBenchmark.sumOptional:gc.alloc.rate.norm           avgt    5           13.973 ±  1.840    B/op
OptionalBenchmark.sumOptional:gc.count                     avgt    5              ≈ 0           counts
OptionalBenchmark.sumOptionalNulls                         avgt    5         1292.880 ± 11.567   ms/op
OptionalBenchmark.sumOptionalNulls:gc.alloc.rate           avgt    5         9835.037 ± 88.169  MB/sec
OptionalBenchmark.sumOptionalNulls:gc.alloc.rate.norm      avgt    5  13333332380.800 ±  6.888    B/op
OptionalBenchmark.sumOptionalNulls:gc.count                avgt    5         1515.000           counts
OptionalBenchmark.sumOptionalNulls:gc.time                 avgt    5          445.000               ms
OptionalBenchmark.sumOptionalPrimitive                     avgt    5          265.150 ±  0.252   ms/op
OptionalBenchmark.sumOptionalPrimitive:gc.alloc.rate       avgt    5           ≈ 10⁻⁴           MB/sec
OptionalBenchmark.sumOptionalPrimitive:gc.alloc.rate.norm  avgt    5           14.484 ±  1.450    B/op
OptionalBenchmark.sumOptionalPrimitive:gc.count            avgt    5              ≈ 0           counts

 

위 결과들을 요약하면 아래와 같다.

 

메서드 평균 실행 시간 (ms/op) 메모리 할당률 (B/op) GC 횟수 요약
sumLong() 267.153 14.562 0 박싱 없음
sumOptional() 257.686 13.973 0 박싱 있음
Optional.ofNullable 사용
예상보다 빠름
sumOptionalPrimitive() 265.150 14.484 0 박싱 없음
OptionalLong 사용
sumLongWithNull() 424.664 22.933 0 박싱 있음
null 체크와 박싱된 Long 객체 반복 사용으로 인해 조금 성능 차이 있음.
sumOptionalNulls() 1292.880 13,333,332,380.8 1515 GC 유발
처리 속도와 메모리 사용률 높음

 

 

상황과 환경에 따라 테스트 결과가 다르겠지만, 위 테스트 결과로는 Optional을 어떻게 사용하냐에 따라 성능 차이가 존재하는 것 같다.

Optional 자체가 항상 느린 것은 아니기에 적절하게 사용하면 성능 차이가 없을수도 있다.

하지만 루프 등등 대량의 데이터를 많이 다룰 때는 위와 같이 GC 부담이 커지고 성능도 저하된다.

작은 차이가 누적되면 큰 성능 차이로 만들 수도 있다.

 

댓글