【５分で終わるワークショップ】 GraalVM AOTを触ってみよう！

＜命題＞
GraalVM(AOT)機能を使い、CPU負荷が掛かるJavaプログラムを、ネイティブイメージ化して、JITモードで走らす場合とスループットやCPU利用率を比較してみよう！

＜やり方＞
1.	まず、GraalVMをダウンロードしましょう！
ダウンロードサイドはこちら。
→ https://www.oracle.com/jp/java/technologies/downloads/#graalvmjava21

Tips ：　GraalVM for JDK21かJDK17をDLしてください。
ご自分のOSとCPUに適合したバージョンをDLしてください。
 

２． 通常のJDKと同様に、パスを通しましょう！
GraalVMに切り替わっている事を確認しましょう。(下記は、GraalVM for JDK17の例)
```
testuser_jp@testuser_jp-mac ~ % java -version
java version "17.0.8" 2023-07-18 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 17.0.8+9.1 (build 17.0.8+9-LTS-jvmci-23.0-b14, mixed mode, sharing)
testuser_jp@testuser_jp-mac ~ % 
```

３．　負荷を掛けるJavaプログラムの例です。
任意のデイレクトリーのファイル数とサイズを算出するプログラムです。
これを、JITモードで動かした場合と、ネイティブイメージ化した上で動かした場合を比較します。

```ListDir.java
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class ListDir {
  public static void main(String[] args) throws java.io.IOException {
    String root = ".";
    if(args.length > 0) {
            root = args[0];
    }
    System.out.println("Walking path: " + Paths.get(root));
    long[] size = {0};
    long[] count = {0};
    try (Stream<Path> paths = Files.walk(Paths.get(root))) {
            paths.filter(Files::isRegularFile).forEach((Path p) -> {
                    File f = p.toFile();
                    size[0] += f.length();
                    count[0] += 1;
            });
    }
    System.out.println("Total: " + count[0] + " files, total size = " + size[0] + " bytes");
  }
}
```

４．　クラスファイルを作成します。
```
testuser_jp@testuser_jp-mac graalvm % javac ListDir.java
```

5．　ネイティブイメージ化します。
クラスファイルに対して、”native-image クラスファイル名”とする事で、ネイティブイメージ化が終了します。(数十秒、完了まで掛かります。）

```
testuser_jp@testuser_jp-mac graalvm % native-image ListDir
==========================================================================
GraalVM Native Image: Generating 'listdir' (executable)...
==========================================================================
[1/8] Initializing...                                            (6.9s @ 0.16GB)
 Java version: 17.0.8+9-LTS, vendor version: Oracle GraalVM 17.0.8+9.1
 Graal compiler: optimization level: 2, target machine: armv8-a, PGO: off
 C compiler: cc (apple, arm64, 15.0.0)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
[2/8] Performing analysis...  [***]                              (3.5s @ 0.23GB)
   1,872 (59.79%) of  3,131 types reachable
   1,744 (46.86%) of  3,722 fields reachable
   7,831 (36.27%) of 21,588 methods reachable
     642 types,     0 fields, and   288 methods registered for reflection
      49 types,    33 fields, and    48 methods registered for JNI access
       4 native libraries: -framework Foundation, dl, pthread, z
[3/8] Building universe...                                       (0.6s @ 0.28GB)
[4/8] Parsing methods...      [*]                                (0.7s @ 0.21GB)
[5/8] Inlining methods...     [***]                              (0.3s @ 0.29GB)
[6/8] Compiling methods...    [***]                              (8.1s @ 0.43GB)
[7/8] Layouting methods...    [*]                                (0.4s @ 0.55GB)
[8/8] Creating image...       [*]                                (1.0s @ 0.28GB)
   2.57MB (41.23%) for code area:     3,567 compilation units
   3.45MB (55.36%) for image heap:   49,270 objects and 1 resources
 217.73kB ( 3.41%) for other data
   6.24MB in total
--------------------------------------------------------------------------------
Top 10 origins of code area:            Top 10 object types in image heap:
   1.35MB java.base                      511.54kB byte[] for code metadata
   1.05MB svm.jar (Native Image)         417.76kB byte[] for java.lang.String
  66.98kB com.oracle.svm.svm_enterprise  328.76kB java.lang.String
  27.29kB org.graalvm.nativeimage.base   293.92kB java.lang.Class
  22.11kB org.graalvm.sdk                255.48kB byte[] for general heap data
  16.06kB jdk.internal.vm.ci             149.91kB java.util.HashMap$Node
  13.77kB jdk.internal.vm.compiler       111.71kB char[]
   4.62kB ListDir                         79.03kB java.lang.Object[]
   1.76kB jdk.proxy3                      73.13kB c.o.s.c.h.DynamicHubCompanion
   1.72kB jdk.proxy1                      70.82kB byte[] for reflection metadata
   1.48kB for 2 more packages            452.61kB for 507 more object types
--------------------------------------------------------------------------------
Recommendations:
 PGO:  Use Profile-Guided Optimizations ('--pgo') for improved throughput.
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
 QBM:  Use the quick build mode ('-Ob') to speed up builds during development.
 BRPT: Try out the new build reports with '-H:+BuildReport'.
--------------------------------------------------------------------------------
    0.8s (3.6% of total time) in 71 GCs | Peak RSS: 1.07GB | CPU load: 4.39
--------------------------------------------------------------------------------
Produced artifacts:
 /Users/testuser_jp/Desktop/code/graalvm/listdir (executable)
================================================================================
Finished generating 'listdir' in 21.7s.
```

６．　JITモードで動かした場合と、ネイティブイメージで動かした場合のスループットとCPU使用率を比較します。
```
testuser_jp@testuser_jp-mac graalvm % time java ListDir   
Walking path: .
Total: 9072 files, total size = 5529902980 bytes
java ListDir  0.29s user 0.22s system 153% cpu 0.333 total

testuser_jp@testuser_jp-mac graalvm % time ./ListDir
Walking path: .
Total: 9072 files, total size = 5529902980 bytes
./ListDir  0.02s user 0.18s system 94% cpu 0.219 total
testuser_jp@testuser_jp-mac graalvm % 
```

＜結論＞
CPU利用率は、38％以上の効率化。
スループットは、34％以上の向上。

西川 (yojiro.nishikawa@oracle.com)

本件も含め、Javaに関する事なら、何でもお気軽にご相談ください！