通常我更喜欢空检查。但是在目前的情况下,我知道大部分时间我的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处理器上的空检查更好。
感谢大家指导我。
答案 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%的时间 - 其余的时间你的程序将不关心它。