简体   繁体   中英

Null check vs try/catch when 99% of the time object is not null

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.

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.

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?

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);

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.

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

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 technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM