重複の可能性:
Java 例外はどれくらい遅いですか?
次の 2 つのプログラムの実行には、ほぼ同じ時間がかかります。
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; 時間 Java $x; 終わり 壊す 1035892632、1557724831、520446316 実質 0m10.733s ユーザー 0m10.719s システム 0m0.016s 試す 1035892632、1557724831、520446316 実質 0 分 11.218 秒 ユーザー 0m11.204s システム 0m0.017s
ただし、次の 2 つのプログラムにかかる時間は比較的異なります。
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; 時間 Java $x; 終わり 戻る 84227391、1000000 実質 0m2.437s ユーザー 0m2.429s システム 0m0.017s 投げる 84227391、1000000 実質 0 分 9.251 秒 ユーザー 0 分 9.215 秒 システム 0m0.014s
単純な try/throw/catch メカニズムは、少なくとも部分的に末尾呼び出しが最適化された戻り値のように見えると思います (そのため、制御がどこに戻る必要があるか (最も近いキャッチ) が直接わかります)。もちろん、JRE の実装では多くの最適化が行われます。
なぜ後者には大きな違いがあるのに前者には違いがないのでしょうか? 制御フロー分析により、前の 2 つのプログラムがほとんど同じであると判断され、実際の try/throw/catch が特に遅いためか、Returnfind
がメソッド呼び出しを回避するレベルに展開され、Throw のプログラムがメソッド呼び出しを回避できないためでしょうか。 、 また ..?ありがとう。
編集: この質問は、Java の例外はどれくらい遅いですか? とは異なるようです。これは、このようなケースでなぜこれほど大きな違いがあるのかを尋ねていないからです。また、例外オブジェクトの作成に時間がかかるという事実も無視します (fillInStackTrace
オーバーライドされない限り、スタックを走査してその配列を作成するなどの処理が含まれます)。しかし、それはどうやら私の質問の一部に答えているようです。おそらく、実際のスロー/キャッチ操作を矮小化するでしょう—複雑な分析を行ってスタックが決して見られないと判断しない限り、@Stephenの答えは奇妙になります)。