Returning Java Optional Benchmark

I was reading on the internet that returning Java Optional may be slow and it is not recommend for high-performance applications.

A bit of history

In Java lack of a value can be represented as null or a more readable approach and less error-prone is to use Optional. Using Optional is the recommended way of writing code, beside making it clear for the method caller that value may be missing, it comes with a handy way of processing it using a functional style.

I would like to mention that if you don’t work in a extremely performance sensitive application you should use always Optional to represent the lack of a value.

In order to measure the impact of Optional usage I did a test using JMH:

Sum a list of numbers using a primitive long a Long and an Optional<Long>

@State(Scope.Benchmark)
@Fork(1)
@Warmup(iterations = 2)
@Measurement(iterations = 5)
public class OptionBenchmark {

    private final long MAGIC_NUMBER = 7;

    // Variant 1.
    // Probably the simplest way to sum numbers.
    // No boxing, no objects involved, just primitive long values everywhere.
    private long getNumber(long i) {
        return i & 0xFF;
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public long sumSimple() {
        long sum = 0;
        for (long i = 0; i < 1_000_000; ++i) {
            long n = getNumber(i);
            if (n != MAGIC_NUMBER)
                sum += n;
        }
        return sum;
    }

    // Variant 2.
    // Replace MAGIC_NUMBER with a null.
    // To be able to return null, we need to box long into a Long object.
    private Long getNumberOrNull(long i) {
        long n = i & 0xFF;
        return n == MAGIC_NUMBER ? null : n;
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public long sumNulls() {
        long sum = 0;
        for (long i = 0; i < 1_000_000; ++i) {
            Long n = getNumberOrNull(i);
            if (n != null) {
                sum += n;
            }
        }
        return sum;
    }


    // Variant 3.
    // Replace MAGIC_NUMBER with Optional.empty().
    // Now we not only need to box the value into a Long, but also create the Optional wrapper.
    private Optional<Long> getOptionalNumber(long i) {
        long n = i & 0xFF;
        return n == MAGIC_NUMBER ? Optional.empty() : Optional.of(n);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public long sumOptional() {
        long sum = 0;
        for (long i = 0; i < 1_000_000; ++i) {
            Optional<Long> n = getOptionalNumber(i);
            if (n.isPresent()) {
                sum += n.get();
            }
        }
        return sum;
    }
}

Here are the results that I have obtained on my laptop:

OpenJDK 64-Bit Server VM GraalVM CE 20.3.0

Benchmark                    Mode  Cnt     Score    Error  Units
OptionBenchmark.sumNulls     avgt    5  2753.341 ± 29.974  us/op
OptionBenchmark.sumOptional  avgt    5  5989.606 ± 60.489  us/op
OptionBenchmark.sumSimple    avgt    5  1192.625 ±  1.800  us/op
OpenJDK Runtime Environment (build 1.8.0_292-8u292-b10-0ubuntu1-b10)

Benchmark                    Mode  Cnt     Score     Error  Units
OptionBenchmark.sumNulls     avgt    5  3386.756 ± 348.132  us/op
OptionBenchmark.sumOptional  avgt    5  6647.280 ± 443.013  us/op
OptionBenchmark.sumSimple    avgt    5  1200.177 ±  24.333  us/op
OpenJDK Runtime Environment (build 17+35-Ubuntu-121.04)

Benchmark                    Mode  Cnt     Score     Error  Units
OptionBenchmark.sumNulls     avgt    5  1295.601 ±  33.755  us/op
OptionBenchmark.sumOptional  avgt    5  5649.651 ± 358.539  us/op
OptionBenchmark.sumSimple    avgt    5   617.378 ±  24.074  us/op

Conclusions

  • Using Optional where performance is extremely important will make your code run slower
  • Using boxing where performance is extremely important isn’t probably the best idea
  • Summing primitive long may be up to 9x more performant than using Optional

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *

Acest site folosește Akismet pentru a reduce spamul. Află cum sunt procesate datele comentariilor tale.