-
JIT compiler & GraalVM in javaDEV 2023. 12. 3. 09:28
자바 성능향상을 위한 시도들. JIT compiler부터 GraalVM까지
JIT(Just-In-Time) Compiler
- JVM의 일부, 자바를 고성능으로 만드는데 큰 역할
- java는 byte code로 컴파일, 컴파일 한 byte code를 JVM이 런타임시 native code로 변환하여 실행
- 처음에는 인터프린터 방식으로 바이트 코드 실행
- 인터프린터 사용 → 초기 실행 속도는 빠를 수 있지만, 컴파일러 보다 성능 ⬇️
- JIT compiler 두 가지 방식을 모두 사용
- JVM에서 처음 호출되자마자 컴파일되는 것은 아니고, 메서드 호출마다 횟수를 기록, 특정 수치를 초과하면 컴파일(warm up → C1, C2 컴파일 이후)
- C1, C2 컴파일러 C++로 작성
- C1: 초기 실행 속도에 중점, 빨리 코드를 최적화 컴파일
- C2: 실행 중 성능에 중점, 오랜 시간 코드를 분석 더 나은 최적화
- 인터프린터, 컴파일러의 장점을 모두 사용한다고 했지만, 자바의 구조적인 이슈 때문에 시작속도가 늦다. → warm up이 필요
구조적인 이슈?
- JVM
- 자바 이전의 모든 컴퓨터 프로그램은 OS에 맞게 작성, 메모리 관리 → 개발자
- write once, run anywhere - 물리머신 ← JDK, tomcat, apache,,, 커멘드라인으로 설치
- docker(container) JDK의 위치가 애매..
- docker에서 base image 설정 가능 → 굳이 JVM 써야 하는가?
GraalVM
- Java Runtime
- Oracle Java, Open JDK 확장한 오라클이 만들고 있는 차세대 자바
- Licence
- Community edition → Open JDK 확장 → free
- 기존 Oracle Java 보다 15% 성능 ☝🏻
- Enterprise edition → Oracle Java 확장 → Oracle Java Licence
- GraalVM Community endition 보다 10% 성능 👆🏻
Spring Native
- Spring Native documentation
- GraalVM Native Image 컴파일러를 사용 → 네이티브 실행 파일로 컴파일 지원
- .java → compile → .class → JVM → native code ❌
- .java → compile → 실행파일 → GraalVM 👌🏻 - SpringBoot 3.0에서 공식 지원
GraalVM 목표
1. 성능 개선 - JIT Compiler - Graal
- 현재까지 만들어진 JIT compiler의 성능향상이 어렵다고 판단
- C2 컴파일러를 Java로 구현 → Graal
- Graal: 고성능 JIT 컴파일러 - 기존 C++로 작성한 C2보다 코드 최적화 성능 ☝🏻
- JDK를 GraalVM으로 변경하는 것만으로도 성능향상 효과
- app 구동시간이 단축되는 것이 아니고, 기존 C2의 성능 향상
- warm up이후 성능향상
- 기존 프로젝트를 GraalVM으로만 변경하여 JIT모드로 돌려도
→ sprinng initialize 시간은 변함이 없었다.
→ 기존 JIT 컴파일러에서 C2 컴파일러 부분만 변경되었기 때문에 변함이 없는 건 당연함
→ 기존 JVM과 GraalVM의 성능테스트를 비교한다면 C2컴파일러 부분의 최적화가 GraalVM에서 더 잘 이루어졌기 때문에
→ peek throuput, latency에서 더 좋은 성능을 관찰할 수 있을 듯 - JVMCI는 JDK 9부터 OpenJDK의 일부
- XX:+UnlockExperimentalVMOptions
-XX:+EnableJVMCI -XX:+UseJVMCICompiler
왜 자바로 작성했더니 더 빨라졌는가?
- Understanding How Graal Works - a Java JIT Compiler Written in Java
- C2는 C++로 작성 → 본질적으로 문제는 없지만, C++의 오류로 VM이 충돌할 수 있다.
→ 아마도 코드의 나이 때문(유지관리, 확장) → 개발이 너무 어려워서.. - 컴파일러가 하는 일은 프로그램을 사용하고 조작하는 것 → 이렇게 하려면 일종의 데이터 구조를 사용해 프로그램을 나타내야 함 → Graal은 그래프를 사용해 프로그램을 나타냄
int average(int[] values) { int sum = 0; for (int n = 0; n < values.length; n++) { sum += values[n]; } return sum / values.length; }
- 기존 C2도 유사한 데이터 구조를 사용했기 때문에 Graal이 혁신이라고 할 수 없지만 데이터 구조를 최적화하고 컴파일할 때 JAVA의 이점은 그래프 구조가 객체지향 언어와 완벽하게 호환되기 때문
2. 네이티브 이미지 컴파일 → AOT(Ahead of Time)
- 사전 컴파일
- JIT에서 하는 작업들을 최소화하자.
- 자바 class파일을 실행 파일로 만들 수 있는 컴파일러
- 실행파일만 만들고 docker container에서 실행 → 컨테이너 사이즈 🔽
- 초기 실행 속도 ⬆️
- 단점 : 네이티브 이미지를 만드는 컴파일 시간이 길다
- AOT가 JIT의 상위 호환 같지만 아직은 특징이 다르다
- 오랫동안 고성능으로 운영돼야 하는 app → JIT
- 빠르게 실행하고 빠르게 종료해야 하는 app → AOT
- AOT의 Throghput, Latency가 JIT보다 낮은 이유
→ JIT 컴파일러에서 환경, 상황에 맞는 머신코드가 만들어져 실행되기 때문
GraalVM AOT의 최종 목표
How Throughput, Latency?
- 인터프린터가 만든 프로파일 정보를 이용, 상황에 맞는 profile정보 생성
→ 프로파일 정보가 안정화 → warm up이 완료 - AOT 컴파일 시 profile 정보를 가지고 있다면 JIT모드처럼 코드 최적화를 할 수 있고 생성되는 머신코드가 더 좋아지지 않을까?
Entierprise edition 성능이 더 좋은 이유
- AOT 컴파일 시 profile을 넣어주는 옵션은 Enterprise edition만 제공
- JIT 컴파일러도 최적화 알고리즘이 Enterprise edition에 25% 정도 더 많다.
GraalVM Native image(AOT), GraalVM JIT, JDK12
- Native image(AOT)는 실행 초기부터 높은 throuput을 제공
- JIT은 실행 초기 부터 warm up 되면서 c2 compile이후 기존 JDK, AOT보다 15% 정도 높은 처리량을 보여준다.
Reference
728x90'DEV' 카테고리의 다른 글
Virtual Thread & Structured Concurrency & Coroutine (0) 2023.12.03 LangChain에 대하여 (1) 2023.12.03 Spring batch jobScope, stepScope (0) 2023.12.02 Elasticsearch에서의 relation (1) 2023.12.02 Spring batch integration test (feat.Elasticsearch) (0) 2023.12.02