JIT Compiler(feat.C1,C2 Compiler)

김운채·2023년 5월 6일
3

TIL

목록 보기
1/22

정적 컴파일과 동적 컴파일

컴파일러에서 프로그램 코드를 기계어로 변환할 수 있는 시점은 2가지가 있다.
👉 프로그램 실행 전
👉 프로그램 실행 중

실행시점 전에 기계어로 변환 하는 컴파일러를 정적 컴파일러라고 하고,
실행중 기계어로 변환하는 컴파일러를 동적 컴파일러(인터프리터 언어)라 한다.

인터프리터
Interpreter는 컴파일러처럼 고레벨언어를 기계어(저레벨언어)로 해석해주는 번역 프로그램입니다. 컴파일러는 전체 소스코드를 보고 명령어를 수집하고 재구성하는 반면 인터프리터는 소스코드의 각 행을 연속적으로 분석하며 실행합니다. 때문에 일반적으로 각 행마다 실행하는 인터프리터보다는 컴파일러가 더 빠릅니다.
자바스크립트와 파이썬이 대표적인 인터프리터 언어고, C,C++은 컴파일언어, 자바는 컴파일러와 인터프리터 모두 해당됩니다.
출처

C, C++와 같이 실행 시점 전에 모두 컴파일(정적 컴파일)을 하게 되면 컴파일 타임에 시간이 너무 오래 소요된다는 단점이 있는 대신, 런타임에 성능이 뛰어나다는 장점이 있다.

반대로 javascript, python과 같이 실행 중에 컴파일을 하게 되면 컴파일 시간이 거의 필요없이 실행을 할 수 있게 되지만, 실행 중 리소스의 일부를 컴파일에 사용하게 되기 때문에 프로그램 성능이 떨어진다는 문제점이 있다.

초기 JVM은 인터프리터방식만 이용하여 한줄 한줄 읽기 때문에 실행속도가 느린 단점이 있었지만 JIT 컴파일러 방식을 도입해 속도를 보완했다.

JIT를 사용하는 언어에는 자바와 .NET 등이 있다. 즉, 자바에서만 사용하는 개념은 아니다.

JIT Compiler

JVM은 kt(kotlin)나 java 파일을 이용해 바이트 코드를 만들 때는 정적 컴파일러를 사용하지만, 바이트 코드를 기계어로 변환할 때는 JIT(Just In Time) Compiler라 불리는 컴파일러를 지원한다.

🤷‍♂️아니 잠깐, 그럼 javac 컴파일러는??? 갑자기 헷갈린다.

잠깐 정리하자면, javac 컴파일러가 하는 일은 java파일을 class파일(바이트코드)로 변환하는 것일 뿐이다.(정적컴파일)


출처

즉, javac 컴파일러는 어떤 OS에서도 수행할수 있도록 바이트 코드라는 파일로 만들고, 컴퓨터가 제대로 이해하기 위한 언어(기계어)로 다시 변환작업을 하는 데 이 역할을 JVM안의 JIT 컴파일러가 하는 것이다.

JIT 컴파일러의 동작

JIT컴파일러는 기본적으로 실행 중에 컴파일(동적 컴파일)을 한다. 하지만, 동적 컴파일을 하는 언어들보다 훨씬 좋은 성능을 내는데, 그 이유는 기계어로 변환된 코드를 캐시에 저장시켜 재사용시 컴파일을 다시 하지 않아도 되기 때문이다. 즉, 런타임에 바이트코드를 기계어로 변환하는 컴파일을 하면서 이미 캐시에 있는 기계어는 다시 변환하지 않고 사용하는 컴파일러가 바로 JIT 컴파일러이다. 만약 캐시된 코드를 사용한다면 마치 미리 컴파일된 코드를 사용하는 것과 같은 효과(정적 컴파일)를 내기 때문에 성능이 향상된다.

하지만 모든 코드들을 캐시하는건 아니다. JVM은 내부에서 자주 수행되는 코드들을 선별하여 캐시 공간에 넣어둔다.
실제로 JVM의 JIT 컴파일러 내부에는 2가지 컴파일러인 C1컴파일러C2컴파일러가 있다.

C1, C2 Compiler

C1 - Client Compiler
C2 - Server Compiler

C1 컴파일러는 가능한 빠른 실행 속도를 위해 코드를 가능한 빠르게 최적화하고 컴파일한다. 그 중 특정 메서드가 C1 컴파일러의 임계치 설정 이상으로 호출되면, 해당 메서드의 코드는 C1 컴파일러를 통해 제한된 수준으로 최적화된다. 그리고 컴파일된 기계어는 코드 캐시에 저장된다.

C2 컴파일러는 컴파일 시간이 C1 컴파일 시간보다 상대적으로 길지만 더 높은 수준의 최적화를 지원한다. 최적화와 컴파일이 끝나면 마찬가지로 코드 캐시에 기계어를 저장한다.

이렇게 컴파일 과정이 최적화 수준에 따라 복수개의 단계로 나눠진 것이 바로 티어드 컴파일(Tiered Compilation)이다.

Tiered Compilation

티어드 컴파일은 자바 7부터 릴리즈 되었으며 자바 8에서는 기본으로 사용할 수 있다.

JIT Tiered Compilation 은 인터프리터, C1 컴파일러, C2 컴파일러를 통해 5가지 Level로 나뉜다. Level 0은 인터프리터, Level 1 ~ 3은 C1 컴파일러, Level 4는 C2 컴파일러에 의해 수행된다.

C1 compiler : 1~3 level compilation
C2 compiler : 4 level compilation

  • Level 0 - Interpreted Code: JVM은 초기에 모든 코드를 인터프리터를 통해 실행한다. 이 단계는 앞서 살펴본것과 같이, 컴파일된 기계어를 실행하는 것보다 성능이 낮다.

  • Level 1 - Simple C1 Compiled Code: Level 1 은 JIT 컴파일러가 단순하다고 판단한 메서드에 대해 사용된다. 여기서 컴파일된 메서드들은 복잡도가 낮아, C2 컴파일러로 컴파일한다고 하더라도 성능이 향상되지 않는다. 따라서 추가적인 최적화가 필요 없으므로 프로파일링 정보도 수집하지 않는다.

  • Level 2 - Limited C1 Compiled Code: 제한된 수준으로 프로파일링과 최적화를 진행하는 단계이다. C2 컴파일러 큐가 꽉 찬경우 실행된다.

  • Level 3 - Full C1 Compiled Code: 최대 수준으로 프로파일링과 최적화를 진행한다. 즉 일반적인 상황에서 수행된다.

  • Level 4 - C2 Compiled Code: 애플리케이션의 장기적인 성능을 위해 C2 컴파일러가 최적화를 수행한다. Level 4에서 최적화된 코드는 완전히 최적화 되었다고 간주되어, 더이상 프로파일링 정보를 수집하지 않는다.

0개의 댓글