Accelerate Applications in Oracle Cloud with GraalVM Enterprise

8
0
Send lab feedback

Accelerate Applications in Oracle Cloud with GraalVM Enterprise

Introduction

GraalVM is a high performance JDK distribution, built on the global standard for application development. It is designed to accelerate execution of applications written in Java and other JVM languages. GraalVM is available in two editions: Community and Enterprise. For the purpose of this lab, you will use GraalVM Enterprise .

GraalVM offers two ways to run a Java application: on a HotSpot JVM or as an ahead-of-time compiled native exectuable. When running on the HotSpot JVM, GraalVM employs the Graal compiler as the top-tier just-in-time (JIT) compiler. The compiler performs advanced optimization and applies aggressive inlining techniques to accelerate performance of any application, without any code changes. Running as a native exectuable GraalVM uses the Native Image technology to transform a Java application into a self-contained native executable that does not require a JVM to run, starts up instantly, and delivers peak performance with no warm up time.

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

  GraalVM Enterprise is included in the Oracle Java SE Subscription and available at no cost on Oracle Cloud Infrastructure (OCI).

Lab Contents

In this lab you will:

  • Connect to a virtual machine in Oracle Cloud to complete the steps. GraalVM Enterprise will be pre-installed for you
  • 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: Connect to a Virtual Machine in Oracle Cloud

When you start the lab, all necessary resources are provisioned for you in the background: Virtual Cloud Network (VCN), Compute Instance with the Oracle Linux 8 pre-built image. Resources provisioning also includes installing and configuring the GraalVM Enterprise runtime environment with Native Image to keep this lab concise and save your time. It can take ~5 minutes to complete provisioning. You can tell when the resources are fully provisioned and ready by consulting the Resources tab on the Luna Lab webpage (see step 1, below).

  1. Double-click the Luna Lab icon on the desktop to open the browser.

    Wait until the animated gear besides Resources turns into a checkmark. It means all the required compute and network resources are provisioned, and you can proceed.

  2. Click the Resources tab in the Luna Lab page.

  3. Copy the Public IP address from the SERVER_IP box. You many need to click on View Details. To copy hover over the field and then click the Copy to clipboard icon.

  4. Minimize the browser window so you can see the Luna Desktop. Click the Applications menu and open a Terminal Emulator.

  5. Enter SSH connection command where <SERVER_IP> is your VM instance public IP address:

    ssh opc@<SERVER_IP>

    Accept the ECDSA key fingerprint by typing yes at the prompt.

You are now connected to a VM instance in Oracle Cloud. Your VM instance is already preconfigured with GraalVM Enterprise and the Native Image tool required for this lab. You can easily check that by running these commands:

java -version

native-image --version

If you wish to learn how to install GraalVM Enterprise and its features on Oracle Linux yourself, run the Get Started with GraalVM on Oracle Linux lab upon this lab completion.

You can proceed to the next task.

Task 2: Run Demos: Java Microbenchmark Harness (JMH)

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 GraalVM Enterprise 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 GraalVM Enterprise by default.

  4. Run the benchmark on the same JVM (GraalVM Enterprise), 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  53.474 ? 1236.199  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  361.844 ? 37.120  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 GraalVM Enterprise, 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.0.x version.

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

    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 micronautguide 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/micronautguide

    Built with Gradle:

    ./build/native/nativeCompile/micronautguide

    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.

GraalVM 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 GraalVM Enterprise 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/micronautguide

    Built with Gradle:

    ./build/native/nativeCompile/micronautguide
  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.

2022-11-29T11:57:07.602Z