Help

Experiments with GraalVM native images (2/2)

The previous blog post was about the “normal way” of using GraalVM native images. In this blog post I use it to run Java code on Android without the usual limitations of “Android Java”. I.e. the Java code can be JDK 11 and include arbitrary libraries which is usually not possible when using Dalvik or ART. The obvious next step is to do the same with iOS, but this is for another blog post ;).

The first step is to create some Java code and include the maven plugin from Gluon “client-maven-plugin”. For GraphHopper I needed a snapshot version and so I included the gluonhq plugin repository, but this won’t be necessary once the next release is out.

You need lots of tooling for Android. First of all I got the latest GraalVM from Gluon:

wget https://download2.gluonhq.com/substrate/graalvm/graalvm-svm-linux-20.1.0-ea+26.zip

Unzip this and let GRAALVM_HOME point to it:

export GRAALVM_HOME=$BASE/graalvm-svm-linux-20.1-latest

Now you download the Android SDK and set an env variable to it:

export ANDROID_SDK=$BASE/android-sdk-linux

And you do the same for Android NDK:

export ANDROID_NDK=$BASE/android-sdk-linux/ndk/20.1.5948944/ 

The idea of “Gluon Mobile” is to use the same Java also for the mobile application code (“Java end-to-end”) without using the Android UI toolkit – instead these applications use JavaFX. For GrapHopper you can do the same but I wanted to provide a solution for developers with an existing “Android Java” application too and so I tried the client examples from Gluon to create a native library that can be used from an existing “Android Java” application. For that a very fresh GraalVM version was necessary which I built from sources:

# download a patched JDK from https://github.com/graalvm/labs-openjdk-11/releases/download/
export JAVA_HOME=/path/labsjdk-ce-11.0.6-jvmci-20.0-b02/
git clone https://github.com/graalvm/mx.git
export PATH=$PATH:$(pwd)/mx
git clone https://github.com/oracle/graal.git
cd graal/vm
mx clean
# without: polyglot stuff
mx -J-Xmx5g --env ce --disable-polyglot --disable-libpolyglot build

See this issue and this article or this script on how others did this.

But I couldn’t get it working. So I asked Gluon for some consulting and they improved their open source pipeline. After several days from them and also lots of further investment from myself we finally got it working! It is now possible to run the native GraphHopper library on my Android phone:

IDE Output when running GraphHopper native on Android

This means I created the native library libgraphhoppernative that I use from this Android NDK project a rather simple proof that it works looks like:

It works 🙂 !

We could even do the OpenStreetMap data import or use the public transit module as all libraries and language features should work without a problem unlike for “Android Java”. And the speed of one routing request is also good in my preliminary tests.

Unfortunately there are currently a few caveats:

  1. The memory usage is strange and the routing app crashes after being called a few times. Maybe we should not call it from the main thread but maybe there is a GraalVM bug. Will post this on their slack channel.
  2. The produced library file is over 55MB. I’m unsure why it is so large as the native library for linux is 15MB. Maybe this issue is related.
  3. The created native library currently only supports 64bit code. Work is in progress for 32 bit code by Oracle.
  4. The compilation takes several minutes and is very resource intense and sometimes crashes my laptop. Here again Oracle will improve the situation in the future according to my knowledge. But at the moment the development is horrible.
  5. Communication with the “Graal” code is currently ugly: from Java you call C code that calls the run_main method from Graal, which has no possibility to return something. So the communication happens via a file instead which we read from C again (we could do this in Java too).

So this technology is really cutting edge, but I’m still excited about it as it can enable Java to be really platform-independent again.

Until then you would probably better use our Android library. See also this discussion.

Happy Routing!

Experiments with GraalVM native images (1/2)

This blog post is about the experience I made when compiling the GraphHopper routing engine to a native executable using GraalVM.

The GraphHopper routing engine is an open source project that uses the road network from OpenStreetMap and is able to calculate routes from A to B very fast and offers other features like isochrone calculation or map matching.

The GraalVM project is a huge project from Oracle Labs, see its official website. The technology behind “GraalVM native images” allows you to create a standalone native executable e.g. for Linux. This means you can run Java code without a JVM.

For our purpose we do the following steps:

  • install GraalVM with native-image
  • create some config files that tells GraalVM to include certain resources or how to deal with reflection etc.
  • the Java code including a C hook
  • the C++ code that calls the C hook of the Java code

To get started download GraalVM for your OS e.g. GraalVM CE for Linux amd64 and unzip it. Let JAVA_HOME point to this location. Then install the native-image tool into this installation:
$JAVA_HOME/bin/gu install native-image
This tool needs a local C toolchain: glibc-devel, zlib-devel and gcc. On Linux you can install this via:
sudo apt-get install zlib1g-dev
You can now verify that the native-image tool works:
$JAVA_HOME/bin/native-image --help

To create the config files automatically you call:

$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=./ni-configs -jar target/*-jar-with-dependencies.jar

When you include native-image-maven-plugin in your maven build you add these config files via:

-H:ConfigurationFileDirectories=${project.basedir}/ni-configs

See the full pom.xml here that also adds the dependency “library-support” so that we can include the static C hook to our Java code:

@CEntryPoint(name = "runGH")
public static double runGH(IsolateThread thread, /*more params*/) {
  return internalRun(/*more params*/);
}

Which we then can call in our C++ code:

#include <iostream>
#include "libgraphhopper.h"

using namespace std;

int main(int argc, char **argv) {
// This is some Graal boilerplate code
graal_isolatethread_t *thread = NULL;
if (graal_create_isolate(NULL, NULL, &thread) != 0) {
  fprintf(stderr, "graal_create_isolate error\n");
  return 1;
}
...
double distance = runGH(thread, lat1, lon1, lat2, lon2);
std::cout << "Distance calculated by GraphHopper " << distance << std::endl;

// Clean up Graal stuff
if (graal_detach_thread(thread) != 0) {
  fprintf(stderr, "graal_detach_thread error\n");
  return 1;
}
}

We can now build the project via
mvn clean package
that creates an executable at ./target/graphhopper which is 15MB.

If you put some OpenStreetMap data at osm.pbf you can already start it and the second time you run it the startup performance is really nice. For OpenJDK it takes approx. 500ms which is already very good as you have to keep in mind that the road data is already loaded via memory mapping, but with the native image tool we can start GraphHopper, load the graph and run a routing request in under 30ms! For production it is likely faster as I just tested this on my weak laptop.

Low Startup Time with GraalVM native images

So what did we just do? We created a Java program which was started without a JVM!? That is the magic of GraalVM native images. It can handle other JVM-based languages like Scala, Clojure and Kotlin too. The resulting native image can, optionally, execute dynamic languages like JavaScript, Python etc. For more details visit the official documentation.

Of course this has some downsides like lower peak performance and longer compilation, but it makes Java now a viable choice to be used from within a bash loop or in a so called “serverless” scenario.

In the next post you’ll learn how we can use GraalVM to run GraphHopper native on Android.

Birthday and Stars

Today the GraphHopper routing engine turned 8 years. It has now over 2500 stars at GitHub and together we pushed over 4300 commits with contributions from over 80 contributors – thanks a lot! See here for the first commits and read the blog post about the new release 1.0.

Additionally we invested into our other open source project jsprit. jsprit was created in 2013 from Stefan and is a toolkit for solving rich traveling salesman and vehicle routing problems. It has now over 1000 stars and more than 2000 commits. Together with the GraphHopper routing engine the jsprit toolkit creates the foundation for the GraphHopper Route Optimization API, our commercial service to solve vehicle routing problems. This commercial part is mentioned here as it is important for the success, quality and sustainability of our open source projects.

GraphHopper Routing Engine Retrospect

What began as a solo development expanded to team work: multiple core developers and many contributors. 3 years ago I already wrote a small retrospect and let’s expand on it a bit. It was and is an impressive journey for me and I still enjoy it every day and I think this applies to the whole GraphHopper team.

In the beginning I played around with Dijkstra and OpenStreetMap data and wanted to have it in Java as I wanted to leave my C/C++ experience behind me. Doing this in Java was the first challenge to solve.

It took a few months and over 1 year and then I released version 0.1.

Later NopMap contributed elevation data improvements and other vehicle profiles, so not only car worked, but also routing for horses. Other profiles like foot and bike were added. Many bike profile improvements came and come from ratrun. With this help the new biking feature for the Directions API was launched even a bit earlier than Google Maps was able to provide directions for bikes in Germany & France. Of course we used and still use OpenStreetMap data and that is the reason we are still ahead of many others (:proud_smiley).

For several years the routing engine did not support turn restrictions. And even after this big feature was contributed from Karl, it was still not available for the so called speed-mode (Contraction Hierarchies). It is incredible hard to implement turn cost support for the speed-mode with a ‘normal’ “node-based” graph that we are using, i.e. junctions are nodes and the connections are the edges. And then luckily Andi was able to implement this. See this PR and the 0.12 release announcement for more details on this journey.

Turn restrictions and more general turn costs are supported since 0.12 also for the speed mode.

Michael and Stefan Holder implemented a fast and high quality Map Matching after I created an initial “very heuristic” version.

Map Matching Module

Michael also implemented the public transit module.

Public Transit Module

The Isochrone module, first a commercial feature, later open source, was created from Michael and me.

Isochrone Module

In 2018 we introduced a fully open source navigation feature for Android, including a demo app, forked from Mapbox and mainly developed from Robin (kurviger)

100% Open Source Navigation for Android.

Furthermore Robin improved the turn instructions, implemented round tours, the motorcycle vehicle profile and many more things.

Over the years we got 43 translations for the turn instructions – special thanks goes to Manfred and his team!

The UI was initially developed from me and was improved from others like fbonzon (Bikewithme) and Andi.

GraphHopper Maps our demo for world wide routing

There is now even a GraphHopper fork that does routing on rails.

The new 1.0 release will contain many improvements regarding elevation (from Michael Barry) and improvements useful for e.g. truck routing developed (from otbutz).

An exciting new feature for the 1.0 release will be a highly configurable routing.

Configurable routing that will likely appear in 1.0 soon.

Over the years our open source work was supported from Komoot, Deutsche Bahn, Talent Systems, GPSies, Geofabrik, curbside, Falk, BMW Car IT, Sidekix and kurviger. And last but not least from the GraphHopper GmbH. Thanks a lot!

Have a look into the older release announcements with many more unnamed contributions like from @jansoe, @clns or @devemux86:

  • 0.13 announcement (2019). We introduced snap preventions, additional road data like surfaces and a vector tiles endpoint for development.
  • 0.12 announcement (2019). We introduced turn restrictions for CH and public transit isochrones.
  • 0.11 announcement (2018). We introduced the isochrone endpoint, the navigation support, the public transit module supports now a real time support and switch to the dropwizard framework for the web API.
  • 0.10 announcement (2018). We introduced the path details and a big speed up for CH.
  • 0.9 announcement (2017). We introduced a public transit module, the spatial rules and a new hybrid mode, ie. a fast flexible routing.
  • 0.8 announcement (2016). We introduced a high quality map matching functionality.
  • 0.7 announcement (2016). We introduced a round tour feature and did a major refactoring.
  • 0.6 announcement (2016). We introduced alternative route calculation.
  • 0.5 announcement (2015). We introduced support for multiple vehicle profiles.
  • 0.4 announcement (2015). The speep-up and flexibility mode can be used on the same graph.
  • 0.3 announcement (2014). We introduced elevation support and for that the weight can be different for two directions of one edge. Also an iOS port was released.
  • 0.2 announcement (2013). We introduced the speed-mode (Contraction Hierarchy) and GPS-exact routing, before it was from junction to junction only.
  • 0.1 announcement (2013). After over 1 year we released the first public version even with an Android demo, but there were still many limitations.