当99%的时间对象不为空时,空检查与try / catch

时间:2015-10-22 17:42:17

标签: java

通常我更喜欢空检查。但是在目前的情况下,我知道大部分时间我的if条件都会通过,而且很少有合法的场景,其中object可能为null。

此外,负载量很大(约500万次/小时)

现在我试图从性能角度找出哪种方式更好。已经检查过try/catch vs null check in java,但我的情况很独特。

同时检查Which is faster, try catch or if-else in java (WRT performance)但是这一个和上面的都是在通用上下文中,其中通过/失败比率的知识不可用。

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
    }               
}

尝试/捕捉版本

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. 
        //...   
    }               
}

更新:

获胜者是空检查。在Try / catch中,内部JVM将执行空检查并且无论如何都会抛出NPE,并且在JVM中创建异常处理(堆栈等的创建)将是开销。另外,根据另一个答案,现代CPU足够聪明,可以通过良好的预测来处理这些情况,在我的独特情况下,这些预测将始终有利。

我还编写了程序(在我的名字下面发布),结果清楚地表明我的AMD处理器上的空检查更好。

感谢大家指导我。

6 个答案:

答案 0 :(得分:7)

TL; DR:如果你没有在代码中进行空检查,那么运行时无论如何都会为你插入一个。空检查几乎总是没有成本。

您需要从HotSpot或优化JIT编译器的角度来查看此问题:

在对象变量上调用方法时

someObject.callMethod()

然后运行时需要在变量为空(Pseudo ASM)时抛出NPE:

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

现在有时运行时可以确定变量不为null。此分析称为 null check elimination

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

线索是您检查Java源代码

if (someObject != null)

足以向运行时证明变量不为null。

理由:

总是喜欢对捕获NPE进行空检查。如果不进行空检查,则运行时将为您插入。

答案 1 :(得分:3)

使用空检查,处理器将对其进行管道处理,并始终“忽略”if并继续执行该语句。

类似,关于流水线的讨论:Why is it faster to process a sorted array than an unsorted array?

答案 2 :(得分:2)

这里有两个方面,一个是性能,另一个是设计。你可以说哪个更好不是你应该问的问题,它是否足够好就是你所需要的。 如果99%的时间不为空,那么branch prediction将节省您的一天,JIT可能会优化更多。 但无论你选择什么,你都必须对这个特例做些什么。你是做什么?你抛出一个exception或者抓住NPE并再次重新抛出它?然后,exceptions可能是性能bottleneck而不是机制。

我会自己写这篇文章。

SubJob subJob = job.getSubJob();
Objects.requireNotNull(subJob);

答案 3 :(得分:2)

我强烈倾向于你现有的无效检查偏好。对于具有合法空值的情况,我会说应该检查null并为“从未发生过”的事情保存空指针异常。我应该提一下,这更像是一种范式偏好而不是基于表现的偏好。然而,对我来说,必须在性能上有巨大的好处,并且极其需要保证这种方法。

话虽如此,之前的一条评论声明异常开销只会在1%的时间内发生,在异常的情况下,假设try块的“设置”不会产生任何开销。我不确定是这样的。

答案 4 :(得分:1)

对于想要查看我运行测试的代码的人......

case 's' :
    ptr = va_arg(va, const char*);
    _puts(ptr, strlen(ptr));
    break;

输出(diff in nanos):

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

    }
}

答案 5 :(得分:0)

如何撤销if声明

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

这种方式(正如您声称的那样)这段代码只会被调用1%的时间 - 其余的时间你的程序将不关心它。

相关问题