为什么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