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.
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.
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.
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.
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.
In the terminal window connected to your VM instance, go to the demo directory:
cd java-simple-stream-benchmark
Build the project:
mvn package
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.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.
Return to the home directory:
cd
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
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.
Terminate the application by typing
CTRL+C
.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.
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.
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.
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.
When you are logged into the OCI Console, navigate to Compute and click Instances.
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.
Find your VM instance in the main view and open it.
In the Primary VNIC section, click on Subnet your instance is attached to.
On the subnet page, click on the security list (name starting with ds-luna-seclist-).
Press Add Ingress Rule and fill in the following data:
The rule allows traffic from all sources to use port 8080, so that the application can be reached from anywhere.
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
Restart the application:
Built with Maven:
./target/default
Built with Gradle:
./build/native/nativeCompile/default
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
- Take this lab to learn how to install Oracle GraalVM and its features on Oracle Linux yourself.
- Get hands-on lab experience using GraalVM Native Image and start building cloud native Java applications by running the GraalVM Native Image Quick Start lab.
- Check the Oracle GraalVM documentation
- Visit the product page at oracle.com
To end this session, click the End Session button in the toolbar.