개요
인터프리터 언어를위한 X는 임의의 프로그램을 실행하는 프로그램 (또는 시스템, 또는 일반적으로기구의 단지 일종) 인 (P) 언어로 작성된 X를 그 효과를 수행하고, 명세서에 의해 규정 된 결과를 평가하도록 X를 . 최신 고성능 워크 스테이션 CPU는 실제로 그보다 더 복잡하지만 CPU는 일반적으로 해당 명령 세트에 대한 해석기입니다. 실제로는 독점적 인 전용 개인 명령 집합이있을 수 있으며 외부에서 볼 수있는 공개 명령 집합을 번역 (컴파일)하거나 해석 할 수 있습니다.
X 에서 Y 로의 컴파일러 는 일부 언어 X의 모든 프로그램 p 를 의미론과 같은 방식으로 일부 언어 Y 의 의미 상 동등한 프로그램 p ' 로 변환하는 프로그램 (또는 기계 또는 일반적으로 일종의 메커니즘)입니다. 예 를 들어 Y에 대해 해석기 ( interpreter)로 p ' 를 해석 하면 동일한 결과가 나오고 p 에 대해 X에 대해 해석기 (interpreter) 로 해석하는 것과 동일한 결과를 얻을 수 있습니다 . (참고 X를 및 Y는 동일한 언어 일 수있다.)
용어 앞서-의-시간 (AOT) 와 저스트 인 타임 (JIT)를 참조 하면 컴파일이 발생 : 그 용어에 언급 된 "시간"참조 "런타임"이며, 즉 JIT 컴파일러는 프로그램을 컴파일 그대로 running , AOT 컴파일러는 프로그램 이 실행되기 전에 컴파일합니다 . 이 언어에서 JIT 컴파일러해야합니다 X 언어 Y는 어떻게 든 언어에 대한 통역과 함께 일해야 Y그렇지 않으면 프로그램을 실행할 수있는 방법이 없습니다. (예를 들어, 자바 스크립트를 x86 머신 코드로 컴파일하는 JIT 컴파일러는 x86 CPU가 없으면 의미가 없습니다. 실행되는 동안 프로그램을 컴파일하지만 x86 CPU가 없으면 프로그램이 실행되지 않습니다.)
이 차이는 인터프리터에게는 의미가 없습니다. 인터프리터가 프로그램을 실행합니다. 프로그램이 실행되기 전에 프로그램을 실행하는 AOT 인터프리터 또는 프로그램이 실행되는 동안 프로그램을 실행하는 JIT 인터프리터는 의미가 없습니다.
그래서 우리는 :
- AOT 컴파일러 : 실행하기 전에 컴파일
- JIT 컴파일러 : 실행 중 컴파일
- 통역사 : 달리기
JIT 컴파일러
JIT 컴파일러 제품군 내에서 정확히 언제 컴파일 되는지 , 얼마나 자주 , 얼마나 세분화 되는지에 대해서는 여전히 많은 차이점이 있습니다 .
예를 들어, Microsoft CLR의 JIT 컴파일러는 코드를 한 번만 (로드 할 때) 컴파일하고 한 번에 전체 어셈블리를 컴파일합니다. 다른 컴파일러는 프로그램이 실행되는 동안 정보를 수집하고 새 정보를 사용할 수있게되면 코드를 여러 번 다시 컴파일하여 더 잘 최적화 할 수 있습니다. 일부 JIT 컴파일러는 코드를 최적화 해제 할 수도 있습니다. 자, 왜 그렇게하고 싶은지 스스로에게 물어봐도 될까요? 최적화 해제를 사용하면 실제로 안전하지 않은 매우 공격적인 최적화를 수행 할 수 있습니다. 너무 공격적인 것으로 밝혀 지면 다시 취소 할 수 있지만, 최적화를 해제 할 수없는 JIT 컴파일러를 사용하면 우선 공격적인 최적화.
JIT 컴파일러는 한 번에 코드의 일부 고정 장치를 컴파일 할 수도 있고 (하나 개의 모듈, 하나 개의 클래스, 하나의 함수, 하나의 방법, ...;이 일반적으로 호출되는 방법 한번에 한 예를 들어, JIT) 또는 그들이 할 수 추적 동적를 그런 다음 컴파일 할 동적 추적 (일반적으로 루프) 을 찾기위한 코드 실행 ( 추적 JIT 라고 함 )
통역사와 컴파일러 결합
통역사와 컴파일러는 단일 언어 실행 엔진으로 결합 될 수 있습니다. 이를 수행하는 두 가지 일반적인 시나리오가 있습니다.
에서 AOT 컴파일러 결합 X 에 Y를 위한 통역과 Y . 여기서 일반적으로 X 는 인간이 읽을 수 있도록 최적화 된 고급 언어 인 반면 Y기계에 의한 해석에 최적화 된 간결한 언어 (종종 어떤 종류의 바이트 코드)입니다. 예를 들어, CPython Python 실행 엔진에는 Python 소스 코드를 CPython 바이트 코드로 컴파일하는 AOT 컴파일러와 CPython 바이트 코드를 해석하는 인터프리터가 있습니다. 마찬가지로 YARV Ruby 실행 엔진에는 Ruby 소스 코드를 YARV 바이트 코드로 컴파일하는 AOT 컴파일러와 YARV 바이트 코드를 해석하는 인터프리터가 있습니다. 왜 그렇게 하시겠습니까? Ruby와 Python은 매우 수준이 높고 다소 복잡한 언어이므로 먼저 구문 분석하고 해석하기 쉬운 언어로 컴파일 한 다음 해당 언어 를 해석 합니다 .
인터프리터와 컴파일러를 결합하는 다른 방법은 혼합 모드 실행 엔진입니다. 여기서는 동일한 언어를 함께 구현하는 두 가지 "모드" , 즉 X에 대한 인터프리터 와 X 에서 Y에 이르는 JIT 컴파일러를 "혼합" 합니다. (여기서의 차이점은 위의 경우 컴파일러가 프로그램을 컴파일 한 다음 결과를 인터프리터에 공급하는 여러 "단계"를 가지고 있다는 것입니다. 여기 에는 동일한 언어 에서 두 개의 작업이 나란히 있습니다. ) 컴파일러에 의해 컴파일 된 코드는 인터프리터에 의해 실행되는 코드보다 빠르게 실행되는 경향이 있지만 실제로는 코드를 컴파일하는 데 먼저 시간이 걸립니다 (특히 실행하기 위해 코드를 크게 최적화하려는 경우)정말 빠르면 시간이 많이 걸립니다 ). 따라서 이번에 JIT 컴파일러가 코드를 컴파일하는 작업을 수행하기 위해 인터프리터는 이미 코드 실행을 시작할 수 있으며 JIT 컴파일이 완료되면 컴파일 된 코드로 실행을 전환 할 수 있습니다. 이는 컴파일 된 코드의 성능을 최대한 발휘하지만 컴파일이 끝날 때까지 기다릴 필요가 없으며 응용 프로그램이 바로 실행되기 시작한다는 것을 의미합니다.
이것은 실제로 혼합 모드 실행 엔진의 가장 간단한 응용 프로그램입니다. 더 흥미로운 가능성은 예를 들어 컴파일을 즉시 시작하지 않지만 인터프리터가 약간 실행되도록하고 통계, 프로파일 링 정보, 유형 정보, 특정 조건부 분기가 수행 될 가능성에 대한 정보, 수집되는 메소드를 수집하는 것입니다 가장 자주 등을 수행 한 다음이 동적 정보를 컴파일러에 공급하여 더 최적화 된 코드를 생성 할 수 있습니다. 이것은 위에서 언급 한 최적화 해제를 구현하는 방법이기도합니다. 최적화에 너무 공격적이라는 것이 밝혀지면 코드의 일부를 버리고 해석으로 되돌릴 수 있습니다. 예를 들어, HotSpot JVM이이를 수행합니다. JVM 바이트 코드에 대한 인터프리터와 JVM 바이트 코드에 대한 컴파일러가 모두 포함되어 있습니다. (사실로,두 컴파일러!)
제 컴파일 AOT 컴파일러되는 두 단계 : 것도 가능하고 이러한 두 가지 접근법 결합하는 일반적인 사실이다 X가 에 Y 두 번째 단계가 모두 해석 혼합 모드 엔진 인 Y를 하고 컴파일 Y를 까지 Z를 . Rubinius Ruby 실행 엔진은 다음과 같이 작동합니다. 예를 들어 Ruby 소스 코드를 Rubinius 바이트 코드로 컴파일하는 AOT 컴파일러와 먼저 Rubinius 바이트 코드를 해석하는 혼합 모드 엔진이 있으며, 일단 정보를 수집하면 가장 자주 호출되는 메소드를 네이티브로 컴파일합니다. 기계 코드.
혼합 모드 실행 엔진의 경우, 즉 빠른 시작을 제공하고 잠재적으로 정보를 수집하고 대체 기능을 제공하는 인터프리터의 역할은 대안으로 두 번째 JIT 컴파일러에 의해 수행 될 수도 있습니다. 예를 들어 V8의 작동 방식입니다. V8은 해석하지 않으며 항상 컴파일됩니다. 첫 번째 컴파일러는 매우 빠르게 시작되는 매우 빠르고 매우 얇은 컴파일러입니다. 그러나 생성하는 코드는 그리 빠르지 않습니다. 이 컴파일러는 또한 생성 된 코드에 프로파일 링 코드를 삽입합니다. 다른 컴파일러는 더 느리고 더 많은 메모리를 사용하지만 훨씬 더 빠른 코드를 생성하며 첫 번째 컴파일러에서 컴파일 한 코드를 실행하여 수집 된 프로파일 링 정보를 사용할 수 있습니다.