가능한 중복 :
자바 예외는 얼마나 느린가요?
다음 두 프로그램의 실행 시간은 거의 같습니다.
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
그러나 다음 두 프로그램에 소요되는 시간은 상대적으로 다릅니다.
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
나는 간단한 try / throw / catch 메커니즘이 적어도 부분적으로 꼬리말에 최적화 된 리턴과 비슷할 것이라고 생각한다. (그래서 컨트롤이 가장 가까운 catch로 돌아 가야하는 곳을 직접 알 수있다). 물론, JRE 구현은 많은 최적화를 수행합니다.
후자에 큰 차이가있는 이유는 무엇입니까? 제어 흐름 분석이 전 두 프로그램이 거의 같고 실제 try / throw / catch가 특히 느리거나 또는 Return의find
던지기 중 하나가 될 수없는 동안 메서드 호출을 피하는 뭔가로 어떤 수준으로 펼쳐져, 또는 ..?
감사.
편집 :이 질문은 나와 다른 것처럼 보입니다.자바 예외는 얼마나 느린가요?왜냐하면 왜 이와 비슷한 큰 차이가 있는지 묻지 않기 때문입니다. 또한 예외 객체를 만드는 데 시간이 소비된다는 사실을 무시합니다 (fillInStackTrace
스택을 가로 지르고 배열을 만드는 것과 같은 것을 포함하여 오버라이드됩니다. 그것은 분명히 내 질문의 일부 대답 : "그것은 제어 흐름 분석을 꽤 많이 있기 때문에 과거의 두 프로그램을 결정하기 때문에 그것은 - 그것은 스택 추적 않습니다 답변에 언급 된 이상한 것 같지만 아마도 스택이 보이지 않는다고 판단하기위한 복잡한 분석을하지 않으면 어떤 스로우 / 캐치 연산도 왜곡 될 것입니다.
벤치 마크는 JVM 예열 효과를 고려하지 않습니다. 그러므로 실제 프로그램에서 try / break / return이 어떻게 수행되는지를 보여주는 결과가 실제로 있다는 것을 의심 할 여지가 있습니다.
(메소드에서 각 시간 지정 테스트를 선언하고 메소드를 여러 번 호출해야합니다. 그런 다음 처음 몇 번의 호출에서 출력을 버리십시오. 또는 숫자가 안정화 될 때까지 ... JIT의 일회성 비용을 없애기 위해 컴파일, 클래스 로딩 등).
진행중인 작업을 실제로 찾으려면 JIT 컴파일러를 통해 각 경우에 대해 생성하는 원시 코드를 덤프해야합니다.
첫 번째 경우 JIT 컴파일러가 throw / catch를 돌리고 있다는 것을 알게 될 것입니다.방법 내에서간단한 분기 명령으로 두 번째 경우에는 JIT 컴파일러가 더 복잡한 코드를 생성 할 가능성이 있습니다. 아마도이 코드는 분기와 동일한 것으로 인식하지 못하기 때문입니다.
차이점은 무엇입니까? JIT 옵티마이 저가 시도한 각 최적화에 대한 비용 / 이점 절충이 있습니다. JIT 컴파일러가 지원하는 각각의 새로운 최적화에는 구현 및 유지 관리 비용이 있습니다. 그리고 런타임에 컴파일러는 최적화를위한 전제 조건이 충족되는지 컴파일하기 위해 컴파일 할 코드를 검사해야합니다. 그들이 있다면 최적화를 수행 할 수 있습니다. 그렇지 않으면 JIT 컴파일러가 시간을 낭비하게됩니다.
이제이 예제에서 우리는 같은 메소드에서 throw되고 catch 된 (다른 경우에는) 메소드 호출 / 리턴 경계를 넘어서 전파되는 예외가 있습니다. 전자의 예에서 최적화 및 (가능한) 최적화 된 코드 시퀀스에 대한 충분한 전제 조건은 모두 매우 간단합니다. 후자의 예에서, 옵티마이 저는 예외를 던지고 잡는 메소드가 다른 컴파일 단위 (즉, 다시로드 될 수 있음), 재정의 가능성 등을 처리 할 수 있어야합니다. 또한 생성 코드 시퀀스는 훨씬 더 복잡합니다 ... 표준이 아닌 return-from-call 시퀀스와 분기가 이어집니다.
그래서 제 이론은 JIT 컴파일러 작성자들이 더 복잡한 최적화가 효과가 있다고 생각하지 않는다는 것입니다. 그리고 대부분의 사람들이그런 자바 코드를 쓰지 마라.아마 맞을 것이다.
find
Return and Throw에서 같은 수의 시간이라고 불리우는가? - assylias-1
안으로Return
던지기 같아.Throw
-이 결과가 직접적으로 끌어 올려 진 것을 쉽게 알 수 있습니다.main
메소드의 마지막 두 줄 (다른 유일한 호출)을 보면됩니다. - sigynsurfeitBreak
대Try
). Stephen이 아래의 답변에서 지적한 것처럼 JVM 개발자가이를 수행하는 데 드는 비용이이를 수행했을 때의 이점보다 클 수 있다고 판단했기 때문에 다른 코드만큼 사용하는 최적화 코드가 부족하기 때문일 수 있습니다. - sigynsurfeitthrow
누군가가 "예외"를 제기 할 때 보통 행해지는 것처럼. 두 가지 테스트에서 나는 똑같은 것을 던지기 만했다.Throwable
객체, 부분적으로 일종의 신호 - sigynsurfeit