为什么try/catch与Java中其他形式的控制流在性能上存在差异?
可能重复:为什么try/catch与Java中其他形式的控制流在性能上存在差异?,java,benchmarking,microbenchmark,Java,Benchmarking,Microbenchmark,可能重复: 以下两个程序的运行时间大致相同: 公共类中断{ 公共静态void main(字符串[]argv){ 长r=0,s=0,t=0; 对于(长x=10000000;x>0;x--){ 长y=x; 而(y!=1){ 如果(y==0) 抛出新的断言错误(); try2:{ try1:{ 对于(;;){ r++; 如果(y%2==0) 中断try1; y=y*3+1; } }/*渔获物(Thr_1)*/{ 对于(;;){ s++; 如果(y%2==1) 中断try2; y=y/2; } } }
以下两个程序的运行时间大致相同:
公共类中断{
公共静态void main(字符串[]argv){
长r=0,s=0,t=0;
对于(长x=10000000;x>0;x--){
长y=x;
而(y!=1){
如果(y==0)
抛出新的断言错误();
try2:{
try1:{
对于(;;){
r++;
如果(y%2==0)
中断try1;
y=y*3+1;
}
}/*渔获物(Thr_1)*/{
对于(;;){
s++;
如果(y%2==1)
中断try2;
y=y/2;
}
}
}/*渔获物(Thr_2)*/{
t++;
}
}
}
系统输出println(r+,“+s+,”+t);
}
}
公共类尝试{
私有静态类Thr扩展了可丢弃的{}
私有静态最终Thr抛出=新Thr();
公共静态void main(字符串[]argv){
长r=0,s=0,t=0;
对于(长x=10000000;x>0;x--){
长y=x;
而(y!=1){
试一试{
如果(y==0)
抛出新的断言错误();
试一试{
对于(;;){
r++;
如果(y%2==0)
投掷;
y=y*3+1;
}
}渔获物(Thr_1){
对于(;;){
s++;
如果(y%2==1)
投掷;
y=y/2;
}
}
}渔获物(Thr_2){
t++;
}
}
}
系统输出println(r+,“+s+,”+t);
}
}
在休息尝试中,x美元;做echo$x;时间:java$x;完成
打破
1035892632, 1557724831, 520446316
真正的0m10.733s
用户0m10.719s
系统0m0.016s
尝试
1035892632, 1557724831, 520446316
真实0m11.218s
用户0m11.204s
系统0m0.017s
但接下来两个项目所用的时间相对不同:
公共类返回{
私有静态int tc=0;
公共静态长查找(长值、长目标、整数深度){
如果(深度>100)
返回-1;
如果(值%100==目标%100){
tc++;
返回深度;
}
长r=查找(目标,值*29+4221673238171300827l,深度+1);
返回r!=-1?r:find(目标,值*27+4494772161415826936l,深度+1);
}
公共静态void main(字符串[]argv){
长s=0;
对于(int x=0;x<1000000;x++){
长r=find(0,x,0);
如果(r!=-1)
s+=r;
}
系统输出打印项次(s+“,”+tc);
}
}
公共类抛出{
找到可丢弃的公共静态类{
//注意静电!
公共静态int值=0;
}
私有静态最终发现=新发现();
私有静态int tc=0;
找到公共静态void find(长值、长目标、整数深度)抛出{
如果(深度>100)
返回;
如果(值%100==目标%100){
发现值=深度;
tc++;
扔发现;
}
查找(目标,值*29+4221673238171300827l,深度+1);
查找(目标,值*27+4494772161415826936l,深度+1);
}
公共静态void main(字符串[]argv){
长s=0;
对于(int x=0;x<1000000;x++)
试一试{
求(0,x,0);
}捕获(找到){
s+=找到的值;
}
系统输出打印项次(s+“,”+tc);
}
}
以$x作为回击;做echo$x;时间:java$x;完成
返回
84227391, 1000000
实0m2.437s
用户0m2.429s
系统0m0.017s
扔
84227391, 1000000
实际0m9.251s
用户0m9.215s
系统0m0.014s
我想象一个简单的try/throw/catch机制看起来有点像一个至少部分尾部调用优化的返回(因此直接知道控制应该返回到哪里(最近的捕获)),但是,当然,JRE实现做了很多优化
为什么后者有很大的区别而前者没有?这是因为控制流分析确定前两个程序几乎相同,而实际的try/throw/catch速度特别慢,还是因为Return的find
在某种程度上被展开,以避免方法调用,而throw的一个则不能,或者。。?
谢谢
编辑:这个问题对我来说似乎不同,因为它并没有问为什么在类似的情况下会有如此大的差异。它还忽略了创建异常对象所花费的时间这一事实(除非覆盖
fillInStackTrace
,否则它包括遍历堆栈并为其创建数组之类的内容)。然而,它显然回答了我问题的一部分:“这是因为控制流分析确定前两个程序几乎相同吗?”——尽管答案中提到堆栈跟踪似乎有些奇怪(这可能会使任何实际的抛出/捕获操作相形见绌,除非它进行一些复杂的分析以确定堆栈从未出现,这将使@Stephen的答案变得奇怪)。您的基准测试没有考虑JVM预热效应。因此,您看到的结果是否正确值得怀疑
$ for x in Break Try; do echo $x; time java $x; done
Break
1035892632, 1557724831, 520446316
real 0m10.733s
user 0m10.719s
sys 0m0.016s
Try
1035892632, 1557724831, 520446316
real 0m11.218s
user 0m11.204s
sys 0m0.017s
public class Return {
private static int tc = 0;
public static long find(long value, long target, int depth){
if(depth > 100)
return -1;
if(value%100 == target%100){
tc++;
return depth;
}
long r = find(target, value*29 + 4221673238171300827l, depth + 1);
return r != -1? r : find(target, value*27 + 4494772161415826936l, depth + 1);
}
public static void main(String[] argv){
long s = 0;
for(int x = 0; x < 1000000; x++){
long r = find(0, x, 0);
if(r != -1)
s += r;
}
System.out.println(s + ", " + tc);
}
}
$ for x in Return Throw; do echo $x; time java $x; done
Return
84227391, 1000000
real 0m2.437s
user 0m2.429s
sys 0m0.017s
Throw
84227391, 1000000
real 0m9.251s
user 0m9.215s
sys 0m0.014s