똑같은 삽질은 2번 하지 말자

JIT 컴파일러 이해 (Just-In-Time 컴파일러) 본문

Java

JIT 컴파일러 이해 (Just-In-Time 컴파일러)

곽빵 2020. 8. 25. 02:24

JIT (Just-In-Time) 컴파일러는 런타임시 Java 애플리케이션의 성능을 향상시키는 Java Runtime Environment의 구성 요소입니다. JVM의 어떤 것도 컴파일러보다 성능에 더 큰 영향을 미치지 않으며, 컴파일러를 선택하는 것은 Java 개발자이든 최종 사용자이든 Java 애플리케이션을 실행할 때 내린 첫 번째 결정 중 하나입니다.

자바 JIT 컴파일러 : 개요

자바 파워 "한 번 작성하면 어디에서나 실행"의 핵심은 bytecode. 바이트 코드가 애플리케이션에 대한 적절한 기본 명령어로 변환되는 방식은 애플리케이션의 속도에 큰 영향을 미칩니다. 이러한 바이트 코드는 해석되거나 원시 코드로 컴파일되거나 명령어 세트 아키텍처가 바이트 코드 사양 인 프로세서에서 직접 실행될 수 있습니다. JVM (Java Virtual Machine)의 표준 구현 인 바이트 코드를 해석하면 프로그램 실행 속도가 느려집니다. 성능을 향상시키기 위해 JIT 컴파일러는 런타임에 JVM과 상호 작용하고 적절한 바이트 코드 시퀀스를 원시 기계 코드로 컴파일합니다. JIT 컴파일러를 사용할 때 하드웨어는 JVM이 동일한 바이트 코드 시퀀스를 반복적으로 해석하고 상대적으로 긴 변환 프로세스의 패널티를 초래하는 것과는 반대로 원시 코드를 실행할 수 있습니다. 이것은 실행 속도의 성능 향상으로 이어질 수 있습니다. 메소드가 덜 자주 실행되지 않는 한. JIT 컴파일러가 바이트 코드를 컴파일하는 데 걸리는 시간은 전체 실행 시간에 추가되며 JIT에 의해 컴파일 된 메서드가 자주 호출되지 않으면 바이트 코드를 실행하는 인터프리터보다 실행 시간이 길어질 수 있습니다. JIT 컴파일러는 바이트 코드를 네이티브 코드로 컴파일 할 때 특정 최적화를 수행합니다. JIT 컴파일러는 일련의 바이트 코드를 네이티브 명령어로 변환하므로 몇 가지 간단한 최적화를 수행 할 수 있습니다. JIT 컴파일러가 수행하는 일반적인 최적화 중 일부는 데이터 분석, 스택 작업에서 레지스터 작업으로의 변환, 레지스터 할당에 의한 메모리 액세스 감소, 공통 하위 표현식 제거 등입니다. JIT 컴파일러가 수행하는 최적화 수준이 높을수록 실행 단계에서 더 많은 시간을 소비합니다. 따라서 JIT 컴파일러는 실행 시간에 추가되는 오버 헤드와 프로그램에 대한 제한된보기 만 가지고 있기 때문에 정적 컴파일러가 수행하는 모든 최적화를 수행 할 여유가 없습니다.

어떻게 작동합니까?!

JIT (Just-In-Time) 컴파일러는 런타임시 Java 애플리케이션의 성능을 향상시키는 Java Runtime Environment의 구성 요소입니다. Java 프로그램은 다양한 컴퓨터 아키텍처에서 JVM이 해석 할 수있는 플랫폼 중립 바이트 코드를 포함하는 클래스로 구성됩니다. 런타임에 JVM은 클래스 파일을로드하고 각 개별 바이트 코드의 의미를 결정하고 적절한 계산을 수행합니다. 해석 중 추가 프로세서 및 메모리 사용량은 Java 애플리케이션이 원시 애플리케이션보다 느리게 수행됨을 의미합니다. JIT 컴파일러는 런타임에 바이트 코드를 원시 기계 코드로 컴파일하여 Java 프로그램의 성능을 향상시킵니다.

JIT 컴파일러는 기본적으로 사용 가능하며 Java 메소드가 호출 될 때 활성화됩니다. JIT 컴파일러는 해당 메서드의 바이트 코드를 원시 기계 코드로 컴파일하여 "적시에"실행되도록 컴파일합니다. 메소드가 컴파일되면 JVM은 해석하는 대신 해당 메소드의 컴파일 된 코드를 직접 호출합니다. 이론적으로 컴파일에 프로세서 시간과 메모리 사용이 필요하지 않은 경우 모든 메서드를 컴파일하면 Java 프로그램의 속도가 네이티브 응용 프로그램의 속도에 접근 할 수 있습니다.
JIT 컴파일에는 프로세서 시간과 메모리 사용량이 필요합니다. JVM이 처음 시작되면 수천 개의 메소드가 호출됩니다. 이러한 모든 방법을 컴파일하면 프로그램이 결국 매우 우수한 최고 성능을 달성하더라도 시작 시간에 상당한 영향을 미칠 수 있습니다.

다양한 애플리케이션을위한 다양한 컴파일러

JIT 컴파일러는 두 가지 유형으로 제공되며 사용할 컴파일러를 선택하는 것이 종종 애플리케이션을 실행할 때 수행해야하는 유일한 컴파일러 조정입니다. 사실, 어떤 컴파일러를 선택 하려는지 아는 것은 Java가 설치되기 전에도 고려해야 할 사항입니다. 다른 Java 바이너리에는 다른 컴파일러가 포함되어 있기 때문입니다.

클라이언트 측 컴파일러

잘 알려진 최적화 컴파일러는 -clientJVM 시작 옵션을 통해 활성화되는 컴파일러 인 C1 입니다. 시작 이름에서 알 수 있듯이 C1은 클라이언트 측 컴파일러입니다. 사용 가능한 리소스가 적고 대부분의 경우 애플리케이션 시작 시간에 민감한 클라이언트 측 애플리케이션을 위해 설계되었습니다. C1은 코드 프로파일 링에 성능 카운터를 사용하여 간단하고 상대적으로 방해가되지 않는 최적화를 가능하게합니다.

서버 측 컴파일러

서버 측 엔터프라이즈 Java 애플리케이션과 같은 장기 실행 애플리케이션의 경우 클라이언트 측 컴파일러로는 충분하지 않을 수 있습니다. 대신 C2와 같은 서버 측 컴파일러를 사용할 수 있습니다. C2는 일반적으로 -server시작 명령 줄에 JVM 시작 옵션  추가하여 활성화됩니다 . 대부분의 서버 측 프로그램은 오랫동안 실행될 것으로 예상되므로 C2를 활성화하면 단기 실행되는 경량 클라이언트 응용 프로그램보다 더 많은 프로파일 링 데이터를 수집 할 수 있습니다. 따라서 더 고급 최적화 기술과 알고리즘을 적용 할 수 있습니다.

계층 형 컴파일

계층 형 컴파일은 클라이언트 측 및 서버 측 컴파일을 결합합니다. 계층 형 컴파일은 JVM의 클라이언트 및 서버 컴파일러 장점을 모두 활용합니다. 클라이언트 컴파일러는 애플리케이션 시작 중에 가장 활동적이며 낮은 성능 카운터 임계 값으로 트리거 된 최적화를 처리합니다. 또한 클라이언트 측 컴파일러는 성능 카운터를 삽입하고 고급 최적화를위한 명령어 세트를 준비합니다. 이는 나중에 서버 측 컴파일러에서 처리합니다. 계층 형 컴파일은 컴파일러가 영향이 적은 컴파일러 활동 중에 데이터를 수집 할 수 있으므로 나중에 고급 최적화에 사용할 수 있으므로 매우 리소스 효율적인 프로파일 링 방법입니다. 이 접근 방식은 또한 해석 된 코드 프로필 카운터 만 사용하는 것보다 더 많은 정보를 제공합니다.

코드 최적화

컴파일을 위해 메소드를 선택하면 JVM은 JIT (Just-In-Time 컴파일러)에 바이트 코드를 제공합니다. JIT는 메소드를 올바르게 컴파일하기 전에 바이트 코드의 의미와 구문을 이해해야합니다. JIT 컴파일러가 메서드를 분석 할 수 있도록 해당 바이트 코드는 먼저 다음과 같은 내부 표현으로 재구성됩니다.trees, 이는 바이트 코드보다 기계어 코드와 더 유사합니다. 그런 다음 분석 및 최적화가 메서드의 트리에서 수행됩니다. 결국 트리는 네이티브 코드로 변환됩니다. JIT 컴파일러는 둘 이상의 컴파일 스레드를 사용하여 JIT 컴파일 작업을 수행 할 수 있습니다. 다중 스레드를 사용하면 잠재적으로 Java 애플리케이션이 더 빨리 시작될 수 있습니다. 실제로 여러 JIT 컴파일 스레드는 시스템에 사용되지 않은 처리 코어가있는 경우에만 성능 향상을 보여줍니다. 기본 컴파일 스레드 수는 JVM에 의해 식별되며 시스템 구성에 따라 다릅니다. 결과
스레드 수가 최적이 아닌 경우 XcompilationThreads옵션 을 사용하여 JVM 결정을 대체 할 수 있습니다 . 이 옵션 사용에 대한 정보는 JIT 및 AOT 명령 행 옵션을 참조하십시오.

컴파일은 다음 단계로 구성됩니다.

 

인라이닝

인라인은 더 작은 메서드의 트리를 호출자의 트리에 병합하거나 "인라인"하는 프로세스입니다. 이렇게하면 자주 실행되는 메서드 호출 속도가 빨라집니다.

 

로컬 최적화

로컬 최적화는 한 번에 코드의 작은 부분을 분석하고 개선합니다. 많은 로컬 최적화는 클래식 정적 컴파일러에서 사용되는 검증 된 기술을 구현합니다.

 

제어 흐름 최적화

제어 흐름 최적화는 메서드 (또는 메서드의 특정 섹션) 내부의 제어 흐름을 분석하고 코드 경로를 재정렬하여 효율성을 향상시킵니다.

 

글로벌 최적화

전역 최적화는 전체 방법에서 한 번에 작동합니다. 더 "비싸고"더 많은 컴파일 시간이 필요하지만 성능이 크게 향상 될 수 있습니다.

 

네이티브 코드 생성

기본 코드 생성 프로세스는 플랫폼 아키텍처에 따라 다릅니다. 일반적으로이 컴파일 단계에서 메서드의 트리는 기계어 코드 명령어로 변환됩니다. 아키텍처 특성에 따라 몇 가지 작은 최적화가 수행됩니다.

Comments