Reputation: 5146
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?
Upvotes: 1
Views: 1854
Reputation: 53482
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?.
Upvotes: 5