4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GraalVMを学びながら動した (Windows)

Last updated at Posted at 2023-03-25

初めに

  • 最近「GraalVM」について調べたのでまとめます
  • 実行環境は以下です
    • Windows 10
    • GraalVM Community Edition 22.3.1
    • Visual Studio 2022 (Native イメージのコンパイルで必要)
  • 誤り等あれば、ご指摘いただけますと幸いです

本書の対象

  • JVMやJITコンパイラなど、基本的なJavaの用語の理解が必要です
  • 不安な方は、本記事の【復習】Javaはどのように動くのか をご確認ください
    • 筆者も最初はかなり忘れていました

GraalVMとは

  • 複数のプログラム言語を高パフォーマンスで実行できるランタイムプラットフォーム。特徴は以下。
    • Graal
      • GraalVM上で動作する新しいJITコンパイラ(C2)
    • Truffle (多言語プラットフォーム)
      • Java、JavaScript、Ruby、Python 等の様々な言語を実行可能
      • ある言語から別の言語のAPIを呼び出すことも可能 (JavaからJavaScriptのAPIを呼び出す)
      • 独自の言語実装も追加できる
    • Native Image
      • AOT(Ahead Of Tim)コンパイルにより、Nativeイメージを作成できる
      • メモリ使用量が低く、起動時間も短いことから、コンテナアプリケーションとの相性が良い

なぜ、GraalVMが注目されているの?

  • CloudNativeが主流になる中、Javaの重たさや起動処理の遅さが問題視されるようになった
  • GraalVMの出現により、Javaのデメリットを解消できるのではと期待されている

環境構築

1. GraalVMのインストール

  • GraalVMインストールからGraalVMをインストールする。
    • 筆者の場合は、Windows (amd64) × Java 17 を選択
    • ダウンロードされるZIPファイルをC:\Program Files\graalvmに展開
    • 環境変数PATH/JAVAHOMEを以下のように設定
      • PATH に C:\Program Files\graalvm\bin を追加
      • JAVAHOMEにC:\Program Files\graalvmを設定
    • コマンドプロンプトにjava --versionと入力し、GraalVMのJVMのバージョンが表示される事を確認する
    openjdk 17.0.6 2023-01-17
    OpenJDK Runtime Environment GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13)
    OpenJDK 64-Bit Server VM GraalVM CE 22.3.1 (build 17.0.6+10-jvmci-22.3-b13, mixed mode, sharing
    
    • コマンドプロンプトにnative-image --versionと入力し、GraalVMのNativeイメージのバージョンが表示されることを確認する
    GraalVM 22.3.1 Java 17 CE (Java Version 17.0.6+10-jvmci-22.3-b13)
    

2. Visual Studio のインストール

  • GraaalVMでNativeイメージを作成するために必要
  • Visual Studio 2022から「Community Edition」を選択してインストーラをDLする
  • インストーラの[デスクトップとモバイル ] -> [C++ によるデスクトップ開発] を選択してインストール
  • インストール完了後、OSを再起動

動作確認

動作確認用のプログラムを作成する

  • 素数を計算するプログラム。処理量を多くしたいので、最適化はしてません
public class Sample {
	private static final long MAX_SIZE = 200000;
	public static void main(String[] args) {
		int counter = 1;
		int number = 1;
		int index = 1;
		while (counter < MAX_SIZE) {
			boolean hit = true;
			if (counter > 2) {
				for (int index1 = 2; index1 < counter; index1++) {
					if (counter % index1 == 0) {
						hit = false;
						break;
					}
				}
			}
			if (hit) {
				number = counter;
				index++;
				System.out.println(String.format("%d番目の素数は%d", index, number));
			}
			counter++;
		}
	}
}

GraalVMでJavaを実行する

  • 以下のコマンドを実行する
    java -XX:+CITime Sample.java
    
  • 結果は以下の通り。C2コンパイラの表記がGraalVMのJVMCIに変化している
    Individual compiler times (for compiled methods only)
    ------------------------------------------------
    C1 {speed: 164062.898 bytes/s; standard:  0.594 s, 97329 bytes,       ・・・略
    JVMCI-native {speed: 47031.351 bytes/s; standard:  0.425 s, 21046 bytes, ・・・略 ★
    ・・略・・
    
    OpenJDK17の場合
    Individual compiler times (for compiled methods only)
    ------------------------------------------------
    C1 {speed: 209886.496 bytes/s; standard:  0.383 s, 80375 bytes,    ・・・略
    C2 {speed: 23532.082 bytes/s; standard:  0.753 s, 18235 bytes,     ・・・略 ★
    ・・略・・
    

Java以外の言語を実行する

  • GraalVMでは、Java以外のコードを実行したり、Javaから別言語のコードを実行できる。
  • 詳しくは、VSCodeでGraalVMを動かす にまとめているのでご確認ください。

Native Image を作って実行する

  • AOTコンパイラによりJavaをNativeイメージにすることで、VMと比較して高速起動できる
    • AOTコンパイラ:Ahead-Of-Timeコンパイラの略。アプリ実行前に事前にプログラムを機械語に変換する
  1. Jarを作成する

    • サンプルプログラムをJarファイル化する。方法はたくさんあるけど、今回はコマンドで行う
    • コンソールに以下を実行
    javac -d classes Sample.java
    
    • classesフォルダにsample.classが作成されていることを確認
    • MANIFEST.MFを作成し、以下を記述する。必ず改行すること
    Main-Class: Sample
    
    • 以下を実行し、実行後にapp-1.0.0.jarが作成されていることを確認
    jar cvfm app-1.0.0.jar MANIFEST.MF -C classes .
    
    • 以下を実行し、正常にJarが作成されていることを確認
    java -jar app-1.0.0.jar
    
  2. Visual Studio 2022 Developerコンソールを開く

    • このコンソールで実行しないと、Native イメージの作成時に必要なモジュールが参照できず、エラーになるので注意
  3. 以下のコマンドを入力

    native-image -jar app-1.0.0.jar
    

    実行後、下記のように表示され、app-1.0.0.exeが作成されることを確認する

    ========================================================================================================================
    GraalVM Native Image: Generating 'app-1.0.0' (executable)...
    ========================================================================================================================
    [1/7] Initializing...                                                                                    (7.5s @ 0.11GB)
     Version info: 'GraalVM 22.3.1 Java 17 CE'
     Java version info: '17.0.6+10-jvmci-22.3-b13'
     C compiler: cl.exe (microsoft, x64, 19.35.32215)
     Garbage collector: Serial GC
    [2/7] Performing analysis...  [*****]                                                                   (13.4s @ 0.67GB)
       2,799 (74.20%) of  3,772 classes reachable
       3,376 (50.81%) of  6,644 fields reachable
      12,424 (43.11%) of 28,818 methods reachable
          27 classes,     0 fields, and   313 methods registered for reflection
          62 classes,    53 fields, and    52 methods registered for JNI access
           1 native library: version
    [3/7] Building universe...                                                                               (1.9s @ 1.14GB)
    [4/7] Parsing methods...      [*]                                                                        (1.5s @ 0.62GB)
    [5/7] Inlining methods...     [***]                                                                      (0.9s @ 1.00GB)
    [6/7] Compiling methods...    [***]                                                                     (11.5s @ 0.80GB)
    [7/7] Creating image...                                                                                  (2.2s @ 1.20GB)
       4.14MB (36.82%) for code area:     7,124 compilation units
       6.92MB (61.58%) for image heap:   96,905 objects and 5 resources
       184.09KB ( 1.60%) for other data
       11.23MB in total
    ------------------------------------------------------------------------------------------------------------------------
    Top 10 packages in code area:                               Top 10 object types in image heap:
     665.50KB java.util                                          908.06KB java.lang.String
     332.85KB java.lang                                          890.88KB byte[] for code metadata
     266.91KB java.text                                          888.33KB byte[] for general heap data
     218.85KB java.util.regex                                    615.44KB java.lang.Class
     196.40KB java.util.concurrent                               541.42KB byte[] for java.lang.String
     149.10KB java.math                                          439.13KB java.util.HashMap$Node
     127.51KB com.oracle.svm.core.code                           220.28KB char[]
     120.48KB com.oracle.svm.core.genscavenge                    218.67KB com.oracle.svm.core.hub.DynamicHubCompanion
     117.19KB java.lang.invoke                                   212.44KB java.util.HashMap$Node[]
     104.38KB java.util.logging                                  160.30KB java.lang.String[]
       1.84MB for 111 more packages                                1.53MB for 765 more object types
    ------------------------------------------------------------------------------------------------------------------------
                        0.7s (1.8% of total time) in 20 GCs | Peak RSS: 2.64GB | CPU load: 5.34
    ------------------------------------------------------------------------------------------------------------------------
    Produced artifacts:
         app-1.0.0.build_artifacts.txt (txt)
         app-1.0.0.exe (executable)
    ========================================================================================================================
    Finished generating 'app-1.0.0' in 40.7s.
    
  4. 以下を入力し、Native イメージ化されたexeで同様の処理が実行できる事を確認する

    app-1.0.0.exe
    

【復習】Javaはどのように動くのか

あらためて、Javaはどのように動作するのか復習します。

  1. 人間がプログラムを書く。
  2. コンパイラがプログラムをバイトコード(中間ファイル)に変更する
    • javacコマンドにより生成される.classファイル
  3. JVMがバイトコードを実行する
    • JVMはOSごとに用意されるがバイトコードはOSに依存しない。このため、JavaはOSに依存しない言語とされている
    • JVMがバイトコードを実行する時は、後述のインタプリタとJITコンパイルの両方で実行される

インタプリタ

  • バイトコードを逐次実行する方法
  • 逐次実行のため遅い
  • そのため頻出する処理はJITコンパイルラで処理する

JITコンパイラ

  • Just in time コンパイラの略で、頻出するバイトコードをOSの機械語に変換して処理する方法
  • インタプリタと比較して高速だが、コンパイル自体の時間がオーバーヘッドになる
  • JITコンパイラは以下の2種類
    種類 タイミング 最適化度合い コンパイル時間
    クライアントコンパイラ(C1) 初期段階でコンパイル 低い 早い
    サーバーコンパイラ(C2) 必要な最適化情報を集めてからコンパイル 高い 遅い
  • Java8移行、C1とC2を併用する階層型コンパイラがデフォルトになっている

動作確認

  • GraalVMの動作確認でも利用した素数を計算するコードを以下の条件で実行して、処理時間を比較した。

    条件 処理時間 実行コマンド
    インタプリタのみ 17.87秒 java -Xint Sample.java
    インタプリタ+階層型コンパイル 4.89秒 java -XX:+CITime Sample.java
  • 階層型コンパイルが有効の場合、逐次実行するインタプリタのみと比較してかなり処理時間が早い

用語

JVM

  • Java Visual Machineの略
  • Javaで作成されたプログラムを、Windows / MacOS / LinuxのOS上で実行するもの

JRE:

  • Java Runtime Environment の略
  • Javaで作られたプログラムを実行するために必要なソフト
  • JVMはJREに含まれる

JDK:

  • Java Development Kit の略
  • Javaで開発・実行するための総合的なツール群。以下を含む
    • ソースコードをコンパイルをするツール
    • コードをデバッグするツール
  • JREもJDKの一部

引用

https://tech.uzabase.com/entry/2020/01/27/090000
https://speakerdeck.com/oracle4engineer/graalvm-whats-new?slide=55

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?