Kotlin Native vs. GraalVM

I’m thinking about using Kotlin for a native application an wonder whether it would be better to use Kotlin Native or GraalVM. As far as I know Kotlin Native it seems to have some serious limitations:

  • Worse performance than Kotlin on JVM
  • Not all Java libraries and frameworks are usable
  • IntelliJ doesn’t support Kotlin Native (only CLion), although I remember that this limitation should be lifted

Then there is GraalVM, Oracle put a lot of effort in, and what seems to be a very powerful compiler producing fast executables.

@mikehearn wrote on his blog:

Kotlin doesn’t need an LLVM backend, and by extension I believe neither does Scala. Creating such a thing would be a huge and ongoing drain of manpower, take a long time, and end up duplicating work already being done elsewhere in the JVM ecosystem … very likely with worse results.

What a the pros and cons of using Kotlin Native and Kotlin + GraalVM regarding the following points?

  • ease of use (compiler setup, build tools, etc …)
  • usability of Java libraries / frameworks
  • usability of other libraries / frameworks (C and the like)
  • speed of execution
  • memory usage
  • versatility

There was a related discussion in this forum:

3 Likes

Kotlin Native has nothing in common with SubstrateVM (Graal is a runtime, Substrate is its part which allows AOT builds). These tools cover different use cases. K-N is used to create a multi-platform or stand-alone native applications, Substrate is used to pre-compile JVM applications for faster startup.

The actual question is why do you need one of those tools. If you believe that you will get faster performance (not startup time), then both will be slower, than JVM. Maybe you will be surprised, but JVM performance nowadays is better than most native implementations and Graal has some new optimizations (some of them work only on EE though).

2 Likes

Maybe my wording was not precise enough, but in the end both Kotlin Native and Graal Native Images are able to build native binaries for different platforms. So at least in my limited understanding both are competing in this area.

In my concrete scenario I want to avoid the JVM startup costs (think of “serverless”).

Well, you will get faster start on Graal AOT, but overall performance will be worse. Could you explain, what is your goal? Warm-up time for modern JVM is about 1 second. Also it is possible to use warm daemon JVM like Groovy or KScript does to save on startup costs.

I want to run small programs on a per request basis (known today as “servless” or “cloud functions”, the older people among us still know it as “CGI script” :wink: ) . A startup time of 1 second could just be acceptable, but it should really be faster for this scenario.

But to come back to my original question: What are the pros and cons of Graal and Kotlin Native for such a scenario?

They are just two different things. If you want the power of JVM libraries, you need Graal, but you won’t get much in terms of overall performance.If you want to run small CLI tools, than JVM and Graal are probably overkill. You can use Kotlin-native, but there are probably better native solutions. You can also use KScript with warmed-up VM, which will give you fast start times.

2 Likes

Note that “Serverless” can often mean there is still a container per say (e.g. AWS) but that container can scale down to 0 instances via an idle time. That is, there is still a container/runtime (e.g. JVM) but we don’t have much control over it.

In that sense “cgi script” isn’t an exact match to “serverless” with respect to startup cost per request.

What are the pros and cons of Graal and Kotlin Native for such a scenario?

I’ve only used Graal’s native-image and Kotlin/Native for some small test programs and benchmarks. For a recent example that’s pretty typical of my tests:

| Lang            | Opts              | Time    | Speed | Memory |
|-----------------+-------------------+---------+-------+--------|
| Kotlin (JVM)    |                   | 4441 ms |    1x | 102 MB |
| Kotlin (Native) | -opt              | 86.8 s  | 19.5x | 5.5 MB |
| Kotlin (Graal)  | -O3               | 27.7 s  | 6.24x | 261 MB |

So the Kotlin (JVM) does the best, by far, in all respects except for memory usage for low-memory programs, where the JVM gives you a +100MB (or +50MB if you try to limit it) just to pass ‘Go’. There’s a startup speed hit which would disqualify the JVM for CGI tasks, but for me it’s more like 100ms than 1s.

After that, Graal lets you keep your JVM-reliant code and it has shockingly bad performance (vs. my experience with native-image’s treatment of normal Java, even Java that’s transpiled from other languages), but still better performance than Kotlin/Native. At the cost of worse memory usage.

Kotlin/Native’s memory consumption here is exactly what you’d expect from a normal native-compiling language, like C or C++, but its performance is awful. And its performance is awful probably because it’s doing absolutely crazy stuff with memory. Just look at this ltrace output:

calloc(1, 29)                                                            = 0x21c2470
memcpy(0x21c2488, "He", 2)                                               = 0x21c2488
free(0x21c24a0)                                                          = <void>
calloc(1, 33)                                                            = 0x21c24a0
memcpy(0x21c24b8, "Hell", 4)                                             = 0x21c24b8
free(0x21c2470)                                                          = <void>
calloc(1, 41)                                                            = 0x21c24d0
memcpy(0x21c24e8, "Hello, 1", 8)                                         = 0x21c24e8
free(0x21c24a0)                                                          = <void>
calloc(1, 40)                                                            = 0x21c24a0
memcpy(0x21c24b8, "Hello, 126", 10)                                      = 0x21c24b8
free(0x21c24d0)                                                          = <void>
calloc(1, 24)                                                            = 0x21c2390
calloc(1, 19)                                                            = 0x21c2510

That’s coming from puts("Hello, $it"). Yes that’s a static string, "Hello, ", that is getting chopped up and repeatedly memcpy’d for some reason.

So if you want to use Kotlin for CGI, I’d suggest you write a server instead, but anyway the JVM configuration is still probably be the best option for you, even for CGI. At least for now. In the future Kotlin/Native will probably be the best option, with minimal start-up costs and the most reasonable memory usage, at the cost of your having to use different libraries.

5 Likes