Accelerate Applications in OCI with Oracle GraalVM

8
0
Send lab feedback

Accelerate Applications in OCI with Oracle GraalVM

Introduction

Oracle GraalVM compiles your Java applications ahead of time into standalone binaries that start instantly, provide peak performance with no warmup, and use fewer resources. The core distribution of Oracle GraalVM includes the Java Development Kit (JDK), the just-in-time compiler (the Graal compiler), Native Image, and others. You can use the GraalVM JDK just like any other JDK.

In this lab you will run demo applications on the JVM and as native exectuables to compare the performance and see how Oracle GraalVM can accelerate applications on Oracle Cloud Infrastructure (OCI). Faster applications with lower resource requirements translates into fewer or smaller servers, which reduces cloud costs.

  Oracle GraalVM is available at no cost on OCI.

Lab Contents

In this lab you will:

  • Connect to a remote host and check the development environment
  • Run a JMH performance benchmark on the JVM
  • Run a Micronaut application on the JVM, turn it into a native executable and launch
  • Configure the host firewall to allow traffic to your VM instance (optional)

Estimated lab time: 30-45 minutes

NOTE: Whenever you see the laptop icon you will need to perform an action such as entering a command.

# The box under the icon will tell you what to do.

To copy a command, hover over the field and then click the copy to clipboard icon.

To paste a copied command in a terminal window, right click and select the Paste option from the context menu. If you prefer keyboard shortcuts instead, use CTRL+SHIFT+V.

Task 1: Connecting to a Remote Host and Check the Development Environment

Your development environment is provided by a remote host: an OCI Compute Instance with Oracle Linux 8, 4 cores, and 32GB of memory. The Luna Labs desktop environment will display before the remote host is ready: this can take up to two minutes. Visual Studio Code (VS Code) will launch automatically and connect to your remote development environment.

  1. A VS Code window will open and automatically connect to the VM instance that has been provisioned for you. Click Continue to accept the machine fingerprint.

    VS Code Accept

    If you do not click Continue, VS Code will present you with a dialogue box, shown below. Click Retry--VS Code will ask you to accept the machine fingerprint. Then click Continue.

    VS Code Retry Connection

    Issues With Connecting to the Remote Development Environment

    If you encounter any other issues in which VS Code fails to connect to the remote development environment that are not covered above, try the following:

    • Close VS Code
    • Double-click the "Luna-Lab.html" icon on your desktop
    • Copy the "Configure Script" from the Resources tab and paste it into the Luna Desktop Terminal again
    • Repeat the above instructions to connect to the Remote Development environment

Congratulations, you are now successfully connected to a remote host in Oracle Cloud.

The script will open VS Code--connected to your remote host—-with the source code for the lab opened.

Next, open a Terminal within VS Code. The Terminal enables you to interact with the remote host. A terminal can be opened in VS Code via the menu: Terminal > New Terminal, as shown below.

VS Code Terminal

You will use this Terminal to perform the steps described in the lab.

Note on the Development Environment

You will use Oracle GraalVM for JDK 17 as the Java environment for this lab. Your development environment comes preconfigured with Oracle GraalVM and the Native Image tooling required for this lab.

You can easily check that by running these commands in your Terminal:

java -version

native-image --version

You can proceed to the next task.

Task 2: Run Demos: JMH performance benchmark on the JVM

In this part you will run a Java benchmark to compare the performance of the Graal JIT compiler vs. the C2 JIT compiler. The Graal compiler, enabled in Oracle GraalVM by default, provides optimized performance for programs running on the JVM through unique approaches to code analysis, advanced optimizations, and performs an aggressive inlining algorithm for deep and wide class hierarchies.

The benchmark you are going to run is written with Java Microbenchmark Harness (JMH) and uses Java Stream API . It illustrates inlining and partial escape analysis the compiler performs in conjunction, which explains significant performance gains at run time.

The demo source code is available in the java-simple-stream-benchmark directory.

The microbenchmark creates a stream from array elements and maps each number using several mapping functions:

public class JavaSimpleStreamBenchmark {

  static int[] values = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

  @Benchmark
  public int testMethod() {
    return Arrays.stream(values)
      .map(x -> x + 1)
      .map(x -> x * 2)
      .map(x -> x + 2)
      .reduce(0, Integer::sum);
  }
}

The JavaSimpleStreamBenchmark.testMethod is executed with 3 iterations to allow the JIT compiler to warmup before it samples the performance. The benchmark results are printed to the console, and are in nanoseconds per operation, which means lower numbers are better.

  1. In the terminal window connected to your VM instance, go to the demo directory:

    cd java-simple-stream-benchmark
  2. Build the project:

    mvn package
  3. Run the benchmark with the Graal JIT compiler:

    java -jar target/benchmarks.jar

    By invoking the java command, you use the optimized Graal JIT compiler enabled in Oracle GraalVM by default.

  4. Run the benchmark on the same JVM (Oracle GraalVM), but use the C2 compiler instead of the Graal compiler by applying the -XX:-UseJVMCICompiler option:

    java -XX:-UseJVMCICompiler -jar target/benchmarks.jar

Compare the benchmark results after completing steps 4-5. The results, of course, will depend on the number of processors and memory of your machine. Below are the numbers taken on a compute instance with 60GB of memory and 4 cores:

Graal JIT Compiler:

[opc@demo-instance java-simple-stream-benchmark]$ java -jar target/benchmarks.jar
...
Benchmark                             Mode  Cnt   Score    Error    Units
JavaSimpleStreamBenchmark.testMethod  avgt    3  45.672  ± 1147.589 ns/op

C2 JIT Compiler:

[opc@demo-instance java-simple-stream-benchmark]$ java -XX:-UseJVMCICompiler -jar target/benchmarks.jar
...
Benchmark                             Mode  Cnt    Score   Error    Units
JavaSimpleStreamBenchmark.testMethod  avgt    3  260.061 ± 286.923  ns/op

The Graal JIT compiler average result is over 6x faster than C2's on the same benchmark!

You can proceed to the next task.

Task 3: Run Demos: Java Application as a Native Executable

This task will focus on comparing the startup times when running a Java application on a JVM and as a native executable. In both cases the microservice will run on Oracle GraalVM, but will be executed in different modes: just-in-time or ahead-of-time compiled, to demonstrate the startup boost.

This microservice is written with Micronaut , a full-stack Java framework suited for building microservices and serverless applications. It is a simple server-side rendering application, where the service, ConferenceService.java, contains a list of conferences and returns a random conference. The controller is defined with the @Controller annotation and mapped to the path /conferences to obtain a random conference name. Micronaut converts it automatically to JSON in the response.

  1. Return to the home directory:

    cd
  2. Create a Micronaut application specifying Maven or Gradle build tools, download the application sources, unzip the archive, and navigate to it.

    mkdir micronaut-demo && cd micronaut-demo

    Maven:

    curl https://guides.micronaut.io/latest/micronaut-creating-first-graal-app-maven-java.zip -o micronaut-creating-first-graal-app.zip

    Gradle:

    curl https://guides.micronaut.io/latest/micronaut-creating-first-graal-app-gradle-java.zip -o micronaut-creating-first-graal-app.zip

    It will download the Micronaut 3.8.x version.

    unzip micronaut-creating-first-graal-app.zip
  3. Build and run the application with Gradle or Maven Wrapper on the JVM (Oracle GraalVM).

    Maven:

    ./mvnw mn:run

    Gradle:

    ./gradlew run

    The application is started on port 8080. Note the time taken to start this simple Micronaut microservice.

    See the time taken to start a simple Micronaut microservice in JIT

  4. Terminate the application by typing CTRL+C.

  5. Generate a standalone native Linux executable with Native Image. You can build a native executable using Gradle or Maven.

    Maven by specifying the native-image packaging format:

    ./mvnw package -Dpackaging=native-image

    Gradle by running the Micronaut project's nativeCompile task:

    ./gradlew nativeCompile

    If you used Maven, the executable called default will be written to the project target/ directory by default, or to the build/native/nativeCompile/ directory if you used Gradle.

    Note: The time to build an executable depends on application size and complexity and may take some time on low powered VMs.

  6. Invoke the executable.

    Built with Maven:

    ./target/default

    Built with Gradle:

    ./build/native/nativeCompile/default

    Once again, note the time taken to start this application as a native executable. It starts much faster because the executable is a self-contained binary and does not require a JDK to run, making it an easy way to distribute applications. The filesize is also quite small.

    See the time taken to start a Micronaut microservice from a native executable and its file size

  7. Terminate the application by typing CTRL+C.

Native Image creates a native executable with the application classes, dependent library classes, dependent JDK classes, and a snapshot of the application heap with classes initialized at build time. Running a Java application as a native executable provides instantaneous startup, lower CPU and memory consumption, making the Oracle GraalVM runtime environment a good candidate for cloud deployments.

You have just seen the difference in startup times and can already end this lab. However, you can continue and test this server-side application running in a browser which requires configuring the host firewall to allow traffic to your VM instance. Proceed to Task 4.

Task 4: Configure the Host Firewall Allows Traffic to a VM instance (Optional)

To test the Java server-side application from the previous task in a browser, you need to ensure that the host firewall allows traffic to your virtual machine.

  1. Login to OCI Console. Open the Luna Lab page and click the OCI Console quick link. Enter username and password provided under Credentials for this lab ephemeral account.

  2. When you are logged into the OCI Console, navigate to Compute and click Instances.

    Find Compute Instances

  3. Select a necessary compartment in the Compartment dropdown on the left. To find your compartment name, return to the Luna Lab page, then click Oracle Cloud, and see the Compartment Name field.

  4. Find your VM instance in the main view and open it.

  5. In the Primary VNIC section, click on Subnet your instance is attached to.

  6. On the subnet page, click on the security list (name starting with ds-luna-seclist-).

  7. Press Add Ingress Rule and fill in the following data:

    Add Ingress Rule to allow incoming traffic to the port

    The rule allows traffic from all sources to use port 8080, so that the application can be reached from anywhere.

  8. Return to the terminal window and run the following commands to restart the firewall in your running VM instance.

    sudo firewall-cmd --permanent --add-port=8080/tcp

    sudo systemctl reload firewalld
  9. Restart the application:

    Built with Maven:

    ./target/default

    Built with Gradle:

    ./build/native/nativeCompile/default
  10. Open the application in a browser, http://<SERVER_IP>:8080/conferences/random, where the <SERVER_IP> is your instance public IP address. Because the Micronaut @Controller annotation is mapped to the /conferences path, you have to append /conferences/random path to the URL.

    http://<SERVER_IP>:8080/conferences/random

Congratulations! You have successfully completed this lab.

Learn More

To end this session, click the End Session button in the toolbar.

SSR