10

Normally I prefer null check. But in current scenario I know that most of the time my if condition will pass and there are few legitimate scenario where object may be null.

Also, load is huge (about 5 million calls / hour)

Now I trying to find which way is better from performance perspective. Already checked try/catch vs null check in java but my case is unique.

Also checked Which is faster, try catch or if-else in java (WRT performance) but both this one and above ones are in generic context where knowledge of pass/fail ratio is not available.

public void process(Job job) {
    //... some code which processes job

    SubJob subJob = job.getSubJob();
    if(subJob != null) {  // 99% of the time this will pass
        //.. do something
    }               
}

try/catch version

public void process(Job job) {
    //... some code which processes job

    SubJob subJob = job.getSubJob();
    try {
        //.. do something  
    }catch(NullPointerException e) { //This may occure only 1% of the time. 
        //...   
    }               
}

Update:

Winner is null check. In Try/catch, internally JVM will do null check and throw NPE anyway and on top of that exception handling in JVM (creation of stack etc) will be overhead. Also as per another answer, modern CPUs are intelligent enough to handle these scenario with good prediction which in my unique case will always work in favor.

I also wrote program (posted below under my name) and results are clearly indicating that null check is way better on my AMD processor.

Thank you folks for guiding me.

Community
  • 1
  • 1
Jags
  • 799
  • 7
  • 19
  • 5 millions calls per hour is less that 1400 calls per second, and a null check probably only takes a few tens of microseconds. What performance did you see when you compared these two? – David Conrad Oct 22 '15 at 17:49
  • Why don't you stress test it and find out... – brso05 Oct 22 '15 at 17:49
  • You should be able to setup a test to compare the 2 methods and see which one is more efficient. – brso05 Oct 22 '15 at 17:50
  • 1
    Why don't you change it so that every Job always has a SubJob, even if it's a NO-OP? – David Conrad Oct 22 '15 at 17:50
  • 2
    @DavidConrad it's probably closer to one nanosecond per null check with a high chance that branch prediction will make it 0 most of the time... – assylias Oct 22 '15 at 18:09
  • Possible duplicate of [Which is faster, try catch or if-else in java (WRT performance)](http://stackoverflow.com/questions/3490770/which-is-faster-try-catch-or-if-else-in-java-wrt-performance) – Raedwald Oct 26 '15 at 20:19
  • "my question is unique": no it isn't. – Raedwald Oct 26 '15 at 20:20

6 Answers6

8

TL;DR: If you don't do the null check in your code, the runtime will insert one for you anyway. Null checks almost always have zero cost.


You need to view this problem from the perspective of HotSpot or an optimizing JIT compiler:

When you call a method on an object variable

someObject.callMethod()

then the runtime needs to throw a NPE if the variable is null (Pseudo ASM):

check someObject ref not null else throw NPE
invoke 'callMethod' on 'someObject' 

Now sometimes the runtime can be sure that a variable is not null. This analysis is called null check elimination.

-- no need: check someObject ref not null else throw NPE
invoke 'callMethod' on 'someObject' 

The clue is that your check in the Java source code

if (someObject != null)

is good enough to prove to the runtime that the variable is not null.

The rationale:

Always prefer a null check over catching a NPE. If you don't do the null check then the runtime will insert it for you.

David Conrad
  • 15,432
  • 2
  • 42
  • 54
wero
  • 32,544
  • 3
  • 59
  • 84
  • 1
    good blog post about null check elimination: http://jpbempel.blogspot.de/2013/09/null-check-elimination.html – wero Oct 22 '15 at 18:09
  • wow. I missed it totally. Yes, you are right, null check will be done anyway. – Jags Oct 22 '15 at 18:25
3

Go with the null checks, the processor will pipeline it and should always "ignore" the if and go on through to the statement.

Similar, talks about pipelining: Why is it faster to process a sorted array than an unsorted array?

Community
  • 1
  • 1
phflack
  • 2,729
  • 1
  • 9
  • 21
  • Nice read. So If i assume that current array of processors are way better then past, my if condition will have no effect on total time. But will it still take cup cycle anyway? – Jags Oct 22 '15 at 18:26
  • 1
    The pipelining is designed to not slow down the processor, it should assume that it's not null and keep processing as if it's instantiated. The processor will then on the side attempt to check if it was right on it's guess of non-nullness. – phflack Oct 22 '15 at 18:35
3

I would strongly lean to your existing preference for the null-check. For a situation that has a legitimate null condition, I'd say you should check for null and save a null-pointer exception for something that "never happens." I should mention that this is more of a paradigm preference than a performance-based one. However, for me, there would have to be an enormous benefit in performance and an extreme need to warrant the approach.

That being said, a previous comment stating that exception overhead would only be incurred 1% of the time, in the case of an exception, assumes that the "setting up" of the try block takes no overhead. I'm not sure this is the case.

closerware
  • 41
  • 1
  • 7
2

There are two aspects here, one is performance and the other is design. You can say which is better is not question you should ask, is it good enough is what you need. If 99% of the time it is not null, then branch prediction will save your day, and JIT might optimise more. But whatever you choose, you have to do something with this special-case. What do you do? You throw an exception or catch the NPE and re-throw it again?Then, exceptions might be the performance bottleneck and not the mechanism .

I would write this myself.

SubJob subJob = job.getSubJob();
Objects.requireNotNull(subJob);
Community
  • 1
  • 1
Sleiman Jneidi
  • 22,907
  • 14
  • 56
  • 77
1

For people who want to see the code I ran for testing...

public class TestMainResuse {

    static int load = 1000000;
    public static void main(String[] args) throws Exception {
        Object[] objects = new Object[load]; 

        for(int i = 0; i < load; i++) {
            if(i % 100 == 0 ) 
                objects[i] = null;
            else 
                objects[i] = new Object(); 
        }


        withTry(objects);
        withIf(objects);
        withTry(objects);
        withIf(objects);
        withTry(objects);
        withIf(objects);

        withIf(objects);
        withIf(objects);

        withTry(objects);
        withTry(objects);
    }

    public static void withTry(Object[] objects){
        long startTime = System.nanoTime();
        for(int i = 0; i < load; i++) {
            try {
              objects[i].getClass();
            } catch (NullPointerException e) {
                //System.out.println("this");
            }
        }
        System.out.println(" try took "+ (System.nanoTime() - startTime));

    }

    public static void withIf(Object[] objects){
        long startTime = System.nanoTime(); 
        for(int i = 0; i < load; i++) {
            if(objects[i] != null) {
                objects[i].getClass();
            }
        }
        System.out.println("null took "+ (System.nanoTime() - startTime));

    }
}

Output (diff in nanos):

 try took 6906539
null took 3801656
 try took 13380219
null took 87684
 try took 1036815
null took 79558
 try took 1021416
null took 10693
 try took 1020989
null took 1711
null took 1284
null took 1283
null took 1283
null took 1283
null took 1711
 try took 1038954
 try took 1106107
 try took 1040237
 try took 1020134
 try took 1028261
Jags
  • 799
  • 7
  • 19
0

How about reversing your if statement

if(subJob == null){
//do something to fix it;
}

This way (as you claim) this piece of code will be invoked only 1% of time¨and rest of the time your program will not care about it.

The Law
  • 344
  • 3
  • 20
  • 1
    or even an `else` off of the `if statement` to fix it – austin wernli Oct 22 '15 at 17:46
  • 4
    I am trying to avoid if condition itself to save cpu cycle. – Jags Oct 22 '15 at 17:47
  • 2
    @austinwernli But those are additional keystrokes, and we all know every programmer has finite number of keystrokes left until he expires – The Law Oct 22 '15 at 17:48
  • @TheLaw I'm pretty sure its that he wants to remove the check altogether, not about typing a few more letters – user1231232141214124 Oct 22 '15 at 17:48
  • @Jags Only two alternatives then are to either prevent subJob being constructed as null, or having unchecked exception in your code, which is devil incarnate. When someone finds a way to eliminate null checks altogether, without checking them or making conditions for them to no occur, he will be the next big thing. And to answer your question then, using if is definetly better idea than try-catch. – The Law Oct 22 '15 at 17:52