By

As more applications are moving towards cloud deployments, minimizing startup time is becoming more and more important. Read on to find out how to use Timefold Solver with Spring native images to reduce startup time in your application.

If you want to follow along, clone the Timefold quickstarts and change to the technology/java-spring-boot directory, which is already set up to support Spring native images:

git clone https://github.com/TimefoldAI/timefold-quickstarts.git
cd timefold-quickstarts/technology/java-spring-boot

Setup

You can modify your existing Spring applications to support building Spring native images by modifying your configuration.

Maven

Add org.graalvm.buildtools:native-maven-plugin to your build plugins:

<build>
  <plugin>
      <groupId>org.graalvm.buildtools</groupId>
      <artifactId>native-maven-plugin</artifactId>
  </plugin>
</build>
Note
If you are not using spring-boot-starter-parent, you would need to configure executions for the process-aot goal from Spring Boot’s plugin and the add-reachability-metadata goal from the Native Build Tools plugin.
Gradle

Add org.graalvm.buildtools.native to your plugins:

plugins {
    // ...
    id 'org.graalvm.buildtools.native' version '0.10.1'
}

Building

Spring provides two ways of building a native image:

  • Using Docker

  • Using a locally installed GraalVM

Build using Docker

Maven

Run

mvn -Pnative spring-boot:build-image
Gradle

Run

gradle bootBuildImage

This will produce an image tagged as docker.io/library/${name}:${version} where ${name} is the artifactId in Maven or the archivesBaseName in Gradle.

You can then run the image using Docker:

docker run --rm -p 8080:8080 docker.io/library/docker.io/library/timefold-solver-spring-boot-school-timetabling-quickstart:1.0-SNAPSHOT

Build using locally installed GraalVM

Maven

Run

mvn -Pnative native:compile

This will create an executable in target using the project’s artifactId as the name. The native image can then be run directly:

./target/timefold-solver-spring-boot-school-timetabling-quickstart
Gradle

Run

gradle nativeCompile

This will create an executable in build/native/nativeCompile with the same name as the parent directory for build.gradle. The native image can then be run directly:

./build/native/nativeCompile/java-spring-boot

Benefits

The primary benefit of native images is the massively reduced startup time:

./target/timefold-solver-spring-boot-school-timetabling-quickstart
...
20:40:44.888  INFO [main           ] Started TimetableSpringBootApp in 0.07 seconds (process running for 0.073)

Compared to running with a JVM:

java -jar target/timefold-solver-spring-boot-school-timetabling-quickstart-1.0-SNAPSHOT.jar
...
20:42:40.323  INFO [main           ] Started TimetableSpringBootApp in 1.216 seconds (process running for 1.426)

The native image started 20 times faster! This means a freshly started Kubernetes pod can respond to its first requests 20 times faster when a native image is used, changing an abysmal ~1 seconds wait time to a much more acceptable ~0.1 seconds wait time.

Native starts up 20 times faster than the JVM.

However, it is not all roses, since a native image is unable to perform various profiling based optimizations that a JVM can perform, reducing the speed of compute bound problems (such as solving):

./target/timefold-solver-spring-boot-school-timetabling-quickstart
...
20:58:43.064  INFO [pool-5-thread-1] Solving ended: ... score calculation speed (124774/sec) ...

Compared to running with a JVM:

java -jar target/timefold-solver-spring-boot-school-timetabling-quickstart-1.0-SNAPSHOT.jar
...
20:56:17.441  INFO [pool-2-thread-1] Solving ended: ... score calculation speed (213780/sec) ...

In a native image, Timefold Solver ran about 42% slower compared to a JVM run. This means to get to the same solution as a JVM run, the native image would need to run almost twice as long!

Important
Make sure to use GraalVM JDK 22 or above to generate the native image, which fixes a major performance regression involving record hashCode and equals.

Conclusion

It is easy to modify your existing Spring applications to make use of native images, allowing you to massively reduce startup time and thus respond to requests quicker on newly spawned Kubernetes pods. However, using native images currently prevents profiling based JIT optimizations, significantly impacting performance for compute bound tasks such as solving. If you have long-running solving tasks, consider running the solver in a separate application, which will allow you to gain the startup time benefits without affecting the performance of the solver.

Continue reading

  • Red Hat: OptaPlanner End Of Life Notice (EOL)

    Timefold, led by former core OptaPlanner engineers, offers a seamless transition with extended support and accelerated innovation.

  • Newsletter 4: A big Speed Upgrade for Timefold - Our First Customer Story and AMA!

    Unlock unprecedented speed with Timefold's latest update! Essential for OptaPlanner and pre-1.8.0 users – upgrade now for instant gains

  • How fast is Java 22?

    Explore the performance of Java 22 against Java 21, this time also with GraalVM.

  • Continuous Planning Optimization with Pinning

    Discover how to make non-disruptive, feasible adjustments to your already in-progress plans with Timefold, ensuring real-time adaptability to unexpected changes.

  • Fast Planning Optimization with the Recommended Fit API

    Discover how Timefold's Recommended Fit API offers swift, feasible adjustments to your plans, ensuring real-time adaptability to unexpected changes.

  • Newsletter 3: Explainable AI for Planning Optimization

    Discover how Timefold's ScoreAnalysis API builds trust in automated planning. And don't forget to join Timefold's first AMA on Youtube Live on March 19th!

Sign up for our newsletter

And stay up to date with announcements, the latest news, events, roadmap progress & product updates from Timefold!

We care about the protection of your data. Read our Privacy Policy.