> Java > java지도 시간 > 본문

Graal 살펴보기: Java용 차세대 JIT 컴파일

WBOY
풀어 주다: 2024-08-31 16:36:32
원래의
938명이 탐색했습니다.

Graal 컴파일러는 동적 Just-In Time(JIT) 컴파일 기술에서 획기적인 발전을 이루었습니다. Java의 인상적인 성능 뒤에 중요한 요소로 알려진 Java Virtual Machine(JVM) 아키텍처 내 JIT 컴파일의 역할과 기능은 복잡하고 다소 불투명한 특성으로 인해 종종 많은 실무자를 당혹스럽게 만듭니다.

JIT 컴파일러란 무엇입니까?

javac 명령을 실행하거나 IDE를 사용하면 Java 프로그램이 Java 소스 코드에서 JVM 바이트 코드로 변환됩니다. 이
프로세스는 원래 소스 코드보다 훨씬 간단하고 컴팩트한 형식인 Java 프로그램의 바이너리 표현을 생성합니다.

그러나 컴퓨터나 서버에 있는 기존 프로세서는 JVM 바이트코드를 직접 실행할 수 없습니다. 이를 위해서는 JVM이 바이트코드를 해석해야 합니다.

Exploring Graal: Next-Generation JIT Compilation for Java

그림 1 – JIT(Just-In-Time) 컴파일러 작동 방식

통역사는 종종 실제 프로세서에서 실행되는 네이티브 코드에 비해 성능이 저하될 수 있으며, 이는 JVM이 런타임에 다른 컴파일러인 JIT 컴파일러를 호출하도록 동기를 부여합니다. JIT 컴파일러는 바이트코드를 프로세서가 직접 실행할 수 있는 기계어 코드로 변환합니다. 이 정교한 컴파일러는 다양한 고급 최적화를 실행하여 고품질 기계어 코드를 생성합니다.

이 바이트코드는 중간 계층 역할을 하여 Java 애플리케이션이 다양한 프로세서 아키텍처를 갖춘 다양한 운영 체제에서 실행될 수 있도록 합니다. JVM 자체는 이 바이트코드 명령어를 명령어별로 해석하는 소프트웨어 프로그램입니다.

Graal JIT 컴파일러 - Java로 작성되었습니다.

JVM의 OpenJDK 구현에는 클라이언트 컴파일러(C1)와 서버 컴파일러(C2 또는 Opto)라는 두 가지 기존 JIT 컴파일러가 포함되어 있습니다. 클라이언트 컴파일러는 더 빠른 작업과 덜 최적화된 코드 출력에 최적화되어 있어 확장된 JIT 컴파일 일시 중지로 인해 사용자 경험이 중단될 수 있는 데스크톱 애플리케이션에 이상적입니다. 반대로, 서버 컴파일러는 고도로 최적화된 코드를 생성하는 데 더 많은 시간을 할애하도록 설계되어 장기 실행 서버 애플리케이션에 적합합니다.

'계층 컴파일'을 통해 두 컴파일러를 동시에 사용할 수 있습니다. 처음에는 코드가 C1을 통해 컴파일되고, 실행 빈도가 추가 컴파일 시간을 정당화하는 경우 C2가 이어집니다.

C++로 개발된 C2는 고성능 특성에도 불구하고 본질적인 단점이 있습니다. C++는 안전하지 않은 언어입니다. 따라서 C2 모듈의 오류로 인해 전체 VM이 충돌할 수 있습니다. 상속된 C++ 코드의 복잡성과 견고성으로 인해 유지 관리 및 확장성이 중요한 과제가 되었습니다.

Graal의 고유한 이 JIT 컴파일러는 Java로 개발되었습니다. 컴파일러의 주요 요구 사항은 JVM 바이트코드를 받아들이고 기계어 코드를 출력하는 것입니다. 이는 C나 C++와 같은 시스템 수준 언어가 필요하지 않은 높은 수준의 작업입니다.

Java로 작성된 Graal은 다음과 같은 몇 가지 이점을 제공합니다.

  • 향상된 안전성: Java의 가비지 수집 및 관리형 메모리 접근 방식은 JIT 컴파일러 자체의 메모리 관련 충돌 위험을 제거합니다.

  • 간편한 유지 관리 및 확장: Java 코드베이스는 개발자가 JIT 컴파일러에 기여하고 기능을 확장하는 데 더 쉽게 접근할 수 있습니다.

  • 이식성: Java의 플랫폼 독립성은 Graal JIT 컴파일러가 Java Virtual Machine이 있는 모든 플랫폼에서 작동할 수 있다는 의미입니다.

JVM 컴파일러 인터페이스(JVMCI)

JVMCI(JVM 컴파일러 인터페이스)는 JVM(JEP 243: https://openjdk.org/jeps/243)의 혁신적인 기능이자 새로운 인터페이스입니다.
Java 주석 처리 API와 마찬가지로 JVMCI는 사용자 정의 Java JIT 컴파일러의 통합도 허용합니다.

JVMCI 인터페이스는 바이트에서 바이트[]까지의 순수 기능으로 구성됩니다.

interface JVMCICompiler {

  byte[] compileMethod(byte[] bytecode);
}
로그인 후 복사

실제 시나리오의 복잡성을 완전히 포착하지는 못합니다.

실제 응용 프로그램에서는 코드가 어떻게 작동하는지 더 잘 이해하기 위해 로컬 변수 수, 스택 크기, 인터프리터의 프로파일링에서 수집한 데이터와 같은 추가 정보가 필요한 경우가 많습니다. 따라서 인터페이스는 더 복잡한 입력을 사용합니다. 바이트코드 대신 CompilationRequest를 허용합니다.

public interface JVMCICompiler {

  int INVOCATION_ENTRY_BCI = -1;

  CompilationRequestResult compileMethod(CompilationRequest request);
}
로그인 후 복사

JVMCICompiler.java

CompilationRequest는 어떤 JavaMethod가 컴파일용인지, 잠재적으로 컴파일러에 필요한 훨씬 더 많은 데이터와 같은 보다 포괄적인 정보를 캡슐화합니다.

CompilationRequest.java

This approach has the benefit of providing all necessary details to the custom JIT-compiler in a more organized and contextual manner. To create a new JIT-compiler for the JVM, one must implement the JVMCICompiler interface.

Ideal Graph

An aspect where Graal truly shines in terms of performing sophisticated code optimization is in its use of a unique data structure: the program-dependence-graph, or colloquially, an "Ideal Graph".

The program-dependence-graph is a directed graph that presents a visual representation of the dependencies between individual operations, essentially laying out the matrix of dependencies between different parts of your Java code.

Let's illustrate this concept with a simple example of adding two local variables, x and y. The program-dependence-graph for this operation in Graal's context would involve three nodes and two edges:

  • Nodes:

    • Load(x) and Load(y): These nodes represent the operations of loading the values of variables x and y from memory into registers within the processor.
    • Add: This node embodies the operation of adding the values loaded from x and y.
  • Edges:

    • Two edges would be drawn from the Load(x) and Load(y) nodes to the Add node. These directional paths convey the data flow. They signify that the values loaded from x and y are the inputs to the addition operation.
      +--------->+--------->+
      | Load(x)  | Load(y)  |
      +--------->+--------->+
                 |
                 v
              +-----+
              | Add |
              +-----+
로그인 후 복사

In this illustration, the arrows represent the data flow between the nodes. The Load(x) and Load(y) nodes feed their loaded values into the Add node, which performs the addition operation. This visual representation helps Graal identify potential optimizations based on the dependencies between these operations.

This graph-based architecture provides the Graal compiler with a clear visible landscape of dependencies and scheduling in the code it compiles. The program-dependence-graph not only maps the flow of data and relationships between operations but also offers a canvas for Gaal to manipulate these relationships. Each node on the graph is a clear candidate for specific optimizations, while the edges indicate where alterations would propagate changes elsewhere in the code - both aspects influence how Graal optimizes your program's performance.

Visualizing and analyzing this graph can be achieved through a tool called the IdealGraphVisualizer, or IGV. This tool is invaluable in understanding the intricacies of Graal's code optimization capabilities. It allows you to pinpoint how specific parts of your code are being analyzed, modified, and optimized, providing valuable insights for further code enhancements.

Let's consider a simple Java program that performs a complex operation in a loop:

public class Demo {
 public static void main(String[] args) {
        for (int i = 0; i < 1_000_000; i++) {
            System.err.println(complexOperation(i, i + 2));
        }
    }

    public static int complexOperation(int a, int b) {
        return ((a + b)-a) / 2;
    }
}
로그인 후 복사

When compiled with Graal, the Ideal Graph for this program would look something like this(Figure 2).

Exploring Graal: Next-Generation JIT Compilation for Java

Figure 2 – Graal Graphs

Therefore, along with its method level optimizations and overall code performance improvements, this graph-based representation constitutes the key to understanding the power of the Graal compiler in optimizing your Java applications

In Conclusion

The Graal JIT compiler represents a significant leap forward in Java performance optimization. Its unique characteristic of being written in Java itself offers a compelling alternative to traditional C-based compilers. This not only enhances safety and maintainability but also paves the way for a more dynamic and adaptable JIT compilation landscape.

The introduction of the JVM Compiler Interface (JVMCI) further amplifies this potential. By allowing the development of custom JIT compilers in Java, JVMCI opens doors for further experimentation and innovation. This could lead to the creation of specialized compilers targeting specific needs or architectures, ultimately pushing the boundaries of Java performance optimization.

In essence, Graal and JVMCI represent a paradigm shift in JIT compilation within the Java ecosystem. They lay the foundation for a future where JIT compilation can be customized, extended, and continuously improved, leading to even more performant and versatile Java applications.

위 내용은 Graal 살펴보기: Java용 차세대 JIT 컴파일의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿