Search code examples
javaperformanceperformance-testing

Java null check performance


I was wondering if there was any significative difference on checking if an object is null in java with direct comparison, or by using the Objects.isNull() method.

public class Test {

  public final static Long ITERATIONS = 100000000L; 

  @Test
  public void noFnCalls() {
    balong startTime = System.currentTimeMillis();
    Object x = new Object();
    Long i;
    for (i = 0L; i < ITERATIONS; i++) {
      boolean t = x == null;
    }
    long estimatedTime = System.currentTimeMillis() - startTime;
    System.out.println("noFnCalls ellapsed time: " + estimatedTime);
  }

  @Test
  public void withFnCalls() {
    long startTime = System.currentTimeMillis();
    Object x = new Object();
    Long i;
    for (i = 0L; i < ITERATIONS; i++) {
      boolean t = Objects.isNull(x);
    }
    long estimatedTime = System.currentTimeMillis() - startTime;
    System.out.println("withFnCalls ellapsed time: " + estimatedTime);
  }
}

And surprisingly, at least for me, it always takes more time to finish the "noFnCalls". I was expecting pretty much the opposite result, since it results into a method call, using stack.

This is the output: (Changes every time, obviously, but always with "noFnCalls" higher)

noFnCalls ellapsed time: 583

withFnCalls ellapsed time: 463

Why is this produced?


Solution

  • The results you see are probably due to running "noFnCalls" first, without introducing proper warmup before the test and the measurement.

    I get this:

    withFnCalls ellapsed time: 444
    noFnCalls ellapsed time: 471
    withFnCalls ellapsed time: 334
    noFnCalls ellapsed time: 331
    withFnCalls ellapsed time: 330
    noFnCalls ellapsed time: 325
    withFnCalls ellapsed time: 331
    noFnCalls ellapsed time: 326
    withFnCalls ellapsed time: 326
    noFnCalls ellapsed time: 328
    

    Using

    import java.util.Objects;
    
    public class Test {
    
      public final static Long ITERATIONS = 100000000L; 
    
      public static void main(String args[]) {
        withFnCalls();
        noFnCalls();
        withFnCalls();
        noFnCalls();
        withFnCalls();
        noFnCalls();
        withFnCalls();
        noFnCalls();
        withFnCalls();
        noFnCalls();
      }
      public static void noFnCalls() {
        long startTime = System.currentTimeMillis();
        Object x = new Object();
        Long i;
        for (i = 0L; i < ITERATIONS; i++) {
          boolean t = x == null;
        }
        long estimatedTime = System.currentTimeMillis() - startTime;
        System.out.println("noFnCalls ellapsed time: " + estimatedTime);
      }
    
      public static void withFnCalls() {
        long startTime = System.currentTimeMillis();
        Object x = new Object();
        Long i;
        for (i = 0L; i < ITERATIONS; i++) {
          boolean t = Objects.isNull(x);
        }
        long estimatedTime = System.currentTimeMillis() - startTime;
        System.out.println("withFnCalls ellapsed time: " + estimatedTime);
      }
    }
    

    and

    withFnCalls ellapsed time: 3618
    noFnCalls ellapsed time: 3361
    withFnCalls ellapsed time: 3445
    noFnCalls ellapsed time: 3278
    withFnCalls ellapsed time: 3350
    noFnCalls ellapsed time: 3292
    withFnCalls ellapsed time: 3309
    noFnCalls ellapsed time: 3262
    withFnCalls ellapsed time: 3293
    noFnCalls ellapsed time: 3261
    

    If I increase to 1000000000L iterations. This was done with Java 9 64-bit server jvm, build 9+181, by Oracle, running on Windows 10 with a machine having Intel i5-2600 cpu.

    Like others have said, micro-benchmarking is hard and lot of different things affect the results. You shouldn't jump on conclusions with tests like these. This kind of test does not really tell much - any differences get easily lost in the noise measuring code that are so close to each other.

    Obligatory recommended thread about micro-benchmarking in java: How do I write a correct micro-benchmark in Java?.