Possible Duplicate:
How slow are Java exceptions?
The following two programs take approximately the same amount of time to run:
public class Break {
public static void main(String[] argv){
long r = 0, s = 0, t = 0;
for(long x = 10000000; x > 0; x--){
long y = x;
while(y != 1){
if(y == 0)
throw new AssertionError();
try2: {
try1: {
for(;;){
r++;
if(y%2 == 0)
break try1;
y = y*3 + 1;
}
}/*catch(Thr _1)*/{
for(;;){
s++;
if(y%2 == 1)
break try2;
y = y/2;
}
}
}/*catch(Thr _2)*/{
t++;
}
}
}
System.out.println(r + ", " + s + ", " + t);
}
}
public class Try {
private static class Thr extends Throwable {}
private static final Thr thrown = new Thr();
public static void main(String[] argv){
long r = 0, s = 0, t = 0;
for(long x = 10000000; x > 0; x--){
long y = x;
while(y != 1){
try{
if(y == 0)
throw new AssertionError();
try{
for(;;){
r++;
if(y%2 == 0)
throw thrown;
y = y*3 + 1;
}
}catch(Thr _1){
for(;;){
s++;
if(y%2 == 1)
throw thrown;
y = y/2;
}
}
}catch(Thr _2){
t++;
}
}
}
System.out.println(r + ", " + s + ", " + t);
}
}
$ 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
But the time taken by the next two programs is relatively different:
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);
}
}
public class Throw {
public static class Found extends Throwable {
// note the static!
public static int value = 0;
}
private static final Found found = new Found();
private static int tc = 0;
public static void find(long value, long target, int depth) throws Found {
if(depth > 100)
return;
if(value%100 == target%100){
Found.value = depth;
tc++;
throw found;
}
find(target, value*29 + 4221673238171300827l, depth + 1);
find(target, value*27 + 4494772161415826936l, depth + 1);
}
public static void main(String[] argv){
long s = 0;
for(int x = 0; x < 1000000; x++)
try{
find(0, x, 0);
}catch(Found _){
s += found.value;
}
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
I'd imagine a simple try/throw/catch mechanism would look sort of like an at least-partially-tail-call-optimised return (so it is directly known where control should return to (the closest catch)), but, of course, the JRE implementation does a lot of optimisation.
Why is there a large difference in the latter but not the former? Will it be because control flow analysis determines the former two programs to be pretty much the same and actual try/throw/catches are particularly slow, or because Return's find
is unfolded to some level into something that avoids method calls while Throw's one can't be, or ..?
Thanks.
Edit: this question seems different to me to How slow are Java exceptions? because it doesn't ask why there is such a large difference in a case similar to this one. It also disregards the fact that time is spent creating the exception object (which—unless fillInStackTrace
is overridden—includes something like traversing the stack and creating an array for it). It does, however, apparently answer part of my question: “Will it be because control flow analysis determines the former two programs to be pretty much the same”—though it seems odd that it was mentioned in the answer that does the stack trace (which would presumably dwarf any actual throw/catch operation—unless it does some complicated analysis to determine that the stack is never seen, which would make @Stephen's answer odd).