JVM이 여전히 꼬리 호출 최적화를 지원하지 않는 이유는 무엇입니까?


95

2 년 후 않습니다 - 더 - JVM이 - 꼬리 - 전화 - 최적화를 방지 하는있을 것 같습니다 프로토 타입 구현MLVM이 지금 얼마 동안 "프로토 80 %"로 기능을 나열하고있다.

테일 콜을 지원하는 데 썬 / 오라클 측의 적극적인 관심이 없나요? 아니면 JVM 에서 언급 한 것처럼 테일 콜이 "[...] 모든 기능 우선 순위 목록에서 2 위를 차지할 운명이었던 것일까 요? Language Summit ?

누군가 MLVM 빌드를 테스트하고 그것이 얼마나 잘 작동하는지에 대한 인상을 공유 할 수 있다면 정말로 관심이있을 것입니다.

업데이트 : Avian 과 같은 일부 VM은 문제없이 적절한 테일 콜을 지원합니다.


14
오라클 썬 사람들의보고 탈출로, 나는 기대하지 않을 것이다 어떤 명시 적 :( Oracle에서 말했다하지 않는 한 현재 프로젝트의 계속
Thorbjørn Ravn 안데르센

16
귀하의 수락 된 답변은 완전히 잘못되었습니다. 테일 콜 최적화와 OOP 사이에는 근본적인 충돌이 없으며 물론 OCaml 및 F #과 같은 여러 언어에는 OOP와 TCO가 모두 있습니다.
JD

2
음, OCaml 및 F # OOP 언어를 호출하는 것은 애초에 나쁜 농담입니다. 하지만 그렇습니다. OOP와 TCO는 공통점이 많지 않습니다. 런타임이 최적화되는 메서드가 다른 곳에서 재정의 / 서브 클래 싱되지 않았는지 확인해야한다는 사실을 제외하고는 다릅니다.
soc

5
+1 C 배경에서 저는 항상 TCO가 모든 최신 JVM에서 주어진다고 생각했습니다. 실제로 확인한 적이 없었고 결과가 놀라웠습니다 ...
thkala

2
@soc : "최적화되는 메소드가 다른 곳에서 재정의 / 서브 클래 싱되지 않았는지 런타임이 확인해야한다는 사실을 제외하고". 당신의 "사실"은 완전 넌센스입니다.
JD

답변:


32

Java 코드 진단 : Java 코드의 성능 향상 ( alt )은 JVM이 테일 호출 최적화를 지원하지 않는 이유를 설명합니다.

그러나 꼬리 재귀 함수를 자동으로 간단한 루프로 변환하는 방법은 잘 알려져 있지만 Java 사양에서는이 변환을 수행 할 필요가 없습니다. 아마도 요구 사항이 아닌 한 가지 이유는 일반적으로 객체 지향 언어에서 정적으로 변환 할 수 없기 때문입니다. 대신 꼬리 재귀 함수에서 단순 루프로의 변환은 JIT 컴파일러에 의해 동적으로 수행되어야합니다.

그런 다음 변환되지 않는 Java 코드의 예를 제공합니다.

따라서 목록 3의 예에서 볼 수 있듯이 정적 컴파일러가 언어의 의미를 유지하면서 Java 코드에서 꼬리 재귀 변환을 수행 할 것으로 기대할 수 없습니다. 대신 JIT에 의한 동적 컴파일에 의존해야합니다. JVM에 따라 JIT는이를 수행하거나 수행하지 않을 수 있습니다.

그런 다음 JIT가이를 수행하는지 확인하는 데 사용할 수있는 테스트를 제공합니다.

당연히이 문서는 IBM 문서이므로 다음과 같은 플러그가 포함됩니다.

이 프로그램을 Java SDK 몇 개로 실행했는데 결과는 놀라웠습니다. 버전 1.3 용 Sun의 Hotspot JVM에서 실행하면 Hotspot이 변환을 수행하지 않는다는 것을 알 수 있습니다. 기본 설정에서 스택 공간은 내 컴퓨터에서 1 초 이내에 소진됩니다. 반면에 IBM의 1.3 버전 용 JVM은 문제없이 순조롭게 진행되어 이러한 방식으로 코드를 변환합니다.


62
FWIW, 꼬리 호출은 그가 암시하는 것처럼 자기 재귀 함수에 관한 것이 아닙니다. 테일 호출은 테일 위치에 나타나는 모든 함수 호출입니다. 자체 호출 일 필요는 없으며 정적으로 알려진 위치에 대한 호출 일 필요도 없습니다 (예 : 가상 메서드 호출 일 수 있음). 그가 설명하는 문제는 테일 호출 최적화가 일반적인 경우에 제대로 수행되면 문제가되지 않으며 결과적으로 그의 예제는 테일 호출을 지원하는 객체 지향 언어 (예 : OCaml 및 F #)에서 완벽하게 작동합니다.
JD

3
"JIT 컴파일러에 의해 동적으로 수행되어야 함"즉, Java 컴파일러가 아닌 JVM 자체에서 수행되어야 함을 의미합니다. 그러나 OP는 JVM에 대해 묻고 있습니다.
Raedwald 2011 년

11
"일반적으로 객체 지향 언어에서는 정적으로 변환 할 수 없습니다." 이것은 물론 인용문이지만, 그러한 변명을 볼 때마다 숫자에 대해 물어보고 싶습니다. 실제로 대부분의 경우 컴파일 타임에 설정 될 수 있다면 놀라지 않을 것이기 때문입니다.
greenoldman 2012

5
인용 된 기사에 대한 링크는 이제 끊어졌지만 Google은 캐시에 저장했습니다. 더 중요한 것은 저자의 추론이 잘못되었다는 것입니다. 주어진 예제 는 컴파일러 가 객체 인지 확인하기 위해 (의 하위 클래스가 아닌) 확인을 삽입 한 경우 동적 컴파일이 아닌 정적 컴파일을 사용하여 꼬리 호출 최적화 될 있습니다 . instanceofthisExampleExample
Alex D


30

Java에서 TCO를 구현하지 않은 (그리고 어려운 것으로 간주되는) 이유 중 하나는 JVM의 권한 모델이 스택에 민감하므로 테일 호출이 보안 측면을 처리해야한다는 것입니다.

나는 이것이 Clements와 Felleisen [1] [2]에 의해 장애물이 아니라고 생각하며 질문에 언급 된 MLVM 패치도 그것을 다루고 있다고 확신합니다.

나는 이것이 당신의 질문에 대답하지 않는다는 것을 알고 있습니다. 흥미로운 정보를 추가하는 것뿐입니다.

  1. http://www.ccs.neu.edu/scheme/pubs/esop2003-cf.pdf
  2. http://www.ccs.neu.edu/scheme/pubs/cf-toplas04.pdf


15

이미 이것을 알고 있을지 모르지만 Java 언어가 실제로 프로그래머에게 스택 추적을 노출하기 때문에 기능이 들리는 것처럼 사소한 것은 아닙니다.

다음 프로그램을 고려하십시오.

public class Test {

    public static String f() {
        String s = Math.random() > .5 ? f() : g();
        return s;
    }

    public static String g() {
        if (Math.random() > .9) {
            StackTraceElement[] ste = new Throwable().getStackTrace();
            return ste[ste.length / 2].getMethodName();
        }
        return f();
    }

    public static void main(String[] args) {
        System.out.println(f());
    }
}

"테일 콜"이 있더라도 최적화되지 않을 수 있습니다. (이 경우 되고 최적화, 여전히 프로그램이 의존의 의미 이후 전체 호출 스택의 부기가 필요합니다.)

기본적으로 이것은 하위 호환성을 유지하면서이를 지원하기가 어렵다는 것을 의미합니다.


17
당신의 생각에서 실수를 발견했습니다 : "프로그램의 의미론이 그것에 의존하기 때문에 전체 호출 스택의 부기가 필요합니다". :-) 새로운 "억제 된 예외"와 같습니다. 그러한 것에 의존하는 프로그램은 깨질 수밖에 없습니다. 제 생각에 프로그램의 동작은 절대적으로 정확합니다. 스택 프레임을 버리는 것이 테일 호출의 전부입니다.
soc

4
@Marco,하지만 거의 모든 메서드가 예외를 throw 할 수 있으며, 여기서 전체 호출 스택을 사용할 수 있습니다. 게다가이 g경우 간접적으로 호출 할 메서드를 미리 결정할 수는 없습니다 . 예를 들어 다형성과 반사에 대해 생각해보십시오.
aioobe

2
Java 7에 ARM을 추가하여 발생하는 부작용입니다. 위에서 보여준 그런 것에 의존 할 수없는 예입니다.
soc

6
"언어가 호출 스택을 노출한다는 사실은 이것을 구현하기 어렵게 만듭니다": 언어 는 소스 코드가 보여주는 getStackTrace()메소드에서 반환 된 스택 추적 이 메소드 x()에서 호출 된 것을 y()보여줄 것을 x()요구 y()합니까? 자유가 있다면 진짜 문제가 없기 때문입니다.
Raedwald 2011 년

8
이것은 "모든 스택 프레임을 제공합니다"에서 "테일 호출에 의해 사용되지 않는 스택 프레임을 제외하고 모든 활성 스택 프레임을 제공합니다"에 이르기까지 단일 메소드의 사양을 표현하는 문제 일뿐입니다. 또한 꼬리 호출이 수행되는지 여부에 관계없이 명령 줄 스위치 또는 시스템 속성으로 만들 수 있습니다.
Ingo 2015 년

12

Java는 당신이 상상할 수있는 최소한의 기능적 언어 이지만 (글쎄요, 아마도 아닐 것입니다 !) 이것은 Scala 와 같은 JVM 언어에 큰 이점이 될 것 입니다.

내 관찰에 따르면 JVM을 다른 언어 용 플랫폼으로 만드는 것은 Sun의 우선 순위 목록에있는 것 같지 않았으며 이제 Oracle의 경우에도 마찬가지입니다.


16
@ Thorbjørn-주어진 프로그램이 한정된 시간 내에 중단되는지를 예측하는 프로그램을 작성했습니다. 그것은 나에게했다 연령대를 !
oxbow_lakes

3
내가 사용한 첫 번째 BASIC에는 기능이 없었지만 GOSUB와 RETURN이 있습니다. 나는 LOLCODE가 매우 기능적이라고 생각하지 않습니다 (그리고 두 가지 의미로 받아 들일 수 있습니다).
David Thornley

1
@David, 기능적! = 기능이 있습니다.
Thorbjørn Ravn Andersen

2
@ Thorbjørn Ravn Andersen : 아니요,하지만 전제 조건입니다. 그렇지 않나요?
David Thornley

3
"JVM을 다른 언어 용 플랫폼으로 만드는 것은 Sun의 최우선 순위 목록에있는 것 같지 않았습니다." 그들은 JVM을 기능적 언어보다 동적 언어를위한 플랫폼으로 만드는 데 훨씬 더 많은 노력을 기울였습니다.
JD
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.